1/*
2PostgreSQL Database Management System
3(formerly known as Postgres, then as Postgres95)
4
5Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
6
7Portions Copyright (c) 1994, The Regents of the University of California
8
9Permission to use, copy, modify, and distribute this software and its
10documentation for any purpose, without fee, and without a written agreement
11is hereby granted, provided that the above copyright notice and this paragraph
12and the following two paragraphs appear in all copies.
13
14IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
15DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
16LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
17EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
18SUCH DAMAGE.
19
20THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
24TO 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
57static 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
77static 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
93static 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
106static 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
114static 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
156static 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
203static 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
256int 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
318void rep_freeaddrinfo(struct addrinfo *res)
319{
320	struct addrinfo *next = NULL;
321
322	for (;res; res = next) {
323		next = res->ai_next;
324		if (res->ai_canonname) {
325			free(res->ai_canonname);
326		}
327		if (res->ai_addr) {
328			free(res->ai_addr);
329		}
330		free(res);
331	}
332}
333
334
335const char *rep_gai_strerror(int errcode)
336{
337#ifdef HAVE_HSTRERROR
338	int			hcode;
339
340	switch (errcode)
341	{
342		case EAI_NONAME:
343			hcode = HOST_NOT_FOUND;
344			break;
345		case EAI_AGAIN:
346			hcode = TRY_AGAIN;
347			break;
348		case EAI_FAIL:
349		default:
350			hcode = NO_RECOVERY;
351			break;
352	}
353
354	return hstrerror(hcode);
355#else							/* !HAVE_HSTRERROR */
356
357	switch (errcode)
358	{
359		case EAI_NONAME:
360			return "Unknown host";
361		case EAI_AGAIN:
362			return "Host name lookup failure";
363#ifdef EAI_BADFLAGS
364		case EAI_BADFLAGS:
365			return "Invalid argument";
366#endif
367#ifdef EAI_FAMILY
368		case EAI_FAMILY:
369			return "Address family not supported";
370#endif
371#ifdef EAI_MEMORY
372		case EAI_MEMORY:
373			return "Not enough memory";
374#endif
375#ifdef EAI_NODATA
376		case EAI_NODATA:
377			return "No host data of that type was found";
378#endif
379#ifdef EAI_SERVICE
380		case EAI_SERVICE:
381			return "Class type not found";
382#endif
383#ifdef EAI_SOCKTYPE
384		case EAI_SOCKTYPE:
385			return "Socket type not supported";
386#endif
387		default:
388			return "Unknown server error";
389	}
390#endif   /* HAVE_HSTRERROR */
391}
392
393static int gethostnameinfo(const struct sockaddr *sa,
394			char *node,
395			size_t nodelen,
396			int flags)
397{
398	int ret = -1;
399	char *p = NULL;
400
401	if (!(flags & NI_NUMERICHOST)) {
402		struct hostent *hp = gethostbyaddr(
403				&((struct sockaddr_in *)sa)->sin_addr,
404				sizeof(struct in_addr),
405				sa->sa_family);
406		ret = check_hostent_err(hp);
407		if (ret == 0) {
408			/* Name looked up successfully. */
409			ret = snprintf(node, nodelen, "%s", hp->h_name);
410			if (ret < 0 || (size_t)ret >= nodelen) {
411				return EAI_MEMORY;
412			}
413			if (flags & NI_NOFQDN) {
414				p = strchr(node,'.');
415				if (p) {
416					*p = '\0';
417				}
418			}
419			return 0;
420		}
421
422		if (flags & NI_NAMEREQD) {
423			/* If we require a name and didn't get one,
424			 * automatically fail. */
425			return ret;
426		}
427		/* Otherwise just fall into the numeric host code... */
428	}
429	p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
430	ret = snprintf(node, nodelen, "%s", p);
431	if (ret < 0 || (size_t)ret >= nodelen) {
432		return EAI_MEMORY;
433	}
434	return 0;
435}
436
437static int getservicenameinfo(const struct sockaddr *sa,
438			char *service,
439			size_t servicelen,
440			int flags)
441{
442	int ret = -1;
443	int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
444
445	if (!(flags & NI_NUMERICSERV)) {
446		struct servent *se = getservbyport(
447				port,
448				(flags & NI_DGRAM) ? "udp" : "tcp");
449		if (se && se->s_name) {
450			/* Service name looked up successfully. */
451			ret = snprintf(service, servicelen, "%s", se->s_name);
452			if (ret < 0 || (size_t)ret >= servicelen) {
453				return EAI_MEMORY;
454			}
455			return 0;
456		}
457		/* Otherwise just fall into the numeric service code... */
458	}
459	ret = snprintf(service, servicelen, "%d", port);
460	if (ret < 0 || (size_t)ret >= servicelen) {
461		return EAI_MEMORY;
462	}
463	return 0;
464}
465
466/*
467 * Convert an ipv4 address to a hostname.
468 *
469 * Bugs:	- No IPv6 support.
470 */
471int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen,
472			char *node, size_t nodelen,
473			char *service, size_t servicelen, int flags)
474{
475
476	/* Invalid arguments. */
477	if (sa == NULL || (node == NULL && service == NULL)) {
478		return EAI_FAIL;
479	}
480
481	if (sa->sa_family != AF_INET) {
482		return EAI_FAIL;
483	}
484
485	if (salen < sizeof(struct sockaddr_in)) {
486		return EAI_FAIL;
487	}
488
489	if (node) {
490		return gethostnameinfo(sa, node, nodelen, flags);
491	}
492
493	if (service) {
494		return getservicenameinfo(sa, service, servicelen, flags);
495	}
496	return 0;
497}
498