]> granicus.if.org Git - postgresql/blob - src/port/getaddrinfo.c
Have a go at fixing various outstanding portability issues in code that
[postgresql] / src / port / getaddrinfo.c
1 /*-------------------------------------------------------------------------
2  *
3  * getaddrinfo.c
4  *        Support getaddrinfo() on platforms that don't have it.
5  *
6  * We also supply getnameinfo() here, assuming that the platform will have
7  * it if and only if it has getaddrinfo().  If this proves false on some
8  * platform, we'll need to split this file and provide a separate configure
9  * test for getnameinfo().
10  *
11  *
12  * Copyright (c) 2003, PostgreSQL Global Development Group
13  *
14  * IDENTIFICATION
15  *        $Header: /cvsroot/pgsql/src/port/getaddrinfo.c,v 1.10 2003/07/23 23:30:41 tgl Exp $
16  *
17  *-------------------------------------------------------------------------
18  */
19
20 /* This is intended to be used in both frontend and backend, so use c.h */
21 #include "c.h"
22
23 #if !defined(_MSC_VER) && !defined(__BORLANDC__)
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netdb.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #endif
30
31 #include "getaddrinfo.h"
32
33 /*
34  * get address info for ipv4 sockets.
35  *
36  *      Bugs:   - only one addrinfo is set even though hintp is NULL or
37  *                ai_socktype is 0
38  *              - AI_CANONNAME is not supported.
39  *              - servname can only be a number, not text.
40  */
41 int
42 getaddrinfo(const char *node, const char *service,
43                         const struct addrinfo *hintp,
44                         struct addrinfo **res)
45 {
46         struct addrinfo         *ai;
47         struct sockaddr_in      sin, *psin;
48         struct addrinfo         hints;
49
50         if (hintp == NULL)      
51         {
52                 memset(&hints, 0, sizeof(hints));
53                 hints.ai_family = AF_INET;
54                 hints.ai_socktype = SOCK_STREAM;
55         }
56         else
57         {
58                 memcpy(&hints, hintp, sizeof(hints));
59         }
60
61         if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
62                 return EAI_FAMILY;
63
64         if (hints.ai_socktype == 0)
65                 hints.ai_socktype = SOCK_STREAM;
66
67         if (!node && !service)
68                 return EAI_NONAME;
69
70         memset(&sin, 0, sizeof(sin));
71
72         sin.sin_family = AF_INET;
73
74         if (node)
75         {
76                 if (node[0] == '\0')
77                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
78                 else if (hints.ai_flags & AI_NUMERICHOST)
79                 {
80                         if (!inet_aton(node, &sin.sin_addr))
81                         {
82                                 return EAI_FAIL;
83                         }
84                 }
85                 else
86                 {
87                         struct hostent *hp;
88 #ifdef FRONTEND
89                         struct hostent hpstr;
90                         char buf[BUFSIZ];
91                         int herrno = 0;
92
93                         pqGethostbyname(node, &hpstr, buf, sizeof(buf),
94                                         &hp, &herrno);
95 #else
96                         hp = gethostbyname(node);
97 #endif
98                         if (hp == NULL)
99                         {
100                                 switch (h_errno)
101                                 {
102                                         case HOST_NOT_FOUND:
103                                         case NO_DATA:
104                                                 return EAI_NONAME;
105                                         case TRY_AGAIN:
106                                                 return EAI_AGAIN;
107                                         case NO_RECOVERY:
108                                         default:
109                                                 return EAI_FAIL;
110                                 }
111                         }
112                         if (hp->h_addrtype != AF_INET)
113                                 return EAI_FAIL;
114
115                         memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
116                 }
117         }
118         else
119         {
120                 if (hints.ai_flags & AI_PASSIVE)
121                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
122                 else
123                         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
124         }
125
126         if (service)
127                 sin.sin_port = htons((unsigned short) atoi(service));
128
129 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
130         sin.sin_len = sizeof(sin);
131 #endif
132
133         ai = malloc(sizeof(*ai));
134         if (!ai)
135         {
136                 return EAI_MEMORY;
137         }
138
139         psin = malloc(sizeof(*psin));
140         if (!psin)
141         {
142                 free(ai);
143                 return EAI_MEMORY;
144         }
145
146         memcpy(psin, &sin, sizeof(*psin));
147
148         ai->ai_flags = 0;
149         ai->ai_family = AF_INET;
150         ai->ai_socktype = hints.ai_socktype;
151         ai->ai_protocol = hints.ai_protocol;
152         ai->ai_addrlen = sizeof(*psin);
153         ai->ai_addr = (struct sockaddr *) psin;
154         ai->ai_canonname = NULL;
155         ai->ai_next = NULL;
156
157         *res = ai;
158
159         return 0;
160 }
161
162
163 void
164 freeaddrinfo(struct addrinfo *res)
165 {
166         if (res)
167         {
168                 if (res->ai_addr)
169                         free(res->ai_addr);
170                 free(res);
171         }
172 }
173
174
175 const char *
176 gai_strerror(int errcode)
177 {
178 #ifdef HAVE_HSTRERROR
179         int hcode;
180
181         switch (errcode)
182         {
183                 case EAI_NONAME:
184                         hcode = HOST_NOT_FOUND;
185                         break;
186                 case EAI_AGAIN:
187                         hcode = TRY_AGAIN;
188                         break;
189                 case EAI_FAIL:
190                 default:
191                         hcode = NO_RECOVERY;
192                         break;
193         }
194
195         return hstrerror(hcode);
196
197 #else /* !HAVE_HSTRERROR */
198
199         switch (errcode)
200         {
201                 case EAI_NONAME:
202                         return "Unknown host";
203                 case EAI_AGAIN:
204                         return "Host name lookup failure";
205                 case EAI_FAIL:
206                 default:
207                         return "Unknown server error";
208         }
209
210 #endif /* HAVE_HSTRERROR */
211 }
212
213 /*
214  * Convert an ipv4 address to a hostname.
215  * 
216  * Bugs:        - Only supports NI_NUMERICHOST and NI_NUMERICSERV
217  *                It will never resolv a hostname.
218  *              - No IPv6 support.
219  */
220 int
221 getnameinfo(const struct sockaddr *sa, int salen,
222                         char *node, int nodelen,
223                         char *service, int servicelen, int flags)
224 {
225         /* Invalid arguments. */
226         if (sa == NULL || (node == NULL && service == NULL))
227         {
228                 return EAI_FAIL;
229         }
230
231         /* We don't support those. */
232         if ((node && !(flags & NI_NUMERICHOST))
233                 || (service && !(flags & NI_NUMERICSERV)))
234         {
235                 return EAI_FAIL;
236         }
237
238 #ifdef  HAVE_IPV6
239         if (sa->sa_family == AF_INET6)
240         {
241                 return  EAI_FAMILY;
242         }
243 #endif
244
245         if (node)
246         {
247                 int             ret = -1;
248
249                 if (sa->sa_family == AF_INET)
250                 {
251                         char    *p;
252                         p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
253                         ret = snprintf(node, nodelen, "%s", p);
254                 }
255                 if (ret == -1 || ret > nodelen)
256                 {
257                         return EAI_MEMORY;
258                 }
259         }
260
261         if (service)
262         {
263                 int             ret = -1;
264
265                 if (sa->sa_family == AF_INET)
266                 {
267                         ret = snprintf(service, servicelen, "%d",
268                                                    ntohs(((struct sockaddr_in *)sa)->sin_port));
269                 }
270                 if (ret == -1 || ret > servicelen)
271                 {
272                         return EAI_MEMORY;
273                 }
274         }
275
276         return 0;
277 }