1 /* $NetBSD: myaddrinfo.c,v 1.3 2022/10/08 16:12:50 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* myaddrinfo 3
6 /* SUMMARY
7 /* addrinfo encapsulation and emulation
8 /* SYNOPSIS
9 /* #include <myaddrinfo.h>
10 /*
11 /* #define MAI_V4ADDR_BITS ...
12 /* #define MAI_V6ADDR_BITS ...
13 /* #define MAI_V4ADDR_BYTES ...
14 /* #define MAI_V6ADDR_BYTES ...
15 /*
16 /* typedef struct { char buf[....]; } MAI_HOSTNAME_STR;
17 /* typedef struct { char buf[....]; } MAI_HOSTADDR_STR;
18 /* typedef struct { char buf[....]; } MAI_SERVNAME_STR;
19 /* typedef struct { char buf[....]; } MAI_SERVPORT_STR;
20 /*
21 /* int hostname_to_sockaddr(hostname, service, socktype, result)
22 /* const char *hostname;
23 /* const char *service;
24 /* int socktype;
25 /* struct addrinfo **result;
26 /*
27 /* int hostname_to_sockaddr_pf(hostname, pf, service, socktype, result)
28 /* const char *hostname;
29 /* int pf;
30 /* const char *service;
31 /* int socktype;
32 /* struct addrinfo **result;
33 /*
34 /* int hostaddr_to_sockaddr(hostaddr, service, socktype, result)
35 /* const char *hostaddr;
36 /* const char *service;
37 /* int socktype;
38 /* struct addrinfo **result;
39 /*
40 /* int sockaddr_to_hostaddr(sa, salen, hostaddr, portnum, socktype)
41 /* const struct sockaddr *sa;
42 /* SOCKADDR_SIZE salen;
43 /* MAI_HOSTADDR_STR *hostaddr;
44 /* MAI_SERVPORT_STR *portnum;
45 /* int socktype;
46 /*
47 /* int sockaddr_to_hostname(sa, salen, hostname, service, socktype)
48 /* const struct sockaddr *sa;
49 /* SOCKADDR_SIZE salen;
50 /* MAI_HOSTNAME_STR *hostname;
51 /* MAI_SERVNAME_STR *service;
52 /* int socktype;
53 /*
54 /* const char *MAI_STRERROR(error)
55 /* int error;
56 /* DESCRIPTION
57 /* This module provides a simplified user interface to the
58 /* getaddrinfo(3) and getnameinfo(3) routines (which provide
59 /* a unified interface to manipulate IPv4 and IPv6 socket
60 /* address structures).
61 /*
62 /* On systems without getaddrinfo(3) and getnameinfo(3) support,
63 /* emulation for IPv4 only can be enabled by defining
64 /* EMULATE_IPV4_ADDRINFO.
65 /*
66 /* hostname_to_sockaddr() looks up the binary addresses for
67 /* the specified symbolic hostname or numeric address. The
68 /* result should be destroyed with freeaddrinfo(). A null host
69 /* pointer converts to the null host address.
70 /*
71 /* hostname_to_sockaddr_pf() is an extended interface that
72 /* provides a protocol family override.
73 /*
74 /* hostaddr_to_sockaddr() converts a printable network address
75 /* into the corresponding binary form. The result should be
76 /* destroyed with freeaddrinfo(). A null host pointer converts
77 /* to the null host address.
78 /*
79 /* sockaddr_to_hostaddr() converts a binary network address
80 /* into printable form. The result buffers should be large
81 /* enough to hold the printable address or port including the
82 /* null terminator.
83 /* This function strips off the IPv6 datalink suffix.
84 /*
85 /* sockaddr_to_hostname() converts a binary network address
86 /* into a hostname or service. The result buffer should be
87 /* large enough to hold the hostname or service including the
88 /* null terminator. This routine rejects malformed hostnames
89 /* or numeric hostnames and pretends that the lookup failed.
90 /*
91 /* MAI_STRERROR() is an unsafe macro (it evaluates the argument
92 /* multiple times) that invokes strerror() or gai_strerror()
93 /* as appropriate.
94 /*
95 /* This module exports the following constants that should be
96 /* user for storage allocation of name or address information:
97 /* .IP MAI_V4ADDR_BITS
98 /* .IP MAI_V6ADDR_BITS
99 /* .IP MAI_V4ADDR_BYTES
100 /* .IP MAI_V6ADDR_BYTES
101 /* The number of bits or bytes needed to store a binary
102 /* IPv4 or IPv6 network address.
103 /* .PP
104 /* The types MAI_HOST{NAME,ADDR}_STR and MAI_SERV{NAME,PORT}_STR
105 /* implement buffers for the storage of the string representations
106 /* of symbolic or numerical hosts or services. Do not use
107 /* buffer types other than the ones that are expected here,
108 /* or things will blow up with buffer overflow problems.
109 /*
110 /* Arguments:
111 /* .IP hostname
112 /* On input to hostname_to_sockaddr(), a numeric or symbolic
113 /* hostname, or a null pointer (meaning the wild-card listen
114 /* address). On output from sockaddr_to_hostname(), storage
115 /* for the result hostname, or a null pointer.
116 /* .IP pf
117 /* Protocol type: PF_UNSPEC (meaning: use any protocol that is
118 /* available), PF_INET, or PF_INET6. This argument is ignored
119 /* in EMULATE_IPV4_ADDRINFO mode.
120 /* .IP hostaddr
121 /* On input to hostaddr_to_sockaddr(), a numeric hostname,
122 /* or a null pointer (meaning the wild-card listen address).
123 /* On output from sockaddr_to_hostaddr(), storage for the
124 /* result hostaddress, or a null pointer.
125 /* .IP service
126 /* On input to hostname/addr_to_sockaddr(), a numeric or
127 /* symbolic service name, or a null pointer in which case the
128 /* socktype argument is ignored. On output from
129 /* sockaddr_to_hostname/addr(), storage for the result service
130 /* name, or a null pointer.
131 /* .IP portnum
132 /* Storage for the result service port number, or a null pointer.
133 /* .IP socktype
134 /* Socket type: SOCK_STREAM, SOCK_DGRAM, etc. This argument is
135 /* ignored when no service or port are specified.
136 /* .IP sa
137 /* Protocol-independent socket address structure.
138 /* .IP salen
139 /* Protocol-dependent socket address structure size in bytes.
140 /* SEE ALSO
141 /* getaddrinfo(3), getnameinfo(3), freeaddrinfo(3), gai_strerror(3)
142 /* DIAGNOSTICS
143 /* All routines either return 0 upon success, or an error code
144 /* that is compatible with gai_strerror().
145 /*
146 /* On systems where addrinfo support is emulated by Postfix,
147 /* some out-of-memory errors are not reported to the caller,
148 /* but are handled by mymalloc().
149 /* BUGS
150 /* The IPv4-only emulation code does not support requests that
151 /* specify a service but no socket type. It returns an error
152 /* indication, instead of enumerating all the possible answers.
153 /*
154 /* The hostname/addr_to_sockaddr() routines should accept a
155 /* list of address families that the caller is interested in,
156 /* and they should return only information of those types.
157 /*
158 /* Unfortunately, it is not possible to remove unwanted address
159 /* family results from hostname_to_sockaddr(), because we
160 /* don't know how the system library routine getaddrinfo()
161 /* allocates memory. For example, getaddrinfo() could save
162 /* space by referencing the same string object from multiple
163 /* addrinfo structures; or it could allocate a string object
164 /* and the addrinfo structure as one memory block.
165 /*
166 /* We could get around this by copying getaddrinfo() results
167 /* to our own private data structures, but that would only
168 /* make an already expensive API even more expensive.
169 /*
170 /* A better workaround is to return a vector of addrinfo
171 /* pointers to the elements that contain only the elements
172 /* that the caller is interested in. The pointer to the
173 /* original getaddrinfo() result can be hidden at the end
174 /* after the null terminator, or before the first element.
175 /* LICENSE
176 /* .ad
177 /* .fi
178 /* The Secure Mailer license must be distributed with this software.
179 /* AUTHOR(S)
180 /* Wietse Venema
181 /* IBM T.J. Watson Research
182 /* P.O. Box 704
183 /* Yorktown Heights, NY 10598, USA
184 /*
185 /* Wietse Venema
186 /* Google, Inc.
187 /* 111 8th Avenue
188 /* New York, NY 10011, USA
189 /*--*/
190
191 /* System library. */
192
193 #include <sys_defs.h>
194 #include <sys/types.h>
195 #include <sys/socket.h>
196 #include <netinet/in.h>
197 #include <arpa/inet.h>
198 #include <netdb.h>
199 #include <string.h>
200 #include <errno.h>
201 #include <stdlib.h>
202 #include <stdio.h> /* sprintf() */
203
204 /* Utility library. */
205
206 #include <mymalloc.h>
207 #include <valid_hostname.h>
208 #include <sock_addr.h>
209 #include <stringops.h>
210 #include <msg.h>
211 #include <inet_proto.h>
212 #include <myaddrinfo.h>
213 #include <split_at.h>
214 #include <known_tcp_ports.h>
215
216 /* Application-specific. */
217
218 /*
219 * Use an old trick to save some space: allocate space for two objects in
220 * one. In Postfix we often use this trick for structures that have an array
221 * of things at the end.
222 */
223 struct ipv4addrinfo {
224 struct addrinfo info;
225 struct sockaddr_in sin;
226 };
227
228 /*
229 * When we're not interested in service ports, we must pick a socket type
230 * otherwise getaddrinfo() will give us duplicate results: one set for TCP,
231 * and another set for UDP. For consistency, we'll use the same default
232 * socket type for the results from emulation mode.
233 */
234 #define MAI_SOCKTYPE SOCK_STREAM /* getaddrinfo() query */
235
236 #ifdef EMULATE_IPV4_ADDRINFO
237
238 /* clone_ipv4addrinfo - clone ipv4addrinfo structure */
239
clone_ipv4addrinfo(struct ipv4addrinfo * tp)240 static struct ipv4addrinfo *clone_ipv4addrinfo(struct ipv4addrinfo * tp)
241 {
242 struct ipv4addrinfo *ip;
243
244 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
245 *ip = *tp;
246 ip->info.ai_addr = (struct sockaddr *) &(ip->sin);
247 return (ip);
248 }
249
250 /* init_ipv4addrinfo - initialize an ipv4addrinfo structure */
251
init_ipv4addrinfo(struct ipv4addrinfo * ip,int socktype)252 static void init_ipv4addrinfo(struct ipv4addrinfo * ip, int socktype)
253 {
254
255 /*
256 * Portability: null pointers aren't necessarily all-zero bits, so we
257 * make explicit assignments to all the pointers that we're aware of.
258 */
259 memset((void *) ip, 0, sizeof(*ip));
260 ip->info.ai_family = PF_INET;
261 ip->info.ai_socktype = socktype;
262 ip->info.ai_protocol = 0; /* XXX */
263 ip->info.ai_addrlen = sizeof(ip->sin);
264 ip->info.ai_canonname = 0;
265 ip->info.ai_addr = (struct sockaddr *) &(ip->sin);
266 ip->info.ai_next = 0;
267 ip->sin.sin_family = AF_INET;
268 #ifdef HAS_SA_LEN
269 ip->sin.sin_len = sizeof(ip->sin);
270 #endif
271 }
272
273 /* find_service - translate numeric or symbolic service name */
274
find_service(const char * service,int socktype)275 static int find_service(const char *service, int socktype)
276 {
277 struct servent *sp;
278 const char *proto;
279 unsigned port;
280
281 service = filter_known_tcp_port(service);
282 if (alldig(service)) {
283 port = atoi(service);
284 return (port < 65536 ? htons(port) : -1);
285 }
286 if (socktype == SOCK_STREAM) {
287 proto = "tcp";
288 } else if (socktype == SOCK_DGRAM) {
289 proto = "udp";
290 } else {
291 return (-1);
292 }
293 if ((sp = getservbyname(service, proto)) != 0) {
294 return (sp->s_port);
295 } else {
296 return (-1);
297 }
298 }
299
300 #endif
301
302 /* hostname_to_sockaddr_pf - hostname to binary address form */
303
hostname_to_sockaddr_pf(const char * hostname,int pf,const char * service,int socktype,struct addrinfo ** res)304 int hostname_to_sockaddr_pf(const char *hostname, int pf,
305 const char *service, int socktype,
306 struct addrinfo ** res)
307 {
308 #ifdef EMULATE_IPV4_ADDRINFO
309
310 /*
311 * Emulated getaddrinfo(3) version.
312 */
313 static struct ipv4addrinfo template;
314 struct ipv4addrinfo *ip;
315 struct ipv4addrinfo *prev;
316 struct in_addr addr;
317 struct hostent *hp;
318 char **name_list;
319 int port;
320
321 /*
322 * Validate the service.
323 */
324 if (service) {
325 if ((port = find_service(service, socktype)) < 0)
326 return (EAI_SERVICE);
327 } else {
328 port = 0;
329 socktype = MAI_SOCKTYPE;
330 }
331
332 /*
333 * No host means INADDR_ANY.
334 */
335 if (hostname == 0) {
336 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
337 init_ipv4addrinfo(ip, socktype);
338 ip->sin.sin_addr.s_addr = INADDR_ANY;
339 ip->sin.sin_port = port;
340 *res = &(ip->info);
341 return (0);
342 }
343
344 /*
345 * Numeric host.
346 */
347 if (inet_pton(AF_INET, hostname, (void *) &addr) == 1) {
348 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
349 init_ipv4addrinfo(ip, socktype);
350 ip->sin.sin_addr = addr;
351 ip->sin.sin_port = port;
352 *res = &(ip->info);
353 return (0);
354 }
355
356 /*
357 * Look up the IPv4 address list.
358 */
359 if ((hp = gethostbyname(hostname)) == 0)
360 return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NODATA);
361 if (hp->h_addrtype != AF_INET
362 || hp->h_length != sizeof(template.sin.sin_addr))
363 return (EAI_NODATA);
364
365 /*
366 * Initialize the result template.
367 */
368 if (template.info.ai_addrlen == 0)
369 init_ipv4addrinfo(&template, socktype);
370
371 /*
372 * Copy the address information into an addrinfo structure.
373 */
374 prev = &template;
375 for (name_list = hp->h_addr_list; name_list[0]; name_list++) {
376 ip = clone_ipv4addrinfo(prev);
377 ip->sin.sin_addr = IN_ADDR(name_list[0]);
378 ip->sin.sin_port = port;
379 if (prev == &template)
380 *res = &(ip->info);
381 else
382 prev->info.ai_next = &(ip->info);
383 prev = ip;
384 }
385 return (0);
386 #else
387
388 /*
389 * Native getaddrinfo(3) version.
390 *
391 * XXX Wild-card listener issues.
392 *
393 * With most IPv4 plus IPv6 systems, an IPv6 wild-card listener also listens
394 * on the IPv4 wild-card address. Connections from IPv4 clients appear as
395 * IPv4-in-IPv6 addresses; when Postfix support for IPv4 is turned on,
396 * Postfix automatically maps these embedded addresses to their original
397 * IPv4 form. So everything seems to be fine.
398 *
399 * However, some applications prefer to use separate listener sockets for
400 * IPv4 and IPv6. The Postfix IPv6 patch provided such an example. And
401 * this is where things become tricky. On many systems the IPv6 and IPv4
402 * wild-card listeners cannot coexist. When one is already active, the
403 * other fails with EADDRINUSE. Solaris 9, however, will automagically
404 * "do the right thing" and allow both listeners to coexist.
405 *
406 * Recent systems have the IPV6_V6ONLY feature (RFC 3493), which tells the
407 * system that we really mean IPv6 when we say IPv6. This allows us to
408 * set up separate wild-card listener sockets for IPv4 and IPv6. So
409 * everything seems to be fine again.
410 *
411 * The following workaround disables the wild-card IPv4 listener when
412 * IPV6_V6ONLY is unavailable. This is necessary for some Linux versions,
413 * but is not needed for Solaris 9 (which allows IPv4 and IPv6 wild-card
414 * listeners to coexist). Solaris 10 beta already has IPV6_V6ONLY.
415 *
416 * XXX This workaround obviously breaks if we want to support protocols in
417 * addition to IPv6 and IPv4, but it is needed only until IPv6
418 * implementations catch up with RFC 3493. A nicer fix is to filter the
419 * getaddrinfo() result, and to return a vector of addrinfo pointers to
420 * only those types of elements that the caller has expressed interested
421 * in.
422 *
423 * XXX Vanilla AIX 5.1 getaddrinfo() does not support a null hostname with
424 * AI_PASSIVE. And since we don't know how getaddrinfo() manages its
425 * memory we can't bypass it for this special case, or freeaddrinfo()
426 * might blow up. Instead we turn off IPV6_V6ONLY in inet_listen(), and
427 * supply a protocol-dependent hard-coded string value to getaddrinfo()
428 * below, so that it will convert into the appropriate wild-card address.
429 *
430 * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null
431 * service argument is specified.
432 */
433 struct addrinfo hints;
434 int err;
435
436 memset((void *) &hints, 0, sizeof(hints));
437 hints.ai_family = (pf != PF_UNSPEC) ? pf : inet_proto_info()->ai_family;
438 hints.ai_socktype = service ? socktype : MAI_SOCKTYPE;
439 if (!hostname) {
440 hints.ai_flags = AI_PASSIVE;
441 #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST)
442 switch (hints.ai_family) {
443 case PF_UNSPEC:
444 hints.ai_family = PF_INET6;
445 #ifdef BROKEN_AI_PASSIVE_NULL_HOST
446 case PF_INET6:
447 hostname = "::";
448 break;
449 case PF_INET:
450 hostname = "0.0.0.0";
451 break;
452 #endif
453 }
454 #endif
455 }
456 if (service) {
457 service = filter_known_tcp_port(service);
458 if (alldig(service))
459 hints.ai_flags |= AI_NUMERICSERV;
460 }
461 err = getaddrinfo(hostname, service, &hints, res);
462 #if defined(BROKEN_AI_NULL_SERVICE)
463 if (service == 0 && err == 0) {
464 struct addrinfo *r;
465 unsigned short *portp;
466
467 for (r = *res; r != 0; r = r->ai_next)
468 if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0)
469 *portp = 0;
470 }
471 #endif
472 return (err);
473 #endif
474 }
475
476 /* hostaddr_to_sockaddr - printable address to binary address form */
477
hostaddr_to_sockaddr(const char * hostaddr,const char * service,int socktype,struct addrinfo ** res)478 int hostaddr_to_sockaddr(const char *hostaddr, const char *service,
479 int socktype, struct addrinfo ** res)
480 {
481 #ifdef EMULATE_IPV4_ADDRINFO
482
483 /*
484 * Emulated getaddrinfo(3) version.
485 */
486 struct ipv4addrinfo *ip;
487 struct in_addr addr;
488 int port;
489
490 /*
491 * Validate the service.
492 */
493 if (service) {
494 if ((port = find_service(service, socktype)) < 0)
495 return (EAI_SERVICE);
496 } else {
497 port = 0;
498 socktype = MAI_SOCKTYPE;
499 }
500
501 /*
502 * No host means INADDR_ANY.
503 */
504 if (hostaddr == 0) {
505 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
506 init_ipv4addrinfo(ip, socktype);
507 ip->sin.sin_addr.s_addr = INADDR_ANY;
508 ip->sin.sin_port = port;
509 *res = &(ip->info);
510 return (0);
511 }
512
513 /*
514 * Deal with bad address forms.
515 */
516 switch (inet_pton(AF_INET, hostaddr, (void *) &addr)) {
517 case 1: /* Success */
518 break;
519 default: /* Unparsable */
520 return (EAI_NONAME);
521 case -1: /* See errno */
522 return (EAI_SYSTEM);
523 }
524
525 /*
526 * Initialize the result structure.
527 */
528 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
529 init_ipv4addrinfo(ip, socktype);
530
531 /*
532 * And copy the result.
533 */
534 ip->sin.sin_addr = addr;
535 ip->sin.sin_port = port;
536 *res = &(ip->info);
537
538 return (0);
539 #else
540
541 /*
542 * Native getaddrinfo(3) version. See comments in hostname_to_sockaddr().
543 *
544 * XXX Vanilla AIX 5.1 getaddrinfo() returns multiple results when
545 * converting a printable ipv4 or ipv6 address to socket address with
546 * ai_family=PF_UNSPEC, ai_flags=AI_NUMERICHOST, ai_socktype=SOCK_STREAM,
547 * ai_protocol=0 or IPPROTO_TCP, and service=0. The workaround is to
548 * ignore all but the first result.
549 *
550 * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null
551 * service argument is specified.
552 */
553 struct addrinfo hints;
554 int err;
555
556 memset(&hints, 0, sizeof(hints));
557 hints.ai_family = inet_proto_info()->ai_family;
558 hints.ai_socktype = service ? socktype : MAI_SOCKTYPE;
559 hints.ai_flags = AI_NUMERICHOST;
560 if (!hostaddr) {
561 hints.ai_flags |= AI_PASSIVE;
562 #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST)
563 switch (hints.ai_family) {
564 case PF_UNSPEC:
565 hints.ai_family = PF_INET6;
566 #ifdef BROKEN_AI_PASSIVE_NULL_HOST
567 case PF_INET6:
568 hostaddr = "::";
569 break;
570 case PF_INET:
571 hostaddr = "0.0.0.0";
572 break;
573 #endif
574 }
575 #endif
576 }
577 if (service) {
578 service = filter_known_tcp_port(service);
579 if (alldig(service))
580 hints.ai_flags |= AI_NUMERICSERV;
581 }
582 err = getaddrinfo(hostaddr, service, &hints, res);
583 #if defined(BROKEN_AI_NULL_SERVICE)
584 if (service == 0 && err == 0) {
585 struct addrinfo *r;
586 unsigned short *portp;
587
588 for (r = *res; r != 0; r = r->ai_next)
589 if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0)
590 *portp = 0;
591 }
592 #endif
593 return (err);
594 #endif
595 }
596
597 /* sockaddr_to_hostaddr - binary address to printable address form */
598
sockaddr_to_hostaddr(const struct sockaddr * sa,SOCKADDR_SIZE salen,MAI_HOSTADDR_STR * hostaddr,MAI_SERVPORT_STR * portnum,int unused_socktype)599 int sockaddr_to_hostaddr(const struct sockaddr *sa, SOCKADDR_SIZE salen,
600 MAI_HOSTADDR_STR *hostaddr,
601 MAI_SERVPORT_STR *portnum,
602 int unused_socktype)
603 {
604 #ifdef EMULATE_IPV4_ADDRINFO
605 char portbuf[sizeof("65535")];
606 ssize_t len;
607
608 /*
609 * Emulated getnameinfo(3) version. The buffer length includes the space
610 * for the null terminator.
611 */
612 if (sa->sa_family != AF_INET) {
613 errno = EAFNOSUPPORT;
614 return (EAI_SYSTEM);
615 }
616 if (hostaddr != 0) {
617 if (inet_ntop(AF_INET, (void *) &(SOCK_ADDR_IN_ADDR(sa)),
618 hostaddr->buf, sizeof(hostaddr->buf)) == 0)
619 return (EAI_SYSTEM);
620 }
621 if (portnum != 0) {
622 sprintf(portbuf, "%d", ntohs(SOCK_ADDR_IN_PORT(sa)) & 0xffff);
623 if ((len = strlen(portbuf)) >= sizeof(portnum->buf)) {
624 errno = ENOSPC;
625 return (EAI_SYSTEM);
626 }
627 memcpy(portnum->buf, portbuf, len + 1);
628 }
629 return (0);
630 #else
631 int ret;
632
633 /*
634 * Native getnameinfo(3) version.
635 */
636 ret = getnameinfo(sa, salen,
637 hostaddr ? hostaddr->buf : (char *) 0,
638 hostaddr ? sizeof(hostaddr->buf) : 0,
639 portnum ? portnum->buf : (char *) 0,
640 portnum ? sizeof(portnum->buf) : 0,
641 NI_NUMERICHOST | NI_NUMERICSERV);
642 if (hostaddr != 0 && ret == 0 && sa->sa_family == AF_INET6)
643 (void) split_at(hostaddr->buf, '%');
644 return (ret);
645 #endif
646 }
647
648 /* sockaddr_to_hostname - binary address to printable hostname */
649
sockaddr_to_hostname(const struct sockaddr * sa,SOCKADDR_SIZE salen,MAI_HOSTNAME_STR * hostname,MAI_SERVNAME_STR * service,int socktype)650 int sockaddr_to_hostname(const struct sockaddr *sa, SOCKADDR_SIZE salen,
651 MAI_HOSTNAME_STR *hostname,
652 MAI_SERVNAME_STR *service,
653 int socktype)
654 {
655 #ifdef EMULATE_IPV4_ADDRINFO
656
657 /*
658 * Emulated getnameinfo(3) version.
659 */
660 struct hostent *hp;
661 struct servent *sp;
662 size_t len;
663
664 /*
665 * Sanity check.
666 */
667 if (sa->sa_family != AF_INET)
668 return (EAI_NODATA);
669
670 /*
671 * Look up the host name.
672 */
673 if (hostname != 0) {
674 if ((hp = gethostbyaddr((char *) &(SOCK_ADDR_IN_ADDR(sa)),
675 sizeof(SOCK_ADDR_IN_ADDR(sa)),
676 AF_INET)) == 0)
677 return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NONAME);
678
679 /*
680 * Save the result. The buffer length includes the space for the null
681 * terminator. Hostname sanity checks are at the end of this
682 * function.
683 */
684 if ((len = strlen(hp->h_name)) >= sizeof(hostname->buf)) {
685 errno = ENOSPC;
686 return (EAI_SYSTEM);
687 }
688 memcpy(hostname->buf, hp->h_name, len + 1);
689 }
690
691 /*
692 * Look up the service.
693 */
694 if (service != 0) {
695 if ((sp = getservbyport(ntohs(SOCK_ADDR_IN_PORT(sa)),
696 socktype == SOCK_DGRAM ? "udp" : "tcp")) == 0)
697 return (EAI_NONAME);
698
699 /*
700 * Save the result. The buffer length includes the space for the null
701 * terminator.
702 */
703 if ((len = strlen(sp->s_name)) >= sizeof(service->buf)) {
704 errno = ENOSPC;
705 return (EAI_SYSTEM);
706 }
707 memcpy(service->buf, sp->s_name, len + 1);
708 }
709 #else
710
711 /*
712 * Native getnameinfo(3) version.
713 */
714 int err;
715
716 err = getnameinfo(sa, salen,
717 hostname ? hostname->buf : (char *) 0,
718 hostname ? sizeof(hostname->buf) : 0,
719 service ? service->buf : (char *) 0,
720 service ? sizeof(service->buf) : 0,
721 socktype == SOCK_DGRAM ?
722 NI_NAMEREQD | NI_DGRAM : NI_NAMEREQD);
723 if (err != 0)
724 return (err);
725 #endif
726
727 /*
728 * Hostname sanity checks.
729 */
730 if (hostname != 0) {
731 if (valid_hostaddr(hostname->buf, DONT_GRIPE)) {
732 msg_warn("numeric hostname: %s", hostname->buf);
733 return (EAI_NONAME);
734 }
735 if (!valid_hostname(hostname->buf, DO_GRIPE))
736 return (EAI_NONAME);
737 }
738 return (0);
739 }
740
741 /* myaddrinfo_control - fine control */
742
myaddrinfo_control(int name,...)743 void myaddrinfo_control(int name,...)
744 {
745 const char *myname = "myaddrinfo_control";
746 va_list ap;
747
748 for (va_start(ap, name); name != 0; name = va_arg(ap, int)) {
749 switch (name) {
750 default:
751 msg_panic("%s: bad name %d", myname, name);
752 }
753 }
754 va_end(ap);
755 }
756
757 #ifdef EMULATE_IPV4_ADDRINFO
758
759 /* freeaddrinfo - release storage */
760
freeaddrinfo(struct addrinfo * ai)761 void freeaddrinfo(struct addrinfo * ai)
762 {
763 struct addrinfo *ap;
764 struct addrinfo *next;
765
766 /*
767 * Artifact of implementation: tolerate a null pointer argument.
768 */
769 for (ap = ai; ap != 0; ap = next) {
770 next = ap->ai_next;
771 if (ap->ai_canonname)
772 myfree(ap->ai_canonname);
773 /* ap->ai_addr is allocated within this memory block */
774 myfree((void *) ap);
775 }
776 }
777
778 static char *ai_errlist[] = {
779 "Success",
780 "Address family for hostname not supported", /* EAI_ADDRFAMILY */
781 "Temporary failure in name resolution", /* EAI_AGAIN */
782 "Invalid value for ai_flags", /* EAI_BADFLAGS */
783 "Non-recoverable failure in name resolution", /* EAI_FAIL */
784 "ai_family not supported", /* EAI_FAMILY */
785 "Memory allocation failure", /* EAI_MEMORY */
786 "No address associated with hostname", /* EAI_NODATA */
787 "hostname nor servname provided, or not known", /* EAI_NONAME */
788 "service name not supported for ai_socktype", /* EAI_SERVICE */
789 "ai_socktype not supported", /* EAI_SOCKTYPE */
790 "System error returned in errno", /* EAI_SYSTEM */
791 "Invalid value for hints", /* EAI_BADHINTS */
792 "Resolved protocol is unknown", /* EAI_PROTOCOL */
793 "Unknown error", /* EAI_MAX */
794 };
795
796 /* gai_strerror - error number to string */
797
gai_strerror(int ecode)798 char *gai_strerror(int ecode)
799 {
800
801 /*
802 * Note: EAI_SYSTEM errors are not automatically handed over to
803 * strerror(). The application decides.
804 */
805 if (ecode < 0 || ecode > EAI_MAX)
806 ecode = EAI_MAX;
807 return (ai_errlist[ecode]);
808 }
809
810 #endif
811
812 #ifdef TEST
813
814 /*
815 * A test program that takes some info from the command line and runs it
816 * forward and backward through the above conversion routines.
817 */
818 #include <stdlib.h>
819 #include <msg.h>
820 #include <vstream.h>
821 #include <msg_vstream.h>
822
compare_family(const void * a,const void * b)823 static int compare_family(const void *a, const void *b)
824 {
825 struct addrinfo *resa = *(struct addrinfo **) a;
826 struct addrinfo *resb = *(struct addrinfo **) b;
827
828 return (resa->ai_family - resb->ai_family);
829 }
830
main(int argc,char ** argv)831 int main(int argc, char **argv)
832 {
833 struct addrinfo *info;
834 struct addrinfo *ip;
835 struct addrinfo **resv;
836 MAI_HOSTNAME_STR host;
837 MAI_HOSTADDR_STR addr;
838 size_t len, n;
839 int err;
840
841 msg_vstream_init(argv[0], VSTREAM_ERR);
842
843 if (argc != 4)
844 msg_fatal("usage: %s protocols hostname hostaddress", argv[0]);
845
846 inet_proto_init(argv[0], argv[1]);
847
848 msg_info("=== hostname %s ===", argv[2]);
849
850 if ((err = hostname_to_sockaddr(argv[2], (char *) 0, 0, &info)) != 0) {
851 msg_info("hostname_to_sockaddr(%s): %s",
852 argv[2], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
853 } else {
854 for (len = 0, ip = info; ip != 0; ip = ip->ai_next)
855 len += 1;
856 resv = (struct addrinfo **) mymalloc(len * sizeof(*resv));
857 for (len = 0, ip = info; ip != 0; ip = ip->ai_next)
858 resv[len++] = ip;
859 qsort((void *) resv, len, sizeof(*resv), compare_family);
860 for (n = 0; n < len; n++) {
861 ip = resv[n];
862 if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
863 (MAI_SERVPORT_STR *) 0, 0)) != 0) {
864 msg_info("sockaddr_to_hostaddr: %s",
865 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
866 continue;
867 }
868 msg_info("%s -> family=%d sock=%d proto=%d %s", argv[2],
869 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
870 if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
871 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
872 msg_info("sockaddr_to_hostname: %s",
873 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
874 continue;
875 }
876 msg_info("%s -> %s", addr.buf, host.buf);
877 }
878 freeaddrinfo(info);
879 myfree((void *) resv);
880 }
881
882 msg_info("=== host address %s ===", argv[3]);
883
884 if ((err = hostaddr_to_sockaddr(argv[3], (char *) 0, 0, &ip)) != 0) {
885 msg_info("hostaddr_to_sockaddr(%s): %s",
886 argv[3], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
887 } else {
888 if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
889 (MAI_SERVPORT_STR *) 0, 0)) != 0) {
890 msg_info("sockaddr_to_hostaddr: %s",
891 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
892 } else {
893 msg_info("%s -> family=%d sock=%d proto=%d %s", argv[3],
894 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
895 if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
896 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
897 msg_info("sockaddr_to_hostname: %s",
898 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
899 } else
900 msg_info("%s -> %s", addr.buf, host.buf);
901 freeaddrinfo(ip);
902 }
903 }
904 exit(0);
905 }
906
907 #endif
908