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 "replace.h"
47 #include "system/network.h"
48 
49 #ifndef SMB_MALLOC
50 #define SMB_MALLOC(s) malloc(s)
51 #endif
52 
53 #ifndef SMB_STRDUP
54 #define SMB_STRDUP(s) strdup(s)
55 #endif
56 
check_hostent_err(struct hostent * hp)57 static int check_hostent_err(struct hostent *hp)
58 {
59 	if (!hp) {
60 		switch (h_errno) {
61 			case HOST_NOT_FOUND:
62 			case NO_DATA:
63 				return EAI_NONAME;
64 			case TRY_AGAIN:
65 				return EAI_AGAIN;
66 			case NO_RECOVERY:
67 			default:
68 				return EAI_FAIL;
69 		}
70 	}
71 	if (!hp->h_name || hp->h_addrtype != AF_INET) {
72 		return EAI_FAIL;
73 	}
74 	return 0;
75 }
76 
canon_name_from_hostent(struct hostent * hp,int * perr)77 static char *canon_name_from_hostent(struct hostent *hp,
78 				int *perr)
79 {
80 	char *ret = NULL;
81 
82 	*perr = check_hostent_err(hp);
83 	if (*perr) {
84 		return NULL;
85 	}
86 	ret = SMB_STRDUP(hp->h_name);
87 	if (!ret) {
88 		*perr = EAI_MEMORY;
89 	}
90 	return ret;
91 }
92 
get_my_canon_name(int * perr)93 static char *get_my_canon_name(int *perr)
94 {
95 	char name[HOST_NAME_MAX+1];
96 
97 	if (gethostname(name, HOST_NAME_MAX) == -1) {
98 		*perr = EAI_FAIL;
99 		return NULL;
100 	}
101 	/* Ensure null termination. */
102 	name[HOST_NAME_MAX] = '\0';
103 	return canon_name_from_hostent(gethostbyname(name), perr);
104 }
105 
get_canon_name_from_addr(struct in_addr ip,int * perr)106 static char *get_canon_name_from_addr(struct in_addr ip,
107 				int *perr)
108 {
109 	return canon_name_from_hostent(
110 			gethostbyaddr(&ip, sizeof(ip), AF_INET),
111 			perr);
112 }
113 
alloc_entry(const struct addrinfo * hints,struct in_addr ip,unsigned short port)114 static struct addrinfo *alloc_entry(const struct addrinfo *hints,
115 				struct in_addr ip,
116 				unsigned short port)
117 {
118 	struct sockaddr_in *psin = NULL;
119 	struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
120 
121 	if (!ai) {
122 		return NULL;
123 	}
124 	memset(ai, '\0', sizeof(*ai));
125 
126 	psin = SMB_MALLOC(sizeof(*psin));
127 	if (!psin) {
128 		free(ai);
129 		return NULL;
130 	}
131 
132 	memset(psin, '\0', sizeof(*psin));
133 
134 	psin->sin_family = AF_INET;
135 	psin->sin_port = htons(port);
136 	psin->sin_addr = ip;
137 
138 	ai->ai_flags = 0;
139 	ai->ai_family = AF_INET;
140 	ai->ai_socktype = hints->ai_socktype;
141 	ai->ai_protocol = hints->ai_protocol;
142 	ai->ai_addrlen = sizeof(*psin);
143 	ai->ai_addr = (struct sockaddr *) psin;
144 	ai->ai_canonname = NULL;
145 	ai->ai_next = NULL;
146 
147 	return ai;
148 }
149 
150 /*
151  * get address info for a single ipv4 address.
152  *
153  *	Bugs:	- servname can only be a number, not text.
154  */
155 
getaddr_info_single_addr(const char * service,uint32_t addr,const struct addrinfo * hints,struct addrinfo ** res)156 static int getaddr_info_single_addr(const char *service,
157 				uint32_t addr,
158 				const struct addrinfo *hints,
159 				struct addrinfo **res)
160 {
161 
162 	struct addrinfo *ai = NULL;
163 	struct in_addr ip;
164 	unsigned short port = 0;
165 
166 	if (service) {
167 		port = (unsigned short)atoi(service);
168 	}
169 	ip.s_addr = htonl(addr);
170 
171 	ai = alloc_entry(hints, ip, port);
172 	if (!ai) {
173 		return EAI_MEMORY;
174 	}
175 
176 	/* If we're asked for the canonical name,
177 	 * make sure it returns correctly. */
178 	if (!(hints->ai_flags & AI_NUMERICSERV) &&
179 			hints->ai_flags & AI_CANONNAME) {
180 		int err;
181 		if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
182 			ai->ai_canonname = get_my_canon_name(&err);
183 		} else {
184 			ai->ai_canonname =
185 			get_canon_name_from_addr(ip,&err);
186 		}
187 		if (ai->ai_canonname == NULL) {
188 			freeaddrinfo(ai);
189 			return err;
190 		}
191 	}
192 
193 	*res = ai;
194 	return 0;
195 }
196 
197 /*
198  * get address info for multiple ipv4 addresses.
199  *
200  *	Bugs:	- servname can only be a number, not text.
201  */
202 
getaddr_info_name(const char * node,const char * service,const struct addrinfo * hints,struct addrinfo ** res)203 static int getaddr_info_name(const char *node,
204 				const char *service,
205 				const struct addrinfo *hints,
206 				struct addrinfo **res)
207 {
208 	struct addrinfo *listp = NULL, *prevp = NULL;
209 	char **pptr = NULL;
210 	int err;
211 	struct hostent *hp = NULL;
212 	unsigned short port = 0;
213 
214 	if (service) {
215 		port = (unsigned short)atoi(service);
216 	}
217 
218 	hp = gethostbyname(node);
219 	err = check_hostent_err(hp);
220 	if (err) {
221 		return err;
222 	}
223 
224 	for(pptr = hp->h_addr_list; *pptr; pptr++) {
225 		struct in_addr ip = *(struct in_addr *)*pptr;
226 		struct addrinfo *ai = alloc_entry(hints, ip, port);
227 
228 		if (!ai) {
229 			freeaddrinfo(listp);
230 			return EAI_MEMORY;
231 		}
232 
233 		if (!listp) {
234 			listp = ai;
235 			prevp = ai;
236 			ai->ai_canonname = SMB_STRDUP(hp->h_name);
237 			if (!ai->ai_canonname) {
238 				freeaddrinfo(listp);
239 				return EAI_MEMORY;
240 			}
241 		} else {
242 			prevp->ai_next = ai;
243 			prevp = ai;
244 		}
245 	}
246 	*res = listp;
247 	return 0;
248 }
249 
250 /*
251  * get address info for ipv4 sockets.
252  *
253  *	Bugs:	- servname can only be a number, not text.
254  */
255 
rep_getaddrinfo(const char * node,const char * service,const struct addrinfo * hintp,struct addrinfo ** res)256 int rep_getaddrinfo(const char *node,
257 		const char *service,
258 		const struct addrinfo * hintp,
259 		struct addrinfo ** res)
260 {
261 	struct addrinfo hints;
262 
263 	/* Setup the hints struct. */
264 	if (hintp == NULL) {
265 		memset(&hints, 0, sizeof(hints));
266 		hints.ai_family = AF_INET;
267 		hints.ai_socktype = SOCK_STREAM;
268 	} else {
269 		memcpy(&hints, hintp, sizeof(hints));
270 	}
271 
272 	if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
273 		return EAI_FAMILY;
274 	}
275 
276 	if (hints.ai_socktype == 0) {
277 		hints.ai_socktype = SOCK_STREAM;
278 	}
279 
280 	if (!node && !service) {
281 		return EAI_NONAME;
282 	}
283 
284 	if (node) {
285 		if (node[0] == '\0') {
286 			return getaddr_info_single_addr(service,
287 					INADDR_ANY,
288 					&hints,
289 					res);
290 		} else if (hints.ai_flags & AI_NUMERICHOST) {
291 			struct in_addr ip;
292 			if (!inet_aton(node, &ip)) {
293 				return EAI_FAIL;
294 			}
295 			return getaddr_info_single_addr(service,
296 					ntohl(ip.s_addr),
297 					&hints,
298 					res);
299 		} else {
300 			return getaddr_info_name(node,
301 						service,
302 						&hints,
303 						res);
304 		}
305 	} else if (hints.ai_flags & AI_PASSIVE) {
306 		return getaddr_info_single_addr(service,
307 					INADDR_ANY,
308 					&hints,
309 					res);
310 	}
311 	return getaddr_info_single_addr(service,
312 					INADDR_LOOPBACK,
313 					&hints,
314 					res);
315 }
316 
317 
rep_freeaddrinfo(struct addrinfo * res)318 void rep_freeaddrinfo(struct addrinfo *res)
319 {
320 	struct addrinfo *next = NULL;
321 
322 	for (;res; res = next) {
323 		next = res->ai_next;
324 		free(res->ai_canonname);
325 		free(res->ai_addr);
326 		free(res);
327 	}
328 }
329 
330 
rep_gai_strerror(int errcode)331 const char *rep_gai_strerror(int errcode)
332 {
333 #ifdef HAVE_HSTRERROR
334 	int			hcode;
335 
336 	switch (errcode)
337 	{
338 		case EAI_NONAME:
339 			hcode = HOST_NOT_FOUND;
340 			break;
341 		case EAI_AGAIN:
342 			hcode = TRY_AGAIN;
343 			break;
344 		case EAI_FAIL:
345 		default:
346 			hcode = NO_RECOVERY;
347 			break;
348 	}
349 
350 	return hstrerror(hcode);
351 #else							/* !HAVE_HSTRERROR */
352 
353 	switch (errcode)
354 	{
355 		case EAI_NONAME:
356 			return "Unknown host";
357 		case EAI_AGAIN:
358 			return "Host name lookup failure";
359 #ifdef EAI_BADFLAGS
360 		case EAI_BADFLAGS:
361 			return "Invalid argument";
362 #endif
363 #ifdef EAI_FAMILY
364 		case EAI_FAMILY:
365 			return "Address family not supported";
366 #endif
367 #ifdef EAI_MEMORY
368 		case EAI_MEMORY:
369 			return "Not enough memory";
370 #endif
371 #ifdef EAI_NODATA
372 		case EAI_NODATA:
373 			return "No host data of that type was found";
374 #endif
375 #ifdef EAI_SERVICE
376 		case EAI_SERVICE:
377 			return "Class type not found";
378 #endif
379 #ifdef EAI_SOCKTYPE
380 		case EAI_SOCKTYPE:
381 			return "Socket type not supported";
382 #endif
383 		default:
384 			return "Unknown server error";
385 	}
386 #endif   /* HAVE_HSTRERROR */
387 }
388 
gethostnameinfo(const struct sockaddr * sa,char * node,size_t nodelen,int flags)389 static int gethostnameinfo(const struct sockaddr *sa,
390 			char *node,
391 			size_t nodelen,
392 			int flags)
393 {
394 	int ret = -1;
395 	char *p = NULL;
396 
397 	if (!(flags & NI_NUMERICHOST)) {
398 		struct hostent *hp = gethostbyaddr(
399 				&((struct sockaddr_in *)sa)->sin_addr,
400 				sizeof(struct in_addr),
401 				sa->sa_family);
402 		ret = check_hostent_err(hp);
403 		if (ret == 0) {
404 			/* Name looked up successfully. */
405 			ret = snprintf(node, nodelen, "%s", hp->h_name);
406 			if (ret < 0 || (size_t)ret >= nodelen) {
407 				return EAI_MEMORY;
408 			}
409 			if (flags & NI_NOFQDN) {
410 				p = strchr(node,'.');
411 				if (p) {
412 					*p = '\0';
413 				}
414 			}
415 			return 0;
416 		}
417 
418 		if (flags & NI_NAMEREQD) {
419 			/* If we require a name and didn't get one,
420 			 * automatically fail. */
421 			return ret;
422 		}
423 		/* Otherwise just fall into the numeric host code... */
424 	}
425 	p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
426 	ret = snprintf(node, nodelen, "%s", p);
427 	if (ret < 0 || (size_t)ret >= nodelen) {
428 		return EAI_MEMORY;
429 	}
430 	return 0;
431 }
432 
getservicenameinfo(const struct sockaddr * sa,char * service,size_t servicelen,int flags)433 static int getservicenameinfo(const struct sockaddr *sa,
434 			char *service,
435 			size_t servicelen,
436 			int flags)
437 {
438 	int ret = -1;
439 	int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
440 
441 	if (!(flags & NI_NUMERICSERV)) {
442 		struct servent *se = getservbyport(
443 				port,
444 				(flags & NI_DGRAM) ? "udp" : "tcp");
445 		if (se && se->s_name) {
446 			/* Service name looked up successfully. */
447 			ret = snprintf(service, servicelen, "%s", se->s_name);
448 			if (ret < 0 || (size_t)ret >= servicelen) {
449 				return EAI_MEMORY;
450 			}
451 			return 0;
452 		}
453 		/* Otherwise just fall into the numeric service code... */
454 	}
455 	ret = snprintf(service, servicelen, "%d", port);
456 	if (ret < 0 || (size_t)ret >= servicelen) {
457 		return EAI_MEMORY;
458 	}
459 	return 0;
460 }
461 
462 /*
463  * Convert an ipv4 address to a hostname.
464  *
465  * Bugs:	- No IPv6 support.
466  */
rep_getnameinfo(const struct sockaddr * sa,socklen_t salen,char * node,size_t nodelen,char * service,size_t servicelen,int flags)467 int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen,
468 			char *node, size_t nodelen,
469 			char *service, size_t servicelen, int flags)
470 {
471 
472 	/* Invalid arguments. */
473 	if (sa == NULL || (node == NULL && service == NULL)) {
474 		return EAI_FAIL;
475 	}
476 
477 	if (sa->sa_family != AF_INET) {
478 		return EAI_FAIL;
479 	}
480 
481 	if (salen < sizeof(struct sockaddr_in)) {
482 		return EAI_FAIL;
483 	}
484 
485 	if (node) {
486 		return gethostnameinfo(sa, node, nodelen, flags);
487 	}
488 
489 	if (service) {
490 		return getservicenameinfo(sa, service, servicelen, flags);
491 	}
492 	return 0;
493 }
494