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