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