]> granicus.if.org Git - postgresql/blob - src/port/getaddrinfo.c
Update copyright for 2006. Update scripts.
[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-2006, PostgreSQL Global Development Group
17  *
18  * IDENTIFICATION
19  *        $PostgreSQL: pgsql/src/port/getaddrinfo.c,v 1.23 2006/03/05 15:59:10 momjian 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 #if !defined(WIN32_CLIENT_ONLY)
44 /*
45  * The native routines may or may not exist on the Windows platform we are on,
46  * so we dynamically look up the routines, and call them via function pointers.
47  * Here we need to declare what the function pointers look like
48  */
49 typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename,
50                                                                                                                  const char *servname,
51                                                                                            const struct addrinfo * hints,
52                                                                                                          struct addrinfo ** res);
53
54 typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai);
55
56 typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa,
57                                                                                                                  int salen,
58                                                                                                          char *host, int hostlen,
59                                                                                                          char *serv, int servlen,
60                                                                                                                  int flags);
61
62 /* static pointers to the native routines, so we only do the lookup once. */
63 static getaddrinfo_ptr_t getaddrinfo_ptr = NULL;
64 static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL;
65 static getnameinfo_ptr_t getnameinfo_ptr = NULL;
66
67
68 static bool
69 haveNativeWindowsIPv6routines(void)
70 {
71         void       *hLibrary = NULL;
72         static bool alreadyLookedForIpv6routines = false;
73
74         if (alreadyLookedForIpv6routines)
75                 return (getaddrinfo_ptr != NULL);
76
77         /*
78          * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines
79          * are present in the WinSock 2 library (ws2_32.dll). 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 #endif
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 #if defined(WIN32) && !defined(WIN32_CLIENT_ONLY)
155
156         /*
157          * If Windows has native IPv6 support, use the native Windows routine.
158          * Otherwise, fall through and use our own code.
159          */
160         if (haveNativeWindowsIPv6routines())
161                 return (*getaddrinfo_ptr) (node, service, hintp, res);
162 #endif
163
164         if (hintp == NULL)
165         {
166                 memset(&hints, 0, sizeof(hints));
167                 hints.ai_family = AF_INET;
168                 hints.ai_socktype = SOCK_STREAM;
169         }
170         else
171                 memcpy(&hints, hintp, sizeof(hints));
172
173         if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
174                 return EAI_FAMILY;
175
176         if (hints.ai_socktype == 0)
177                 hints.ai_socktype = SOCK_STREAM;
178
179         if (!node && !service)
180                 return EAI_NONAME;
181
182         memset(&sin, 0, sizeof(sin));
183
184         sin.sin_family = AF_INET;
185
186         if (node)
187         {
188                 if (node[0] == '\0')
189                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
190                 else if (hints.ai_flags & AI_NUMERICHOST)
191                 {
192                         if (!inet_aton(node, &sin.sin_addr))
193                                 return EAI_FAIL;
194                 }
195                 else
196                 {
197                         struct hostent *hp;
198
199 #ifdef FRONTEND
200                         struct hostent hpstr;
201                         char            buf[BUFSIZ];
202                         int                     herrno = 0;
203
204                         pqGethostbyname(node, &hpstr, buf, sizeof(buf),
205                                                         &hp, &herrno);
206 #else
207                         hp = gethostbyname(node);
208 #endif
209                         if (hp == NULL)
210                         {
211                                 switch (h_errno)
212                                 {
213                                         case HOST_NOT_FOUND:
214                                         case NO_DATA:
215                                                 return EAI_NONAME;
216                                         case TRY_AGAIN:
217                                                 return EAI_AGAIN;
218                                         case NO_RECOVERY:
219                                         default:
220                                                 return EAI_FAIL;
221                                 }
222                         }
223                         if (hp->h_addrtype != AF_INET)
224                                 return EAI_FAIL;
225
226                         memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
227                 }
228         }
229         else
230         {
231                 if (hints.ai_flags & AI_PASSIVE)
232                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
233                 else
234                         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
235         }
236
237         if (service)
238                 sin.sin_port = htons((unsigned short) atoi(service));
239
240 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
241         sin.sin_len = sizeof(sin);
242 #endif
243
244         ai = malloc(sizeof(*ai));
245         if (!ai)
246                 return EAI_MEMORY;
247
248         psin = malloc(sizeof(*psin));
249         if (!psin)
250         {
251                 free(ai);
252                 return EAI_MEMORY;
253         }
254
255         memcpy(psin, &sin, sizeof(*psin));
256
257         ai->ai_flags = 0;
258         ai->ai_family = AF_INET;
259         ai->ai_socktype = hints.ai_socktype;
260         ai->ai_protocol = hints.ai_protocol;
261         ai->ai_addrlen = sizeof(*psin);
262         ai->ai_addr = (struct sockaddr *) psin;
263         ai->ai_canonname = NULL;
264         ai->ai_next = NULL;
265
266         *res = ai;
267
268         return 0;
269 }
270
271
272 void
273 freeaddrinfo(struct addrinfo * res)
274 {
275         if (res)
276         {
277 #if defined(WIN32) && !defined(WIN32_CLIENT_ONLY)
278
279                 /*
280                  * If Windows has native IPv6 support, use the native Windows routine.
281                  * Otherwise, fall through and use our own code.
282                  */
283                 if (haveNativeWindowsIPv6routines())
284                 {
285                         (*freeaddrinfo_ptr) (res);
286                         return;
287                 }
288 #endif
289
290                 if (res->ai_addr)
291                         free(res->ai_addr);
292                 free(res);
293         }
294 }
295
296
297 const char *
298 gai_strerror(int errcode)
299 {
300 #ifdef HAVE_HSTRERROR
301         int                     hcode;
302
303         switch (errcode)
304         {
305                 case EAI_NONAME:
306                         hcode = HOST_NOT_FOUND;
307                         break;
308                 case EAI_AGAIN:
309                         hcode = TRY_AGAIN;
310                         break;
311                 case EAI_FAIL:
312                 default:
313                         hcode = NO_RECOVERY;
314                         break;
315         }
316
317         return hstrerror(hcode);
318 #else                                                   /* !HAVE_HSTRERROR */
319
320         switch (errcode)
321         {
322                 case EAI_NONAME:
323                         return "Unknown host";
324                 case EAI_AGAIN:
325                         return "Host name lookup failure";
326                         /* Errors below are probably WIN32 only */
327 #ifdef EAI_BADFLAGS
328                 case EAI_BADFLAGS:
329                         return "Invalid argument";
330 #endif
331 #ifdef EAI_FAMILY
332                 case EAI_FAMILY:
333                         return "Address family not supported";
334 #endif
335 #ifdef EAI_MEMORY
336                 case EAI_MEMORY:
337                         return "Not enough memory";
338 #endif
339 #ifdef EAI_NODATA
340                 case EAI_NODATA:
341                         return "No host data of that type was found";
342 #endif
343 #ifdef EAI_SERVICE
344                 case EAI_SERVICE:
345                         return "Class type not found";
346 #endif
347 #ifdef EAI_SOCKTYPE
348                 case EAI_SOCKTYPE:
349                         return "Socket type not supported";
350 #endif
351                 default:
352                         return "Unknown server error";
353         }
354 #endif   /* HAVE_HSTRERROR */
355 }
356
357 /*
358  * Convert an ipv4 address to a hostname.
359  *
360  * Bugs:        - Only supports NI_NUMERICHOST and NI_NUMERICSERV
361  *                It will never resolv a hostname.
362  *              - No IPv6 support.
363  */
364 int
365 getnameinfo(const struct sockaddr * sa, int salen,
366                         char *node, int nodelen,
367                         char *service, int servicelen, int flags)
368 {
369 #if defined(WIN32) && !defined(WIN32_CLIENT_ONLY)
370
371         /*
372          * If Windows has native IPv6 support, use the native Windows routine.
373          * Otherwise, fall through and use our own code.
374          */
375         if (haveNativeWindowsIPv6routines())
376                 return (*getnameinfo_ptr) (sa, salen, node, nodelen,
377                                                                    service, servicelen, flags);
378 #endif
379
380         /* Invalid arguments. */
381         if (sa == NULL || (node == NULL && service == NULL))
382                 return EAI_FAIL;
383
384         /* We don't support those. */
385         if ((node && !(flags & NI_NUMERICHOST))
386                 || (service && !(flags & NI_NUMERICSERV)))
387                 return EAI_FAIL;
388
389 #ifdef  HAVE_IPV6
390         if (sa->sa_family == AF_INET6)
391                 return EAI_FAMILY;
392 #endif
393
394         if (node)
395         {
396                 int                     ret = -1;
397
398                 if (sa->sa_family == AF_INET)
399                 {
400                         char       *p;
401
402                         p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr);
403                         ret = snprintf(node, nodelen, "%s", p);
404                 }
405                 if (ret == -1 || ret > nodelen)
406                         return EAI_MEMORY;
407         }
408
409         if (service)
410         {
411                 int                     ret = -1;
412
413                 if (sa->sa_family == AF_INET)
414                 {
415                         ret = snprintf(service, servicelen, "%d",
416                                                    ntohs(((struct sockaddr_in *) sa)->sin_port));
417                 }
418                 if (ret == -1 || ret > servicelen)
419                         return EAI_MEMORY;
420         }
421
422         return 0;
423 }