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