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