1 /*
2 PostgreSQL Database Management System
3 (formerly known as Postgres, then as Postgres95)
4 
5 Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
6 
7 Portions Copyright (c) 1994, The Regents of the University of California
8 
9 Permission to use, copy, modify, and distribute this software and its
10 documentation for any purpose, without fee, and without a written agreement
11 is hereby granted, provided that the above copyright notice and this paragraph
12 and the following two paragraphs appear in all copies.
13 
14 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
15 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
16 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
17 EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
18 SUCH DAMAGE.
19 
20 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
24 TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 
26 */
27 
28 /*-------------------------------------------------------------------------
29  *
30  * getaddrinfo.c
31  *	  Support getaddrinfo() on platforms that don't have it.
32  *
33  * We also supply getnameinfo() here, assuming that the platform will have
34  * it if and only if it has getaddrinfo().	If this proves false on some
35  * platform, we'll need to split this file and provide a separate configure
36  * test for getnameinfo().
37  *
38  * Copyright (c) 2003-2007, PostgreSQL Global Development Group
39  *
40  * Copyright (C) 2007 Jeremy Allison.
41  * Modified to return multiple IPv4 addresses for Samba.
42  *
43  *-------------------------------------------------------------------------
44  */
45 
46 #include <platform.h>
47 
48 #ifndef SMB_MALLOC
49 #define SMB_MALLOC(s) malloc(s)
50 #endif
51 
52 #ifndef SMB_STRDUP
53 #define SMB_STRDUP(s) strdup(s)
54 #endif
55 
56 #ifndef HOST_NAME_MAX
57 #define HOST_NAME_MAX 255
58 #endif
59 
check_hostent_err(struct hostent * hp)60 static int check_hostent_err(struct hostent *hp)
61 {
62 #ifndef INET6
63 	extern int h_errno;
64 #endif
65 	if (!hp) {
66 		switch (h_errno) {
67 			case HOST_NOT_FOUND:
68 			case NO_DATA:
69 				return EAI_NONAME;
70 			case TRY_AGAIN:
71 				return EAI_AGAIN;
72 			case NO_RECOVERY:
73 			default:
74 				return EAI_FAIL;
75 		}
76 	}
77 	if (!hp->h_name || hp->h_addrtype != AF_INET) {
78 		return EAI_FAIL;
79 	}
80 	return 0;
81 }
82 
canon_name_from_hostent(struct hostent * hp,int * perr)83 static char *canon_name_from_hostent(struct hostent *hp,
84 				int *perr)
85 {
86 	char *ret = NULL;
87 
88 	*perr = check_hostent_err(hp);
89 	if (*perr) {
90 		return NULL;
91 	}
92 	ret = SMB_STRDUP(hp->h_name);
93 	if (!ret) {
94 		*perr = EAI_MEMORY;
95 	}
96 	return ret;
97 }
98 
get_my_canon_name(int * perr)99 static char *get_my_canon_name(int *perr)
100 {
101 	char name[HOST_NAME_MAX+1];
102 
103 	if (gethostname(name, HOST_NAME_MAX) == -1) {
104 		*perr = EAI_FAIL;
105 		return NULL;
106 	}
107 	/* Ensure null termination. */
108 	name[HOST_NAME_MAX] = '\0';
109 	return canon_name_from_hostent(gethostbyname(name), perr);
110 }
111 
get_canon_name_from_addr(struct in_addr ip,int * perr)112 static char *get_canon_name_from_addr(struct in_addr ip,
113 				int *perr)
114 {
115 	return canon_name_from_hostent(
116 			gethostbyaddr((void *)&ip, sizeof ip, AF_INET),
117 			perr);
118 }
119 
alloc_entry(const struct addrinfo * hints,struct in_addr ip,unsigned short port)120 static struct addrinfo *alloc_entry(const struct addrinfo *hints,
121 				struct in_addr ip,
122 				unsigned short port)
123 {
124 	struct sockaddr_in *psin = NULL;
125 	struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
126 
127 	if (!ai) {
128 		return NULL;
129 	}
130 	memset(ai, '\0', sizeof(*ai));
131 
132 	psin = SMB_MALLOC(sizeof(*psin));
133 	if (!psin) {
134 		free(ai);
135 		return NULL;
136 	}
137 
138 	memset(psin, '\0', sizeof(*psin));
139 
140 	psin->sin_family = AF_INET;
141 	psin->sin_port = htons(port);
142 	psin->sin_addr = ip;
143 
144 	ai->ai_flags = 0;
145 	ai->ai_family = AF_INET;
146 	ai->ai_socktype = hints->ai_socktype;
147 	ai->ai_protocol = hints->ai_protocol;
148 	ai->ai_addrlen = sizeof(*psin);
149 	ai->ai_addr = (struct sockaddr *) psin;
150 	ai->ai_canonname = NULL;
151 	ai->ai_next = NULL;
152 
153 	return ai;
154 }
155 
156 /*
157  * get address info for a single ipv4 address.
158  *
159  *	Bugs:	- servname can only be a number, not text.
160  */
161 
getaddr_info_single_addr(const char * service,uint32_t addr,const struct addrinfo * hints,struct addrinfo ** res)162 static int getaddr_info_single_addr(const char *service,
163 				uint32_t addr,
164 				const struct addrinfo *hints,
165 				struct addrinfo **res)
166 {
167 
168 	struct addrinfo *ai = NULL;
169 	struct in_addr ip;
170 	unsigned short port = 0;
171 
172 	if (service) {
173 		port = (unsigned short)atoi(service);
174 	}
175 	ip.s_addr = htonl(addr);
176 
177 	ai = alloc_entry(hints, ip, port);
178 	if (!ai) {
179 		return EAI_MEMORY;
180 	}
181 
182 	/* If we're asked for the canonical name,
183 	 * make sure it returns correctly. */
184 	if (!(hints->ai_flags & AI_NUMERICSERV) &&
185 			hints->ai_flags & AI_CANONNAME) {
186 		int err;
187 		if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
188 			ai->ai_canonname = get_my_canon_name(&err);
189 		} else {
190 			ai->ai_canonname =
191 			get_canon_name_from_addr(ip,&err);
192 		}
193 		if (ai->ai_canonname == NULL) {
194 			freeaddrinfo(ai);
195 			return err;
196 		}
197 	}
198 
199 	*res = ai;
200 	return 0;
201 }
202 
203 /*
204  * get address info for multiple ipv4 addresses.
205  *
206  *	Bugs:	- servname can only be a number, not text.
207  */
208 
getaddr_info_name(const char * node,const char * service,const struct addrinfo * hints,struct addrinfo ** res)209 static int getaddr_info_name(const char *node,
210 				const char *service,
211 				const struct addrinfo *hints,
212 				struct addrinfo **res)
213 {
214 	struct addrinfo *listp = NULL, *prevp = NULL;
215 	char **pptr = NULL;
216 	int err;
217 	struct hostent *hp = NULL;
218 	unsigned short port = 0;
219 
220 	if (service) {
221 		port = (unsigned short)atoi(service);
222 	}
223 
224 	hp = gethostbyname(node);
225 	err = check_hostent_err(hp);
226 	if (err) {
227 		return err;
228 	}
229 
230 	for(pptr = hp->h_addr_list; *pptr; pptr++) {
231 		struct in_addr ip = *(struct in_addr *)*pptr;
232 		struct addrinfo *ai = alloc_entry(hints, ip, port);
233 
234 		if (!ai) {
235 			freeaddrinfo(listp);
236 			return EAI_MEMORY;
237 		}
238 
239 		if (!listp) {
240 			listp = ai;
241 			prevp = ai;
242 			ai->ai_canonname = SMB_STRDUP(hp->h_name);
243 			if (!ai->ai_canonname) {
244 				freeaddrinfo(listp);
245 				return EAI_MEMORY;
246 			}
247 		} else {
248 			prevp->ai_next = ai;
249 			prevp = ai;
250 		}
251 	}
252 	*res = listp;
253 	return 0;
254 }
255 
256 /*
257  * get address info for ipv4 sockets.
258  *
259  *	Bugs:	- servname can only be a number, not text.
260  */
261 
getaddrinfo(const char * node,const char * service,const struct addrinfo * hintp,struct addrinfo ** res)262 int getaddrinfo(const char *node,
263 		const char *service,
264 		const struct addrinfo * hintp,
265 		struct addrinfo ** res)
266 {
267 	struct addrinfo hints;
268 
269 	/* Setup the hints struct. */
270 	if (hintp == NULL) {
271 		memset(&hints, 0, sizeof(hints));
272 		hints.ai_family = AF_INET;
273 		hints.ai_socktype = SOCK_STREAM;
274 	} else {
275 		memcpy(&hints, hintp, sizeof(hints));
276 	}
277 
278 	if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
279 		return EAI_FAMILY;
280 	}
281 
282 	if (hints.ai_socktype == 0) {
283 		hints.ai_socktype = SOCK_STREAM;
284 	}
285 
286 	if (!node && !service) {
287 		return EAI_NONAME;
288 	}
289 
290 	if (node) {
291 		if (node[0] == '\0') {
292 			return getaddr_info_single_addr(service,
293 					INADDR_ANY,
294 					&hints,
295 					res);
296 		} else if (hints.ai_flags & AI_NUMERICHOST) {
297 			struct in_addr ip;
298 			if (inet_pton(AF_INET, node, &ip) <= 0)
299 				return EAI_FAIL;
300 			return getaddr_info_single_addr(service,
301 					ntohl(ip.s_addr),
302 					&hints,
303 					res);
304 		} else {
305 			return getaddr_info_name(node,
306 						service,
307 						&hints,
308 						res);
309 		}
310 	} else if (hints.ai_flags & AI_PASSIVE) {
311 		return getaddr_info_single_addr(service,
312 					INADDR_ANY,
313 					&hints,
314 					res);
315 	}
316 	return getaddr_info_single_addr(service,
317 					INADDR_LOOPBACK,
318 					&hints,
319 					res);
320 }
321 
322 
freeaddrinfo(struct addrinfo * res)323 void freeaddrinfo(struct addrinfo *res)
324 {
325 	struct addrinfo *next = NULL;
326 
327 	for (;res; res = next) {
328 		next = res->ai_next;
329 		if (res->ai_canonname) {
330 			free(res->ai_canonname);
331 		}
332 		if (res->ai_addr) {
333 			free(res->ai_addr);
334 		}
335 		free(res);
336 	}
337 }
338 
339 
gai_strerror(int errcode)340 const char *gai_strerror(int errcode)
341 {
342 #ifdef HAVE_HSTRERROR
343 	int			hcode;
344 
345 	switch (errcode)
346 	{
347 		case EAI_NONAME:
348 			hcode = HOST_NOT_FOUND;
349 			break;
350 		case EAI_AGAIN:
351 			hcode = TRY_AGAIN;
352 			break;
353 		case EAI_FAIL:
354 		default:
355 			hcode = NO_RECOVERY;
356 			break;
357 	}
358 
359 	return hstrerror(hcode);
360 #else							/* !HAVE_HSTRERROR */
361 
362 	switch (errcode)
363 	{
364 		case EAI_NONAME:
365 			return "Unknown host";
366 		case EAI_AGAIN:
367 			return "Host name lookup failure";
368 #ifdef EAI_BADFLAGS
369 		case EAI_BADFLAGS:
370 			return "Invalid argument";
371 #endif
372 #ifdef EAI_FAMILY
373 		case EAI_FAMILY:
374 			return "Address family not supported";
375 #endif
376 #ifdef EAI_MEMORY
377 		case EAI_MEMORY:
378 			return "Not enough memory";
379 #endif
380 #ifdef EAI_NODATA
381 		case EAI_NODATA:
382 			return "No host data of that type was found";
383 #endif
384 #ifdef EAI_SERVICE
385 		case EAI_SERVICE:
386 			return "Class type not found";
387 #endif
388 #ifdef EAI_SOCKTYPE
389 		case EAI_SOCKTYPE:
390 			return "Socket type not supported";
391 #endif
392 		default:
393 			return "Unknown server error";
394 	}
395 #endif   /* HAVE_HSTRERROR */
396 }
397 
gethostnameinfo(const struct sockaddr * sa,char * node,size_t nodelen,int flags)398 static int gethostnameinfo(const struct sockaddr *sa,
399 			char *node,
400 			size_t nodelen,
401 			int flags)
402 {
403 	int ret = -1;
404 	char *p = NULL;
405 
406 	if (!(flags & NI_NUMERICHOST)) {
407 		struct hostent *hp = gethostbyaddr(
408 				(void *)&((struct sockaddr_in *)sa)->sin_addr,
409 				sizeof (struct in_addr),
410 				sa->sa_family);
411 		ret = check_hostent_err(hp);
412 		if (ret == 0) {
413 			/* Name looked up successfully. */
414 			ret = snprintf(node, nodelen, "%s", hp->h_name);
415 			if (ret < 0 || (size_t)ret >= nodelen) {
416 				return EAI_MEMORY;
417 			}
418 			if (flags & NI_NOFQDN) {
419 				p = strchr(node,'.');
420 				if (p) {
421 					*p = '\0';
422 				}
423 			}
424 			return 0;
425 		}
426 
427 		if (flags & NI_NAMEREQD) {
428 			/* If we require a name and didn't get one,
429 			 * automatically fail. */
430 			return ret;
431 		}
432 		/* Otherwise just fall into the numeric host code... */
433 	}
434 	p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
435 	ret = snprintf(node, nodelen, "%s", p);
436 	if (ret < 0 || (size_t)ret >= nodelen) {
437 		return EAI_MEMORY;
438 	}
439 	return 0;
440 }
441 
getservicenameinfo(const struct sockaddr * sa,char * service,size_t servicelen,int flags)442 static int getservicenameinfo(const struct sockaddr *sa,
443 			char *service,
444 			size_t servicelen,
445 			int flags)
446 {
447 	int ret = -1;
448 	int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
449 
450 	if (!(flags & NI_NUMERICSERV)) {
451 		struct servent *se = getservbyport(
452 				port,
453 				(flags & NI_DGRAM) ? "udp" : "tcp");
454 		if (se && se->s_name) {
455 			/* Service name looked up successfully. */
456 			ret = snprintf(service, servicelen, "%s", se->s_name);
457 			if (ret < 0 || (size_t)ret >= servicelen) {
458 				return EAI_MEMORY;
459 			}
460 			return 0;
461 		}
462 		/* Otherwise just fall into the numeric service code... */
463 	}
464 	ret = snprintf(service, servicelen, "%d", port);
465 	if (ret < 0 || (size_t)ret >= servicelen) {
466 		return EAI_MEMORY;
467 	}
468 	return 0;
469 }
470 
471 /*
472  * Convert an ipv4 address to a hostname.
473  *
474  * Bugs:	- No IPv6 support.
475  */
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * node,socklen_t nodelen,char * service,socklen_t servicelen,int flags)476 int getnameinfo(const struct sockaddr *sa, socklen_t salen,
477 			char *node, socklen_t nodelen,
478 			char *service, socklen_t servicelen, int flags)
479 {
480 
481 	/* Invalid arguments. */
482 	if (sa == NULL || (node == NULL && service == NULL)) {
483 		return EAI_FAIL;
484 	}
485 
486 	if (sa->sa_family != AF_INET) {
487 		return EAI_FAIL;
488 	}
489 
490 	if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
491 		return EAI_FAIL;
492 	}
493 
494 	if (node) {
495 		int ret = gethostnameinfo(sa, node, nodelen, flags);
496 		if (ret)
497 			return ret;
498 	}
499 
500 	if (service) {
501 		return getservicenameinfo(sa, service, servicelen, flags);
502 	}
503 	return 0;
504 }
505