1 /*
2  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include "config.h"
31 
32 #include <sofia-sip/su.h>
33 #include <sofia-sip/su_addrinfo.h>
34 
35 #ifndef IN_LOOPBACKNET
36 #define IN_LOOPBACKNET          127
37 #endif
38 
39 #ifndef IN_EXPERIMENTAL
40 #define IN_EXPERIMENTAL(a)      ((((long int) (a)) & 0xf0000000) == 0xf0000000)
41 #endif
42 
43 #if !HAVE_GETADDRINFO
44 
45 #include <string.h>
46 #include <stddef.h>
47 #include <stdlib.h>
48 #include <ctype.h>
49 
50 #ifndef EAI_NODATA
51 #define EAI_NODATA 7
52 #endif
53 
54 /*
55  * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator.
56  *
57  * Issues to be discussed:
58  * - Thread safe-ness must be checked.
59  * - Return values.  There are nonstandard return values defined and used
60  *   in the source code.  This is because RFC2133 is silent about which error
61  *   code must be returned for which situation.
62  * - PF_UNSPEC case would be handled in getipnodebyname() with the AI_ALL flag.
63  */
64 
65 #if defined(__KAME__) && defined(INET6)
66 # define FAITH
67 #endif
68 
69 #define SUCCESS 0
70 #define GAI_ANY 0
71 #define YES 1
72 #define NO  0
73 
74 #undef SU_HAVE_IN6
75 
76 #ifdef FAITH
77 static int translate = NO;
78 static struct in6_addr faith_prefix = IN6ADDR_GAI_ANY_INIT;
79 #endif
80 
81 static const char in_addrany[] = { 0, 0, 0, 0 };
82 static const char in6_addrany[] = {
83 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
84 
85 };
86 static const char in_loopback[] = { 127, 0, 0, 1 };
87 static const char in6_loopback[] = {
88 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
89 };
90 
91 struct sockinet {
92 	u_char	si_len;
93 	u_char	si_family;
94 	u_short	si_port;
95 };
96 
97 static struct gai_afd {
98 	int a_af;
99 	int a_addrlen;
100 	int a_socklen;
101 	int a_off;
102 	const char *a_addrany;
103 	const char *a_loopback;
104 } gai_afdl [] = {
105 #if SU_HAVE_IN6
106 #define N_INET6 0
107 	{PF_INET6, sizeof(struct in6_addr),
108 	 sizeof(struct sockaddr_in6),
109 	 offsetof(struct sockaddr_in6, sin6_addr),
110 	 in6_addrany, in6_loopback},
111 #define N_INET  1
112 #else
113 #define N_INET  0
114 #endif
115 	{PF_INET, sizeof(struct in_addr),
116 	 sizeof(struct sockaddr_in),
117 	 offsetof(struct sockaddr_in, sin_addr),
118 	 in_addrany, in_loopback},
119 	{0, 0, 0, 0, NULL, NULL},
120 };
121 
122 #if SU_HAVE_IN6
123 #define PTON_MAX	16
124 #else
125 #define PTON_MAX	4
126 #endif
127 
128 #if SU_HAVE_IN6 && !defined(s6_addr8)
129 #  define s6_addr8 s6_addr
130 #endif
131 
132 #if !SU_HAVE_IN6
133 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
134 #else
135 extern int h_errno;
136 #endif
137 #endif
138 
139 static int get_name(const char *, struct gai_afd *,
140 		    struct addrinfo **, char *, struct addrinfo *,
141 		    int);
142 static int get_addr(const char *, int, struct addrinfo **,
143 		    struct addrinfo *, int);
144 static int str_isnumber(const char *);
145 
146 #define GET_CANONNAME(ai, str) \
147 if (pai->ai_flags & AI_CANONNAME) {\
148 	if (((ai)->ai_canonname = (char *)malloc(strlen(str) + 1)) != NULL) {\
149 		strcpy((ai)->ai_canonname, (str));\
150 	} else {\
151 		error = EAI_MEMORY;\
152 		goto free;\
153 	}\
154 }
155 
156 #if SU_HAVE_SOCKADDR_SA_LEN
157 #define GET_AI(ai, gai_afd, addr, port) {\
158 	char *p;\
159 	if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\
160 					      ((gai_afd)->a_socklen)))\
161 	    == NULL) goto free;\
162 	memcpy(ai, pai, sizeof(struct addrinfo));\
163 	(ai)->ai_addr = (struct sockaddr *)((ai) + 1);\
164 	memset((ai)->ai_addr, 0, (gai_afd)->a_socklen);\
165 	(ai)->ai_addr->sa_len = (ai)->ai_addrlen = (gai_afd)->a_socklen;\
166 	(ai)->ai_addr->sa_family = (ai)->ai_family = (gai_afd)->a_af;\
167 	((struct sockinet *)(ai)->ai_addr)->si_port = port;\
168 	p = (char *)((ai)->ai_addr);\
169 	memcpy(p + (gai_afd)->a_off, (addr), (gai_afd)->a_addrlen);\
170 }
171 #else
172 #define GET_AI(ai, gai_afd, addr, port) {\
173 	char *p;\
174 	if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\
175 					      ((gai_afd)->a_socklen)))\
176 	    == NULL) goto free;\
177 	memcpy(ai, pai, sizeof(struct addrinfo));\
178 	(ai)->ai_addr = (struct sockaddr *)((ai) + 1);\
179 	memset((ai)->ai_addr, 0, (gai_afd)->a_socklen);\
180 	(ai)->ai_addrlen = (gai_afd)->a_socklen; \
181 	(ai)->ai_addr->sa_family = (ai)->ai_family = (gai_afd)->a_af;\
182 	((struct sockinet *)(ai)->ai_addr)->si_port = port;\
183 	p = (char *)((ai)->ai_addr);\
184 	memcpy(p + (gai_afd)->a_off, (addr), (gai_afd)->a_addrlen);\
185 }
186 #endif
187 
188 #define ERR(err) { error = (err); goto bad; }
189 
190 static int
str_isnumber(p)191 str_isnumber(p)
192 	const char *p;
193 {
194 	char *q = (char *)p;
195 	while (*q) {
196 		if (! isdigit(*q))
197 			return NO;
198 		q++;
199 	}
200 	return YES;
201 }
202 
203 static
204 int
getaddrinfo(hostname,servname,hints,res)205 getaddrinfo(hostname, servname, hints, res)
206 	const char *hostname, *servname;
207 	const struct addrinfo *hints;
208 	struct addrinfo **res;
209 {
210 	struct addrinfo sentinel;
211 	struct addrinfo *top = NULL;
212 	struct addrinfo *cur;
213 	int i, error = 0;
214 	char pton[PTON_MAX];
215 	struct addrinfo ai;
216 	struct addrinfo *pai;
217 	u_short port;
218 
219 #ifdef FAITH
220 	static int firsttime = 1;
221 
222 	if (firsttime) {
223 		/* translator hack */
224 		{
225 			char *q = getenv("GAI");
226 			if (q && su_inet_pton(AF_INET6, q, &faith_prefix) == 1)
227 				translate = YES;
228 		}
229 		firsttime = 0;
230 	}
231 #endif
232 
233 	/* initialize file static vars */
234 	sentinel.ai_next = NULL;
235 	cur = &sentinel;
236 	pai = &ai;
237 	pai->ai_flags = 0;
238 	pai->ai_family = PF_UNSPEC;
239 	pai->ai_socktype = GAI_ANY;
240 	pai->ai_protocol = GAI_ANY;
241 	pai->ai_addrlen = 0;
242 	pai->ai_canonname = NULL;
243 	pai->ai_addr = NULL;
244 	pai->ai_next = NULL;
245 	port = GAI_ANY;
246 
247 	if (hostname == NULL && servname == NULL)
248 		return EAI_NONAME;
249 	if (hints) {
250 		/* error check for hints */
251 		if (hints->ai_addrlen || hints->ai_canonname ||
252 		    hints->ai_addr || hints->ai_next)
253 			ERR(EAI_BADHINTS); /* xxx */
254 		if (hints->ai_flags & ~AI_MASK)
255 			ERR(EAI_BADFLAGS);
256 		switch (hints->ai_family) {
257 		case PF_UNSPEC:
258 		case PF_INET:
259 #if SU_HAVE_IN6
260 		case PF_INET6:
261 #endif
262 			break;
263 		default:
264 			ERR(EAI_FAMILY);
265 		}
266 		memcpy(pai, hints, sizeof(*pai));
267 		switch (pai->ai_socktype) {
268 		case GAI_ANY:
269 			switch (pai->ai_protocol) {
270 			case GAI_ANY:
271 				break;
272 			case IPPROTO_UDP:
273 				pai->ai_socktype = SOCK_DGRAM;
274 				break;
275 			case IPPROTO_TCP:
276 				pai->ai_socktype = SOCK_STREAM;
277 				break;
278 #if HAVE_SCTP
279 			case IPPROTO_SCTP:
280 				pai->ai_socktype = SOCK_STREAM;
281 				break;
282 #endif
283 			default:
284 				pai->ai_socktype = SOCK_RAW;
285 				break;
286 			}
287 			break;
288 		case SOCK_RAW:
289 			break;
290 		case SOCK_DGRAM:
291 			if (pai->ai_protocol != IPPROTO_UDP &&
292 #if HAVE_SCTP
293 			    pai->ai_protocol != IPPROTO_SCTP &&
294 #endif
295 			    pai->ai_protocol != GAI_ANY)
296 				ERR(EAI_BADHINTS);	/*xxx*/
297 			if (pai->ai_protocol == GAI_ANY)
298 				pai->ai_protocol = IPPROTO_UDP;
299 			break;
300 		case SOCK_STREAM:
301 			if (pai->ai_protocol != IPPROTO_TCP &&
302 #if HAVE_SCTP
303 			    pai->ai_protocol != IPPROTO_SCTP &&
304 #endif
305 			    pai->ai_protocol != GAI_ANY)
306 				ERR(EAI_BADHINTS);	/*xxx*/
307 			if (pai->ai_protocol == GAI_ANY)
308 				pai->ai_protocol = IPPROTO_TCP;
309 			break;
310 #if HAVE_SCTP
311 		case SOCK_SEQPACKET:
312 			if (pai->ai_protocol != IPPROTO_SCTP &&
313 			    pai->ai_protocol != GAI_ANY)
314 				ERR(EAI_BADHINTS);	/*xxx*/
315 
316 			if (pai->ai_protocol == GAI_ANY)
317 				pai->ai_protocol = IPPROTO_SCTP;
318 			break;
319 #endif
320 		default:
321 			ERR(EAI_SOCKTYPE);
322 			break;
323 		}
324 	}
325 
326 	/*
327 	 * service port
328 	 */
329 	if (servname) {
330 		if (str_isnumber(servname)) {
331 			if (pai->ai_socktype == GAI_ANY) {
332 				/* caller accept *GAI_ANY* socktype */
333 				pai->ai_socktype = SOCK_DGRAM;
334 				pai->ai_protocol = IPPROTO_UDP;
335 			}
336 			port = htons(atoi(servname));
337 		} else {
338 			struct servent *sp;
339 			char *proto;
340 
341 			proto = NULL;
342 			switch (pai->ai_socktype) {
343 			case GAI_ANY:
344 				proto = NULL;
345 				break;
346 			case SOCK_DGRAM:
347 				proto = "udp";
348 				break;
349 			case SOCK_STREAM:
350 				proto = "tcp";
351 				break;
352 			default:
353 				fprintf(stderr, "panic!\n");
354 				break;
355 			}
356 			if ((sp = getservbyname(servname, proto)) == NULL)
357 				ERR(EAI_SERVICE);
358 			port = sp->s_port;
359 			if (pai->ai_socktype == GAI_ANY) {
360 				if (strcmp(sp->s_proto, "udp") == 0) {
361 					pai->ai_socktype = SOCK_DGRAM;
362 					pai->ai_protocol = IPPROTO_UDP;
363 				} else if (strcmp(sp->s_proto, "tcp") == 0) {
364 					pai->ai_socktype = SOCK_STREAM;
365 					pai->ai_protocol = IPPROTO_TCP;
366 #if HAVE_SCTP
367 				} else if (strcmp(sp->s_proto, "sctp") == 0) {
368 					pai->ai_socktype = SOCK_STREAM;
369 					pai->ai_protocol = IPPROTO_SCTP;
370 #endif
371 				} else
372 					ERR(EAI_PROTOCOL);	/*xxx*/
373 			}
374 		}
375 	}
376 
377 	/*
378 	 * hostname == NULL.
379 	 * passive socket -> anyaddr (0.0.0.0 or ::)
380 	 * non-passive socket -> localhost (127.0.0.1 or ::1)
381 	 */
382 	if (hostname == NULL) {
383 		struct gai_afd *gai_afd;
384 
385 		for (gai_afd = &gai_afdl[0]; gai_afd->a_af; gai_afd++) {
386 			if (!(pai->ai_family == PF_UNSPEC
387 			   || pai->ai_family == gai_afd->a_af)) {
388 				continue;
389 			}
390 
391 			if (pai->ai_flags & AI_PASSIVE) {
392 				GET_AI(cur->ai_next, gai_afd, gai_afd->a_addrany, port);
393 				/* xxx meaningless?
394 				 * GET_CANONNAME(cur->ai_next, "anyaddr");
395 				 */
396 			} else {
397 				GET_AI(cur->ai_next, gai_afd, gai_afd->a_loopback,
398 					port);
399 				/* xxx meaningless?
400 				 * GET_CANONNAME(cur->ai_next, "localhost");
401 				 */
402 			}
403 			cur = cur->ai_next;
404 		}
405 		top = sentinel.ai_next;
406 		if (top)
407 			goto good;
408 		else
409 			ERR(EAI_FAMILY);
410 	}
411 
412 	/* hostname as numeric name */
413 	for (i = 0; gai_afdl[i].a_af; i++) {
414 		if (su_inet_pton(gai_afdl[i].a_af, hostname, pton)) {
415 			u_long v4a;
416 			u_char pfx;
417 
418 			switch (gai_afdl[i].a_af) {
419 			case AF_INET:
420 				v4a = ((struct in_addr *)pton)->s_addr;
421 				if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
422 					pai->ai_flags &= ~AI_CANONNAME;
423 				v4a >>= IN_CLASSA_NSHIFT;
424 				if (v4a == 0 || v4a == IN_LOOPBACKNET)
425 					pai->ai_flags &= ~AI_CANONNAME;
426 				break;
427 #if SU_HAVE_IN6
428 			case AF_INET6:
429 				pfx = ((struct in6_addr *)pton)->s6_addr8[0];
430 				if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
431 					pai->ai_flags &= ~AI_CANONNAME;
432 				break;
433 #endif
434 			}
435 
436 			if (pai->ai_family == gai_afdl[i].a_af ||
437 			    pai->ai_family == PF_UNSPEC) {
438 				if (! (pai->ai_flags & AI_CANONNAME)) {
439 					GET_AI(top, &gai_afdl[i], pton, port);
440 					goto good;
441 				}
442 				/*
443 				 * if AI_CANONNAME and if reverse lookup
444 				 * fail, return ai anyway to pacify
445 				 * calling application.
446 				 *
447 				 * XXX getaddrinfo() is a name->address
448 				 * translation function, and it looks strange
449 				 * that we do addr->name translation here.
450 				 */
451 				get_name(pton, &gai_afdl[i], &top, pton, pai, port);
452 				goto good;
453 			} else
454 				ERR(EAI_FAMILY);	/*xxx*/
455 		}
456 	}
457 
458 	if (pai->ai_flags & AI_NUMERICHOST)
459 		ERR(EAI_NONAME);
460 
461 	/* hostname as alphabetical name */
462 	error = get_addr(hostname, pai->ai_family, &top, pai, port);
463 	if (error == 0) {
464 		if (top) {
465  good:
466 			*res = top;
467 			return SUCCESS;
468 		} else
469 			error = EAI_FAIL;
470 	}
471  free:
472 	if (top)
473 		freeaddrinfo(top);
474  bad:
475 	*res = NULL;
476 	return error;
477 }
478 
479 static int
get_name(addr,gai_afd,res,numaddr,pai,port0)480 get_name(addr, gai_afd, res, numaddr, pai, port0)
481 	const char *addr;
482 	struct gai_afd *gai_afd;
483 	struct addrinfo **res;
484 	char *numaddr;
485 	struct addrinfo *pai;
486 	int port0;
487 {
488 	u_short port = port0 & 0xffff;
489 	struct hostent *hp;
490 	struct addrinfo *cur;
491 	int error = 0, h_error;
492 
493 #if SU_HAVE_IN6
494 	hp = getipnodebyaddr(addr, gai_afd->a_addrlen, gai_afd->a_af, &h_error);
495 #else
496 	hp = gethostbyaddr(addr, gai_afd->a_addrlen, AF_INET);
497 #endif
498 	if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
499 		GET_AI(cur, gai_afd, hp->h_addr_list[0], port);
500 		GET_CANONNAME(cur, hp->h_name);
501 	} else
502 		GET_AI(cur, gai_afd, numaddr, port);
503 
504 #if SU_HAVE_IN6
505 	if (hp)
506 		freehostent(hp);
507 #endif
508 	*res = cur;
509 	return SUCCESS;
510  free:
511 	if (cur)
512 		freeaddrinfo(cur);
513 #if SU_HAVE_IN6
514 	if (hp)
515 		freehostent(hp);
516 #endif
517  /* bad: */
518 	*res = NULL;
519 	return error;
520 }
521 
522 static int
get_addr(hostname,af,res,pai,port0)523 get_addr(hostname, af, res, pai, port0)
524 	const char *hostname;
525 	int af;
526 	struct addrinfo **res;
527 	struct addrinfo *pai;
528 	int port0;
529 {
530 	u_short port = port0 & 0xffff;
531 	struct addrinfo sentinel;
532 	struct hostent *hp;
533 	struct addrinfo *top, *cur;
534 	struct gai_afd *gai_afd;
535 	int i, error = 0, h_error;
536 	char *ap;
537 
538 	top = NULL;
539 	sentinel.ai_next = NULL;
540 	cur = &sentinel;
541 #if SU_HAVE_IN6
542 	if (af == AF_UNSPEC) {
543 		hp = getipnodebyname(hostname, AF_INET6,
544 				AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error);
545 	} else
546 		hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error);
547 #else
548 	hp = gethostbyname(hostname);
549 	h_error = h_errno;
550 #endif
551 	if (hp == NULL) {
552 		switch (h_error) {
553 		case HOST_NOT_FOUND:
554 		case NO_DATA:
555 			error = EAI_NODATA;
556 			break;
557 		case TRY_AGAIN:
558 			error = EAI_AGAIN;
559 			break;
560 		case NO_RECOVERY:
561 		default:
562 			error = EAI_FAIL;
563 			break;
564 		}
565 		goto bad;
566 	}
567 
568 	if ((hp->h_name == NULL) || (hp->h_name[0] == 0) ||
569 	    (hp->h_addr_list[0] == NULL))
570 		ERR(EAI_FAIL);
571 
572 	for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) {
573 		switch (af) {
574 #if SU_HAVE_IN6
575 		case AF_INET6:
576 			gai_afd = &gai_afdl[N_INET6];
577 			break;
578 #endif
579 #ifndef INET6
580 		default:	/* AF_UNSPEC */
581 #endif
582 		case AF_INET:
583 			gai_afd = &gai_afdl[N_INET];
584 			break;
585 #if SU_HAVE_IN6
586 		default:	/* AF_UNSPEC */
587 			if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) {
588 				ap += sizeof(struct in6_addr) -
589 					sizeof(struct in_addr);
590 				gai_afd = &gai_afdl[N_INET];
591 			} else
592 				gai_afd = &gai_afdl[N_INET6];
593 			break;
594 #endif
595 		}
596 #ifdef FAITH
597 		if (translate && gai_afd->a_af == AF_INET) {
598 			struct in6_addr *in6;
599 
600 			GET_AI(cur->ai_next, &gai_afdl[N_INET6], ap, port);
601 			in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr;
602 			memcpy(&in6->s6_addr32[0], &faith_prefix,
603 			    sizeof(struct in6_addr) - sizeof(struct in_addr));
604 			memcpy(&in6->s6_addr32[3], ap, sizeof(struct in_addr));
605 		} else
606 #endif /* FAITH */
607 		GET_AI(cur->ai_next, gai_afd, ap, port);
608 		if (cur == &sentinel) {
609 			top = cur->ai_next;
610 			GET_CANONNAME(top, hp->h_name);
611 		}
612 		cur = cur->ai_next;
613 	}
614 #if SU_HAVE_IN6
615 	freehostent(hp);
616 #endif
617 	*res = top;
618 	return SUCCESS;
619  free:
620 	if (top)
621 		freeaddrinfo(top);
622 #if SU_HAVE_IN6
623 	if (hp)
624 		freehostent(hp);
625 #endif
626  bad:
627 	*res = NULL;
628 	return error;
629 }
630 
631 /*
632  * Issues to be discussed:
633  * - Thread safe-ness must be checked
634  * - Return values.  There seems to be no standard for return value (RFC2133)
635  *   but INRIA implementation returns EAI_xxx defined for getaddrinfo().
636  */
637 
638 #define SUCCESS 0
639 #define YES 1
640 #define NO  0
641 
642 static struct gni_afd {
643 	int a_af;
644 	int a_addrlen;
645 	int a_socklen;
646 	int a_off;
647 } gni_afdl [] = {
648 #if SU_HAVE_IN6
649 	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
650 		offsetof(struct sockaddr_in6, sin6_addr)},
651 #endif
652 	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
653 		offsetof(struct sockaddr_in, sin_addr)},
654 	{0, 0, 0},
655 };
656 
657 struct gni_sockinet {
658 	u_char	si_len;
659 	u_char	si_family;
660 	u_short	si_port;
661 };
662 
663 #define ENI_NOSOCKET 	EAI_FAIL
664 #define ENI_NOSERVNAME	EAI_NONAME
665 #define ENI_NOHOSTNAME	EAI_NONAME
666 #define ENI_MEMORY	EAI_MEMORY
667 #define ENI_SYSTEM	EAI_SYSTEM
668 #define ENI_FAMILY	EAI_FAMILY
669 #define ENI_SALEN	EAI_MEMORY
670 
671 static
672 int
getnameinfo(sa,salen,host,hostlen,serv,servlen,flags)673 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
674 	const struct sockaddr *sa;
675 	size_t salen;
676 	char *host;
677 	size_t hostlen;
678 	char *serv;
679 	size_t servlen;
680 	int flags;
681 {
682 	struct gni_afd *gni_afd;
683 	struct servent *sp;
684 	struct hostent *hp;
685 	u_short port;
686 	int family, len, i;
687 	char *addr, *p;
688 	u_long v4a;
689 	u_char pfx;
690 	int h_error;
691 	char numserv[512];
692 	char numaddr[512];
693 
694 	if (sa == NULL)
695 		return ENI_NOSOCKET;
696 
697 #if SU_HAVE_SOCKADDR_SA_LEN
698 	len = sa->sa_len;
699 	if (len != salen) return ENI_SALEN;
700 #else
701 	len = salen;
702 #endif
703 
704 	family = sa->sa_family;
705 	for (i = 0; gni_afdl[i].a_af; i++)
706 		if (gni_afdl[i].a_af == family) {
707 			gni_afd = &gni_afdl[i];
708 			goto found;
709 		}
710 	return ENI_FAMILY;
711 
712  found:
713 	if (len != gni_afd->a_socklen) return ENI_SALEN;
714 
715 	port = ((struct gni_sockinet *)sa)->si_port; /* network byte order */
716 	addr = (char *)sa + gni_afd->a_off;
717 
718 	if (serv == NULL || servlen == 0) {
719 		/* what we should do? */
720 	} else if (flags & NI_NUMERICSERV) {
721 		snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
722 		if (strlen(numserv) > servlen)
723 			return ENI_MEMORY;
724 		strcpy(serv, numserv);
725 	} else {
726 		sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp");
727 		if (sp) {
728 			if (strlen(sp->s_name) > servlen)
729 				return ENI_MEMORY;
730 			strcpy(serv, sp->s_name);
731 		} else
732 			return ENI_NOSERVNAME;
733 	}
734 
735 	switch (sa->sa_family) {
736 	case AF_INET:
737 		v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
738 		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
739 			flags |= NI_NUMERICHOST;
740 		v4a >>= IN_CLASSA_NSHIFT;
741 		if (v4a == 0 || v4a == IN_LOOPBACKNET)
742 			flags |= NI_NUMERICHOST;
743 		break;
744 #if SU_HAVE_IN6
745 	case AF_INET6:
746 		pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr8[0];
747 		if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
748 			flags |= NI_NUMERICHOST;
749 		break;
750 #endif
751 	}
752 	if (host == NULL || hostlen == 0) {
753 		/* what should we do? */
754 	} else if (flags & NI_NUMERICHOST) {
755 		if (su_inet_ntop(gni_afd->a_af, addr, numaddr, sizeof(numaddr))
756 		    == NULL)
757 			return ENI_SYSTEM;
758 		if (strlen(numaddr) > hostlen)
759 			return ENI_MEMORY;
760 		strcpy(host, numaddr);
761 	} else {
762 #if SU_HAVE_IN6
763 		hp = getipnodebyaddr(addr, gni_afd->a_addrlen, gni_afd->a_af, &h_error);
764 #else
765 		hp = gethostbyaddr(addr, gni_afd->a_addrlen, gni_afd->a_af);
766 		h_error = h_errno;
767 #endif
768 
769 		if (hp) {
770 			if (flags & NI_NOFQDN) {
771 				p = strchr(hp->h_name, '.');
772 				if (p) *p = '\0';
773 			}
774 			if (strlen(hp->h_name) > hostlen) {
775 #if SU_HAVE_IN6
776 				freehostent(hp);
777 #endif
778 				return ENI_MEMORY;
779 			}
780 			strcpy(host, hp->h_name);
781 #if SU_HAVE_IN6
782 			freehostent(hp);
783 #endif
784 		} else {
785 			if (flags & NI_NAMEREQD)
786 				return ENI_NOHOSTNAME;
787 			if (su_inet_ntop(gni_afd->a_af, addr,
788 					 numaddr, sizeof(numaddr))
789 			    == NULL)
790 				return ENI_NOHOSTNAME;
791 			if (strlen(numaddr) > hostlen)
792 				return ENI_MEMORY;
793 			strcpy(host, numaddr);
794 		}
795 	}
796 	return SUCCESS;
797 }
798 
799 #endif	/* !HAVE_GETNAMEINFO */
800 
801 #if !HAVE_FREEADDRINFO
802 static
803 void
freeaddrinfo(ai)804 freeaddrinfo(ai)
805 	struct addrinfo *ai;
806 {
807 	struct addrinfo *next;
808 
809 	if (ai == NULL)
810 		return;
811 
812 	do {
813 		next = ai->ai_next;
814 		if (ai->ai_canonname)
815 			free(ai->ai_canonname);
816 		/* no need to free(ai->ai_addr) */
817 		free(ai);
818 	} while ((ai = next) != NULL);
819 }
820 #endif
821 
822 #if !HAVE_GAI_STRERROR
823 static
824 char *
gai_strerror(ecode)825 gai_strerror(ecode)
826 	int ecode;
827 {
828   switch (ecode) {
829   case 0:
830     return "success.";
831 #if defined(EAI_ADDRFAMILY)
832   case EAI_ADDRFAMILY:
833     return "address family for hostname not supported.";
834 #endif
835 #if defined(EAI_AGAIN)
836   case EAI_AGAIN:
837     return "temporary failure in name resolution.";
838 #endif
839 #if defined(EAI_BADFLAGS)
840   case EAI_BADFLAGS:
841     return "invalid value for ai_flags.";
842 #endif
843 #if defined(EAI_FAIL)
844   case EAI_FAIL:
845     return "non-recoverable failure in name resolution.";
846 #endif
847 #if defined(EAI_FAMILY)
848   case EAI_FAMILY:
849     return "ai_family not supported.";
850 #endif
851 #if defined(EAI_MEMORY)
852   case EAI_MEMORY:
853     return "memory allocation failure.";
854 #endif
855 #if defined(EAI_NODATA)
856   case EAI_NODATA:
857     return "no address associated with hostname.";
858 #endif
859 #if defined(EAI_NONAME)
860   case EAI_NONAME:
861     return "hostname nor servname provided, or not known.";
862 #endif
863 #if defined(EAI_SERVICE)
864   case EAI_SERVICE:
865     return "servname not supported for ai_socktype.";
866 #endif
867 #if defined(EAI_SOCKTYPE)
868   case EAI_SOCKTYPE:
869     return "ai_socktype not supported.";
870 #endif
871 #if defined(EAI_SYSTEM)
872   case EAI_SYSTEM:
873     return "system error returned in errno.";
874 #endif
875 #if defined(EAI_BADHINTS)
876   case EAI_BADHINTS:
877     return "invalid value for hints.";
878 #endif
879 #if defined(EAI_PROTOCOL)
880   case EAI_PROTOCOL:
881     return "resolved protocol is unknown.";
882 #endif
883   default:
884     return "unknown error.";
885   }
886 }
887 #endif
888 
889 
890 /** Translate address and service.
891  *
892  * This is a getaddrinfo() supporting SCTP and other exotic protocols.
893  */
su_getaddrinfo(char const * node,char const * service,su_addrinfo_t const * hints,su_addrinfo_t ** res)894 int su_getaddrinfo(char const *node, char const *service,
895 		   su_addrinfo_t const *hints,
896 		   su_addrinfo_t **res)
897 {
898   int retval;
899   su_addrinfo_t *ai;
900   char const *realservice = service;
901 
902   if (!service || service[0] == '\0')
903     service = "0";
904 
905 #if HAVE_SCTP
906   if (res && hints && hints->ai_protocol == IPPROTO_SCTP) {
907     su_addrinfo_t system_hints[1];
908     int socktype;
909 
910     socktype = hints->ai_socktype;
911 
912     if (!(socktype == 0 ||
913 	  socktype == SOCK_SEQPACKET ||
914 	  socktype == SOCK_STREAM ||
915 	  socktype == SOCK_DGRAM))
916       return EAI_SOCKTYPE;
917 
918     *system_hints = *hints;
919     system_hints->ai_protocol = IPPROTO_TCP;
920     system_hints->ai_socktype = SOCK_STREAM;
921 
922     retval = getaddrinfo(node, service, system_hints, res);
923     if (retval)
924       return retval;
925 
926     if (socktype == 0)
927       socktype = SOCK_STREAM;
928 
929     for (ai = *res; ai; ai = ai->ai_next) {
930       ai->ai_protocol = IPPROTO_SCTP;
931       ai->ai_socktype = socktype;
932     }
933 
934     return 0;
935   }
936 #endif
937 
938   retval = getaddrinfo(node, service, hints, res);
939 
940   if (service != realservice && retval == EAI_SERVICE)
941     retval = getaddrinfo(node, realservice, hints, res);
942 
943   if (retval == 0) {
944     for (ai = *res; ai; ai = ai->ai_next) {
945       if (ai->ai_protocol)
946 	continue;
947 
948       if (hints && hints->ai_protocol) {
949 	ai->ai_protocol = hints->ai_protocol;
950 	continue;
951       }
952 
953       if (ai->ai_family != AF_INET
954 #if SU_HAVE_IN6
955 	  && ai->ai_family != AF_INET6
956 #endif
957 	  ) continue;
958 
959       if (ai->ai_socktype == SOCK_STREAM)
960 	ai->ai_protocol = IPPROTO_TCP;
961       else if (ai->ai_socktype == SOCK_DGRAM)
962 	ai->ai_protocol = IPPROTO_UDP;
963     }
964   }
965   return retval;
966 }
967 
968 /** Free su_addrinfo_t structure allocated by su_getaddrinfo(). */
su_freeaddrinfo(su_addrinfo_t * res)969 void su_freeaddrinfo(su_addrinfo_t *res)
970 {
971   freeaddrinfo(res);
972 }
973 
974 /** Return string describing address translation error. */
su_gai_strerror(int errcode)975 char const *su_gai_strerror(int errcode)
976 {
977 	return (char const *)gai_strerror(errcode);
978 }
979 
980 /** Resolve socket address into hostname and service name.
981  *
982  * @note
983  * This function uses now @RFC2133 prototype. The @RFC3493 redefines the
984  * prototype as well as ai_addrlen to use socklen_t instead of size_t.
985  * If your application allocates more than 2 gigabytes for resolving the
986  * hostname, you probably lose.
987  */
988 int
su_getnameinfo(const su_sockaddr_t * su,size_t sulen,char * return_host,size_t hostlen,char * return_serv,size_t servlen,int flags)989 su_getnameinfo(const su_sockaddr_t *su, size_t sulen,
990 	       char *return_host, size_t hostlen,
991 	       char *return_serv, size_t servlen,
992 	       int flags)
993 {
994   return getnameinfo(&su->su_sa, (socklen_t)sulen,
995 		     return_host, (socklen_t)hostlen,
996 		     return_serv, (socklen_t)servlen,
997 		     flags);
998 }
999