]> granicus.if.org Git - postgresql/blob - src/port/getaddrinfo.c
Fix bogus freeaddrinfo() call in WIN32 code, extend gai_strerror to
[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  * Windows may or may not have these routines, so we handle Windows special
12  * by dynamically checking for their existence.  If they already exist, we
13  * use the Windows native routines, but if not, we use our own.
14  *
15  *
16  * Copyright (c) 2003-2005, PostgreSQL Global Development Group
17  *
18  * IDENTIFICATION
19  *        $PostgreSQL: pgsql/src/port/getaddrinfo.c,v 1.19 2005/08/25 17:51:01 tgl Exp $
20  *
21  *-------------------------------------------------------------------------
22  */
23
24 /* This is intended to be used in both frontend and backend, so use c.h */
25 #include "c.h"
26
27 #ifndef WIN32_CLIENT_ONLY
28 #include <sys/socket.h>
29 #include <netdb.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #endif
33
34 #include "getaddrinfo.h"
35
36
37 #ifdef WIN32
38
39 #define WIN32_LEAN_AND_MEAN
40
41 #include <windows.h>
42
43 /*
44  * The native routines may or may not exist on the Windows platform we are on,
45  * so we dynamically look up the routines, and call them via function pointers.
46  * Here we need to declare what the function pointers look like
47  */
48 typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename,
49                                                                                          const char *servname,
50                                                                                          const struct addrinfo * hints,
51                                                                                          struct addrinfo ** res);
52
53 typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai);
54
55 typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa,
56                                                                                          int salen,
57                                                                                          char *host, int hostlen,
58                                                                                          char *serv, int servlen,
59                                                                                          int flags);
60
61 /* static pointers to the native routines, so we only do the lookup once. */
62 static getaddrinfo_ptr_t getaddrinfo_ptr = NULL;
63 static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL;
64 static getnameinfo_ptr_t getnameinfo_ptr = NULL;
65
66
67 static bool
68 haveNativeWindowsIPv6routines(void)
69 {
70         void       *hLibrary = NULL;
71         static bool alreadyLookedForIpv6routines = FALSE;
72
73         if (alreadyLookedForIpv6routines)
74                 return (getaddrinfo_ptr != NULL);
75
76         /*
77          * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6
78          * routines are present in the WinSock 2 library (ws2_32.dll).
79          * Try that first
80          */
81
82         hLibrary = LoadLibraryA("ws2_32");
83
84         if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL)
85         {
86                 /*
87                  * Well, ws2_32 doesn't exist, or more likely doesn't have
88                  * getaddrinfo.
89                  */
90                 if (hLibrary != NULL)
91                         FreeLibrary(hLibrary);
92
93                 /*
94                  * In Windows 2000, there was only the IPv6 Technology Preview look in
95                  * the IPv6 WinSock library (wship6.dll).
96                  */
97
98                 hLibrary = LoadLibraryA("wship6");
99         }
100
101         /* If hLibrary is null, we couldn't find a dll with functions */
102         if (hLibrary != NULL)
103         {
104                 /* We found a dll, so now get the addresses of the routines */
105
106                 getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary,
107                                                                                                                          "getaddrinfo");
108                 freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary,
109                                                                                                                            "freeaddrinfo");
110                 getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary,
111                                                                                                                          "getnameinfo");
112
113                 /*
114                  * If any one of the routines is missing, let's play it safe and
115                  * ignore them all
116                  */
117                 if (getaddrinfo_ptr == NULL ||
118                         freeaddrinfo_ptr == NULL ||
119                         getnameinfo_ptr == NULL)
120                 {
121                         FreeLibrary(hLibrary);
122                         hLibrary = NULL;
123                         getaddrinfo_ptr = NULL;
124                         freeaddrinfo_ptr = NULL;
125                         getnameinfo_ptr = NULL;
126                 }
127         }
128
129         alreadyLookedForIpv6routines = TRUE;
130         return (getaddrinfo_ptr != NULL);
131 }
132
133 #endif
134
135
136 /*
137  * get address info for ipv4 sockets.
138  *
139  *      Bugs:   - only one addrinfo is set even though hintp is NULL or
140  *                ai_socktype is 0
141  *              - AI_CANONNAME is not supported.
142  *              - servname can only be a number, not text.
143  */
144 int
145 getaddrinfo(const char *node, const char *service,
146                         const struct addrinfo * hintp,
147                         struct addrinfo ** res)
148 {
149         struct addrinfo *ai;
150         struct sockaddr_in sin,
151                            *psin;
152         struct addrinfo hints;
153
154 #ifdef WIN32
155         /*
156          * If Windows has native IPv6 support, use the native Windows routine.
157          * Otherwise, fall through and use our own code.
158          */
159         if (haveNativeWindowsIPv6routines())
160                 return (*getaddrinfo_ptr) (node, service, hintp, res);
161 #endif
162
163         if (hintp == NULL)
164         {
165                 memset(&hints, 0, sizeof(hints));
166                 hints.ai_family = AF_INET;
167                 hints.ai_socktype = SOCK_STREAM;
168         }
169         else
170                 memcpy(&hints, hintp, sizeof(hints));
171
172         if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
173                 return EAI_FAMILY;
174
175         if (hints.ai_socktype == 0)
176                 hints.ai_socktype = SOCK_STREAM;
177
178         if (!node && !service)
179                 return EAI_NONAME;
180
181         memset(&sin, 0, sizeof(sin));
182
183         sin.sin_family = AF_INET;
184
185         if (node)
186         {
187                 if (node[0] == '\0')
188                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
189                 else if (hints.ai_flags & AI_NUMERICHOST)
190                 {
191                         if (!inet_aton(node, &sin.sin_addr))
192                                 return EAI_FAIL;
193                 }
194                 else
195                 {
196                         struct hostent *hp;
197
198 #ifdef FRONTEND
199                         struct hostent hpstr;
200                         char            buf[BUFSIZ];
201                         int                     herrno = 0;
202
203                         pqGethostbyname(node, &hpstr, buf, sizeof(buf),
204                                                         &hp, &herrno);
205 #else
206                         hp = gethostbyname(node);
207 #endif
208                         if (hp == NULL)
209                         {
210                                 switch (h_errno)
211                                 {
212                                         case HOST_NOT_FOUND:
213                                         case NO_DATA:
214                                                 return EAI_NONAME;
215                                         case TRY_AGAIN:
216                                                 return EAI_AGAIN;
217                                         case NO_RECOVERY:
218                                         default:
219                                                 return EAI_FAIL;
220                                 }
221                         }
222                         if (hp->h_addrtype != AF_INET)
223                                 return EAI_FAIL;
224
225                         memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
226                 }
227         }
228         else
229         {
230                 if (hints.ai_flags & AI_PASSIVE)
231                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
232                 else
233                         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
234         }
235
236         if (service)
237                 sin.sin_port = htons((unsigned short) atoi(service));
238
239 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
240         sin.sin_len = sizeof(sin);
241 #endif
242
243         ai = malloc(sizeof(*ai));
244         if (!ai)
245                 return EAI_MEMORY;
246
247         psin = malloc(sizeof(*psin));
248         if (!psin)
249         {
250                 free(ai);
251                 return EAI_MEMORY;
252         }
253
254         memcpy(psin, &sin, sizeof(*psin));
255
256         ai->ai_flags = 0;
257         ai->ai_family = AF_INET;
258         ai->ai_socktype = hints.ai_socktype;
259         ai->ai_protocol = hints.ai_protocol;
260         ai->ai_addrlen = sizeof(*psin);
261         ai->ai_addr = (struct sockaddr *) psin;
262         ai->ai_canonname = NULL;
263         ai->ai_next = NULL;
264
265         *res = ai;
266
267         return 0;
268 }
269
270
271 void
272 freeaddrinfo(struct addrinfo * res)
273 {
274         if (res)
275         {
276 #ifdef WIN32
277                 /*
278                  * If Windows has native IPv6 support, use the native Windows routine.
279                  * Otherwise, fall through and use our own code.
280                  */
281                 if (haveNativeWindowsIPv6routines())
282                 {
283                         (*freeaddrinfo_ptr) (res);
284                         return;
285                 }
286 #endif
287
288                 if (res->ai_addr)
289                         free(res->ai_addr);
290                 free(res);
291         }
292 }
293
294
295 const char *
296 gai_strerror(int errcode)
297 {
298 #ifdef HAVE_HSTRERROR
299         int                     hcode;
300
301         switch (errcode)
302         {
303                 case EAI_NONAME:
304                         hcode = HOST_NOT_FOUND;
305                         break;
306                 case EAI_AGAIN:
307                         hcode = TRY_AGAIN;
308                         break;
309                 case EAI_FAIL:
310                 default:
311                         hcode = NO_RECOVERY;
312                         break;
313         }
314
315         return hstrerror(hcode);
316
317 #else   /* !HAVE_HSTRERROR */
318
319         switch (errcode)
320         {
321                 case EAI_NONAME:
322                         return "Unknown host";
323                 case EAI_AGAIN:
324                         return "Host name lookup failure";
325                 /* Errors below are probably WIN32 only */
326 #ifdef EAI_BADFLAGS
327                 case EAI_BADFLAGS:
328                         return "Invalid argument";
329 #endif
330 #ifdef EAI_FAMILY
331                 case EAI_FAMILY:
332                         return "Address family not supported";
333 #endif
334 #ifdef EAI_MEMORY
335                 case EAI_MEMORY:
336                         return "Not enough memory";
337 #endif
338 #ifdef EAI_NODATA
339                 case EAI_NODATA:
340                         return "No host data of that type was found";
341 #endif
342 #ifdef EAI_SERVICE
343                 case EAI_SERVICE:
344                         return "Class type not found";
345 #endif
346 #ifdef EAI_SOCKTYPE
347                 case EAI_SOCKTYPE:
348                         return "Socket type not supported";
349 #endif
350                 default:
351                         return "Unknown server error";
352         }
353 #endif   /* HAVE_HSTRERROR */
354 }
355
356 /*
357  * Convert an ipv4 address to a hostname.
358  *
359  * Bugs:        - Only supports NI_NUMERICHOST and NI_NUMERICSERV
360  *                It will never resolv a hostname.
361  *              - No IPv6 support.
362  */
363 int
364 getnameinfo(const struct sockaddr * sa, int salen,
365                         char *node, int nodelen,
366                         char *service, int servicelen, int flags)
367 {
368 #ifdef WIN32
369         /*
370          * If Windows has native IPv6 support, use the native Windows routine.
371          * Otherwise, fall through and use our own code.
372          */
373         if (haveNativeWindowsIPv6routines())
374                 return (*getnameinfo_ptr) (sa, salen, node, nodelen,
375                                                                    service, servicelen, flags);
376 #endif
377
378         /* Invalid arguments. */
379         if (sa == NULL || (node == NULL && service == NULL))
380                 return EAI_FAIL;
381
382         /* We don't support those. */
383         if ((node && !(flags & NI_NUMERICHOST))
384                 || (service && !(flags & NI_NUMERICSERV)))
385                 return EAI_FAIL;
386
387 #ifdef  HAVE_IPV6
388         if (sa->sa_family == AF_INET6)
389                 return EAI_FAMILY;
390 #endif
391
392         if (node)
393         {
394                 int                     ret = -1;
395
396                 if (sa->sa_family == AF_INET)
397                 {
398                         char       *p;
399
400                         p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr);
401                         ret = snprintf(node, nodelen, "%s", p);
402                 }
403                 if (ret == -1 || ret > nodelen)
404                         return EAI_MEMORY;
405         }
406
407         if (service)
408         {
409                 int                     ret = -1;
410
411                 if (sa->sa_family == AF_INET)
412                 {
413                         ret = snprintf(service, servicelen, "%d",
414                                                    ntohs(((struct sockaddr_in *) sa)->sin_port));
415                 }
416                 if (ret == -1 || ret > servicelen)
417                         return EAI_MEMORY;
418         }
419
420         return 0;
421 }