]> granicus.if.org Git - postgresql/blob - src/port/getaddrinfo.c
Standard pgindent run for 8.1.
[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.21 2005/10/15 02:49:51 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 /*
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 routines
78          * are present in the WinSock 2 library (ws2_32.dll). Try that first
79          */
80
81         hLibrary = LoadLibraryA("ws2_32");
82
83         if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL)
84         {
85                 /*
86                  * Well, ws2_32 doesn't exist, or more likely doesn't have
87                  * getaddrinfo.
88                  */
89                 if (hLibrary != NULL)
90                         FreeLibrary(hLibrary);
91
92                 /*
93                  * In Windows 2000, there was only the IPv6 Technology Preview look in
94                  * the IPv6 WinSock library (wship6.dll).
95                  */
96
97                 hLibrary = LoadLibraryA("wship6");
98         }
99
100         /* If hLibrary is null, we couldn't find a dll with functions */
101         if (hLibrary != NULL)
102         {
103                 /* We found a dll, so now get the addresses of the routines */
104
105                 getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary,
106                                                                                                                          "getaddrinfo");
107                 freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary,
108                                                                                                                          "freeaddrinfo");
109                 getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary,
110                                                                                                                          "getnameinfo");
111
112                 /*
113                  * If any one of the routines is missing, let's play it safe and
114                  * ignore them all
115                  */
116                 if (getaddrinfo_ptr == NULL ||
117                         freeaddrinfo_ptr == NULL ||
118                         getnameinfo_ptr == NULL)
119                 {
120                         FreeLibrary(hLibrary);
121                         hLibrary = NULL;
122                         getaddrinfo_ptr = NULL;
123                         freeaddrinfo_ptr = NULL;
124                         getnameinfo_ptr = NULL;
125                 }
126         }
127
128         alreadyLookedForIpv6routines = true;
129         return (getaddrinfo_ptr != NULL);
130 }
131 #endif
132
133
134 /*
135  * get address info for ipv4 sockets.
136  *
137  *      Bugs:   - only one addrinfo is set even though hintp is NULL or
138  *                ai_socktype is 0
139  *              - AI_CANONNAME is not supported.
140  *              - servname can only be a number, not text.
141  */
142 int
143 getaddrinfo(const char *node, const char *service,
144                         const struct addrinfo * hintp,
145                         struct addrinfo ** res)
146 {
147         struct addrinfo *ai;
148         struct sockaddr_in sin,
149                            *psin;
150         struct addrinfo hints;
151
152 #ifdef WIN32
153
154         /*
155          * If Windows has native IPv6 support, use the native Windows routine.
156          * Otherwise, fall through and use our own code.
157          */
158         if (haveNativeWindowsIPv6routines())
159                 return (*getaddrinfo_ptr) (node, service, hintp, res);
160 #endif
161
162         if (hintp == NULL)
163         {
164                 memset(&hints, 0, sizeof(hints));
165                 hints.ai_family = AF_INET;
166                 hints.ai_socktype = SOCK_STREAM;
167         }
168         else
169                 memcpy(&hints, hintp, sizeof(hints));
170
171         if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
172                 return EAI_FAMILY;
173
174         if (hints.ai_socktype == 0)
175                 hints.ai_socktype = SOCK_STREAM;
176
177         if (!node && !service)
178                 return EAI_NONAME;
179
180         memset(&sin, 0, sizeof(sin));
181
182         sin.sin_family = AF_INET;
183
184         if (node)
185         {
186                 if (node[0] == '\0')
187                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
188                 else if (hints.ai_flags & AI_NUMERICHOST)
189                 {
190                         if (!inet_aton(node, &sin.sin_addr))
191                                 return EAI_FAIL;
192                 }
193                 else
194                 {
195                         struct hostent *hp;
196
197 #ifdef FRONTEND
198                         struct hostent hpstr;
199                         char            buf[BUFSIZ];
200                         int                     herrno = 0;
201
202                         pqGethostbyname(node, &hpstr, buf, sizeof(buf),
203                                                         &hp, &herrno);
204 #else
205                         hp = gethostbyname(node);
206 #endif
207                         if (hp == NULL)
208                         {
209                                 switch (h_errno)
210                                 {
211                                         case HOST_NOT_FOUND:
212                                         case NO_DATA:
213                                                 return EAI_NONAME;
214                                         case TRY_AGAIN:
215                                                 return EAI_AGAIN;
216                                         case NO_RECOVERY:
217                                         default:
218                                                 return EAI_FAIL;
219                                 }
220                         }
221                         if (hp->h_addrtype != AF_INET)
222                                 return EAI_FAIL;
223
224                         memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
225                 }
226         }
227         else
228         {
229                 if (hints.ai_flags & AI_PASSIVE)
230                         sin.sin_addr.s_addr = htonl(INADDR_ANY);
231                 else
232                         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
233         }
234
235         if (service)
236                 sin.sin_port = htons((unsigned short) atoi(service));
237
238 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
239         sin.sin_len = sizeof(sin);
240 #endif
241
242         ai = malloc(sizeof(*ai));
243         if (!ai)
244                 return EAI_MEMORY;
245
246         psin = malloc(sizeof(*psin));
247         if (!psin)
248         {
249                 free(ai);
250                 return EAI_MEMORY;
251         }
252
253         memcpy(psin, &sin, sizeof(*psin));
254
255         ai->ai_flags = 0;
256         ai->ai_family = AF_INET;
257         ai->ai_socktype = hints.ai_socktype;
258         ai->ai_protocol = hints.ai_protocol;
259         ai->ai_addrlen = sizeof(*psin);
260         ai->ai_addr = (struct sockaddr *) psin;
261         ai->ai_canonname = NULL;
262         ai->ai_next = NULL;
263
264         *res = ai;
265
266         return 0;
267 }
268
269
270 void
271 freeaddrinfo(struct addrinfo * res)
272 {
273         if (res)
274         {
275 #ifdef WIN32
276
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 #else                                                   /* !HAVE_HSTRERROR */
317
318         switch (errcode)
319         {
320                 case EAI_NONAME:
321                         return "Unknown host";
322                 case EAI_AGAIN:
323                         return "Host name lookup failure";
324                         /* Errors below are probably WIN32 only */
325 #ifdef EAI_BADFLAGS
326                 case EAI_BADFLAGS:
327                         return "Invalid argument";
328 #endif
329 #ifdef EAI_FAMILY
330                 case EAI_FAMILY:
331                         return "Address family not supported";
332 #endif
333 #ifdef EAI_MEMORY
334                 case EAI_MEMORY:
335                         return "Not enough memory";
336 #endif
337 #ifdef EAI_NODATA
338                 case EAI_NODATA:
339                         return "No host data of that type was found";
340 #endif
341 #ifdef EAI_SERVICE
342                 case EAI_SERVICE:
343                         return "Class type not found";
344 #endif
345 #ifdef EAI_SOCKTYPE
346                 case EAI_SOCKTYPE:
347                         return "Socket type not supported";
348 #endif
349                 default:
350                         return "Unknown server error";
351         }
352 #endif   /* HAVE_HSTRERROR */
353 }
354
355 /*
356  * Convert an ipv4 address to a hostname.
357  *
358  * Bugs:        - Only supports NI_NUMERICHOST and NI_NUMERICSERV
359  *                It will never resolv a hostname.
360  *              - No IPv6 support.
361  */
362 int
363 getnameinfo(const struct sockaddr * sa, int salen,
364                         char *node, int nodelen,
365                         char *service, int servicelen, int flags)
366 {
367 #ifdef WIN32
368
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 }