2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2001 Internet Software Consortium.
5 * This code is derived from software contributed to ISC by
6 * Berkeley Software Design, Inc.
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND BERKELEY SOFTWARE DESIGN, INC.
13 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
15 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 /* $Id: getaddrinfo.c,v 1.41.2.1 2004/03/09 06:12:33 marka Exp $ */
29 #include <lwres/lwres.h>
30 #include <lwres/net.h>
31 #include <lwres/netdb.h>
33 #define SA(addr) ((struct sockaddr *)(addr))
34 #define SIN(addr) ((struct sockaddr_in *)(addr))
35 #define SIN6(addr) ((struct sockaddr_in6 *)(addr))
36 #define SUN(addr) ((struct sockaddr_un *)(addr))
38 static struct addrinfo
39 *ai_reverse(struct addrinfo
*oai
),
40 *ai_clone(struct addrinfo
*oai
, int family
),
41 *ai_alloc(int family
, int addrlen
);
43 static int get_local(const char *name
, int socktype
, struct addrinfo
**res
);
46 static int add_ipv4(const char *hostname
, int flags
, struct addrinfo
**aip
,
47 int socktype
, int port
);
48 static int add_ipv6(const char *hostname
, int flags
, struct addrinfo
**aip
,
49 int socktype
, int port
);
50 static void set_order(int, int (**)(const char *, int, struct addrinfo
**,
53 #define FOUND_IPV4 0x1
54 #define FOUND_IPV6 0x2
57 #define ISC_AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST)
60 lwres_getaddrinfo(const char *hostname
, const char *servname
,
61 const struct addrinfo
*hints
, struct addrinfo
**res
)
65 int family
, socktype
, flags
, protocol
;
66 struct addrinfo
*ai
, *ai_list
;
68 int (*net_order
[FOUND_MAX
+1])(const char *, int, struct addrinfo
**,
71 if (hostname
== NULL
&& servname
== NULL
)
76 if ((hints
->ai_flags
& ~(ISC_AI_MASK
)) != 0)
77 return (EAI_BADFLAGS
);
78 if (hints
->ai_addrlen
|| hints
->ai_canonname
||
79 hints
->ai_addr
|| hints
->ai_next
) {
83 family
= hints
->ai_family
;
84 socktype
= hints
->ai_socktype
;
85 protocol
= hints
->ai_protocol
;
86 flags
= hints
->ai_flags
;
89 switch (hints
->ai_socktype
) {
100 switch (hints
->ai_socktype
) {
112 return (EAI_SOCKTYPE
);
117 switch (hints
->ai_socktype
) {
125 return (EAI_SOCKTYPE
);
141 * First, deal with AF_LOCAL. If the family was not set,
142 * then assume AF_LOCAL if the first character of the
143 * hostname/servname is '/'.
146 if (hostname
!= NULL
&&
147 (family
== AF_LOCAL
|| (family
== 0 && *hostname
== '/')))
148 return (get_local(hostname
, socktype
, res
));
150 if (servname
!= NULL
&&
151 (family
== AF_LOCAL
|| (family
== 0 && *servname
== '/')))
152 return (get_local(servname
, socktype
, res
));
156 * Ok, only AF_INET and AF_INET6 left.
161 * First, look up the service name (port) if it was
162 * requested. If the socket type wasn't specified, then
163 * try and figure it out.
165 if (servname
!= NULL
) {
168 port
= strtol(servname
, &e
, 10);
171 return (EAI_SOCKTYPE
);
172 if (port
< 0 || port
> 65535)
173 return (EAI_SERVICE
);
174 port
= htons((unsigned short) port
);
176 sp
= getservbyname(servname
, proto
);
178 return (EAI_SERVICE
);
181 if (strcmp(sp
->s_proto
, "tcp") == 0)
182 socktype
= SOCK_STREAM
;
183 else if (strcmp(sp
->s_proto
, "udp") == 0)
184 socktype
= SOCK_DGRAM
;
191 * Next, deal with just a service name, and no hostname.
192 * (we verified that one of them was non-null up above).
194 if (hostname
== NULL
&& (flags
& AI_PASSIVE
) != 0) {
195 if (family
== AF_INET
|| family
== 0) {
196 ai
= ai_alloc(AF_INET
, sizeof(struct sockaddr_in
));
199 ai
->ai_socktype
= socktype
;
200 ai
->ai_protocol
= protocol
;
201 SIN(ai
->ai_addr
)->sin_port
= port
;
202 ai
->ai_next
= ai_list
;
206 if (family
== AF_INET6
|| family
== 0) {
207 ai
= ai_alloc(AF_INET6
, sizeof(struct sockaddr_in6
));
209 lwres_freeaddrinfo(ai_list
);
212 ai
->ai_socktype
= socktype
;
213 ai
->ai_protocol
= protocol
;
214 SIN6(ai
->ai_addr
)->sin6_port
= port
;
215 ai
->ai_next
= ai_list
;
224 * If the family isn't specified or AI_NUMERICHOST specified,
225 * check first to see if it is a numeric address.
226 * Though the gethostbyname2() routine
227 * will recognize numeric addresses, it will only recognize
228 * the format that it is being called for. Thus, a numeric
229 * AF_INET address will be treated by the AF_INET6 call as
230 * a domain name, and vice versa. Checking for both numerics
233 if (hostname
!= NULL
&&
234 (family
== 0 || (flags
& AI_NUMERICHOST
) != 0)) {
235 char abuf
[sizeof(struct in6_addr
)];
236 char nbuf
[NI_MAXHOST
];
237 int addrsize
, addroff
;
238 #ifdef LWRES_HAVE_SIN6_SCOPE_ID
240 char ntmp
[NI_MAXHOST
];
241 lwres_uint32_t scopeid
;
244 #ifdef LWRES_HAVE_SIN6_SCOPE_ID
246 * Scope identifier portion.
249 if (strchr(hostname
, '%') != NULL
) {
250 strncpy(ntmp
, hostname
, sizeof(ntmp
) - 1);
251 ntmp
[sizeof(ntmp
) - 1] = '\0';
252 p
= strchr(ntmp
, '%');
256 * Vendors may want to support non-numeric
257 * scopeid around here.
261 scopeid
= (lwres_uint32_t
)strtoul(p
+ 1,
263 if (p
!= NULL
&& ep
!= NULL
&& ep
[0] == '\0')
273 if (lwres_net_pton(AF_INET
, hostname
, (struct in_addr
*)abuf
)
276 if (family
== AF_INET6
) {
278 * Convert to a V4 mapped address.
280 struct in6_addr
*a6
= (struct in6_addr
*)abuf
;
281 memcpy(&a6
->s6_addr
[12], &a6
->s6_addr
[0], 4);
282 memset(&a6
->s6_addr
[10], 0xff, 2);
283 memset(&a6
->s6_addr
[0], 0, 10);
286 addrsize
= sizeof(struct in_addr
);
287 addroff
= (char *)(&SIN(0)->sin_addr
) - (char *)0;
290 #ifdef LWRES_HAVE_SIN6_SCOPE_ID
291 } else if (ntmp
[0] != '\0' &&
292 lwres_net_pton(AF_INET6
, ntmp
, abuf
) == 1)
294 if (family
&& family
!= AF_INET6
)
296 addrsize
= sizeof(struct in6_addr
);
297 addroff
= (char *)(&SIN6(0)->sin6_addr
) - (char *)0;
301 } else if (lwres_net_pton(AF_INET6
, hostname
, abuf
) == 1) {
302 if (family
!= 0 && family
!= AF_INET6
)
305 addrsize
= sizeof(struct in6_addr
);
306 addroff
= (char *)(&SIN6(0)->sin6_addr
) - (char *)0;
310 ai
= ai_clone(ai_list
, family
);
314 ai
->ai_socktype
= socktype
;
315 SIN(ai
->ai_addr
)->sin_port
= port
;
316 memcpy((char *)ai
->ai_addr
+ addroff
, abuf
, addrsize
);
317 if (flags
& AI_CANONNAME
) {
318 #if defined(LWRES_HAVE_SIN6_SCOPE_ID)
319 if (ai
->ai_family
== AF_INET6
)
320 SIN6(ai
->ai_addr
)->sin6_scope_id
=
323 if (lwres_getnameinfo(ai
->ai_addr
,
324 ai
->ai_addrlen
, nbuf
, sizeof(nbuf
),
326 NI_NUMERICHOST
) == 0) {
327 ai
->ai_canonname
= strdup(nbuf
);
328 if (ai
->ai_canonname
== NULL
)
331 /* XXX raise error? */
332 ai
->ai_canonname
= NULL
;
336 } else if ((flags
& AI_NUMERICHOST
) != 0) {
341 set_order(family
, net_order
);
342 for (i
= 0; i
< FOUND_MAX
; i
++) {
343 if (net_order
[i
] == NULL
)
345 err
= (net_order
[i
])(hostname
, flags
, &ai_list
,
355 ai_list
= ai_reverse(ai_list
);
362 lwres_strsep(char **stringp
, const char *delim
) {
363 char *string
= *stringp
;
371 for (s
= string
; *s
!= '\0'; s
++) {
373 for (d
= delim
; (dc
= *d
) != '\0'; d
++)
385 set_order(int family
, int (**net_order
)(const char *, int, struct addrinfo
**,
394 *net_order
++ = add_ipv4
;
397 *net_order
++ = add_ipv6
;
401 order
= getenv("NET_ORDER");
403 while (order
!= NULL
) {
405 * We ignore any unknown names.
407 tok
= lwres_strsep(&order
, ":");
408 if (strcasecmp(tok
, "inet6") == 0) {
409 if ((found
& FOUND_IPV6
) == 0)
410 *net_order
++ = add_ipv6
;
412 } else if (strcasecmp(tok
, "inet") == 0 ||
413 strcasecmp(tok
, "inet4") == 0) {
414 if ((found
& FOUND_IPV4
) == 0)
415 *net_order
++ = add_ipv4
;
421 * Add in anything that we didn't find.
423 if ((found
& FOUND_IPV4
) == 0)
424 *net_order
++ = add_ipv4
;
425 if ((found
& FOUND_IPV6
) == 0)
426 *net_order
++ = add_ipv6
;
432 static char v4_loop
[4] = { 127, 0, 0, 1 };
435 * The test against 0 is there to keep the Solaris compiler
436 * from complaining about "end-of-loop code not reached".
439 do { result = (code); \
440 if (result != 0) goto cleanup; \
444 add_ipv4(const char *hostname
, int flags
, struct addrinfo
**aip
,
445 int socktype
, int port
)
448 lwres_context_t
*lwrctx
= NULL
;
449 lwres_gabnresponse_t
*by
= NULL
;
451 lwres_result_t lwres
;
454 lwres
= lwres_context_create(&lwrctx
, NULL
, NULL
, NULL
, 0);
455 if (lwres
!= LWRES_R_SUCCESS
)
457 (void) lwres_conf_parse(lwrctx
, lwres_resolv_conf
);
458 if (hostname
== NULL
&& (flags
& AI_PASSIVE
) == 0) {
459 ai
= ai_clone(*aip
, AF_INET
);
461 lwres_freeaddrinfo(*aip
);
466 ai
->ai_socktype
= socktype
;
467 SIN(ai
->ai_addr
)->sin_port
= port
;
468 memcpy(&SIN(ai
->ai_addr
)->sin_addr
, v4_loop
, 4);
470 lwres
= lwres_getaddrsbyname(lwrctx
, hostname
,
471 LWRES_ADDRTYPE_V4
, &by
);
472 if (lwres
!= LWRES_R_SUCCESS
) {
473 if (lwres
== LWRES_R_NOTFOUND
)
478 addr
= LWRES_LIST_HEAD(by
->addrs
);
479 while (addr
!= NULL
) {
480 ai
= ai_clone(*aip
, AF_INET
);
482 lwres_freeaddrinfo(*aip
);
486 ai
->ai_socktype
= socktype
;
487 SIN(ai
->ai_addr
)->sin_port
= port
;
488 memcpy(&SIN(ai
->ai_addr
)->sin_addr
,
490 if (flags
& AI_CANONNAME
) {
491 ai
->ai_canonname
= strdup(by
->realname
);
492 if (ai
->ai_canonname
== NULL
)
495 addr
= LWRES_LIST_NEXT(addr
, link
);
500 lwres_gabnresponse_free(lwrctx
, &by
);
501 if (lwrctx
!= NULL
) {
502 lwres_conf_clear(lwrctx
);
503 lwres_context_destroy(&lwrctx
);
508 static char v6_loop
[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
511 add_ipv6(const char *hostname
, int flags
, struct addrinfo
**aip
,
512 int socktype
, int port
)
515 lwres_context_t
*lwrctx
= NULL
;
516 lwres_gabnresponse_t
*by
= NULL
;
518 lwres_result_t lwres
;
521 lwres
= lwres_context_create(&lwrctx
, NULL
, NULL
, NULL
, 0);
522 if (lwres
!= LWRES_R_SUCCESS
)
524 (void) lwres_conf_parse(lwrctx
, lwres_resolv_conf
);
526 if (hostname
== NULL
&& (flags
& AI_PASSIVE
) == 0) {
527 ai
= ai_clone(*aip
, AF_INET6
);
529 lwres_freeaddrinfo(*aip
);
534 ai
->ai_socktype
= socktype
;
535 SIN6(ai
->ai_addr
)->sin6_port
= port
;
536 memcpy(&SIN6(ai
->ai_addr
)->sin6_addr
, v6_loop
, 16);
538 lwres
= lwres_getaddrsbyname(lwrctx
, hostname
,
539 LWRES_ADDRTYPE_V6
, &by
);
540 if (lwres
!= LWRES_R_SUCCESS
) {
541 if (lwres
== LWRES_R_NOTFOUND
)
546 addr
= LWRES_LIST_HEAD(by
->addrs
);
547 while (addr
!= NULL
) {
548 ai
= ai_clone(*aip
, AF_INET6
);
550 lwres_freeaddrinfo(*aip
);
554 ai
->ai_socktype
= socktype
;
555 SIN6(ai
->ai_addr
)->sin6_port
= port
;
556 memcpy(&SIN6(ai
->ai_addr
)->sin6_addr
,
558 if (flags
& AI_CANONNAME
) {
559 ai
->ai_canonname
= strdup(by
->realname
);
560 if (ai
->ai_canonname
== NULL
)
563 addr
= LWRES_LIST_NEXT(addr
, link
);
568 lwres_gabnresponse_free(lwrctx
, &by
);
569 if (lwrctx
!= NULL
) {
570 lwres_conf_clear(lwrctx
);
571 lwres_context_destroy(&lwrctx
);
577 lwres_freeaddrinfo(struct addrinfo
*ai
) {
578 struct addrinfo
*ai_next
;
581 ai_next
= ai
->ai_next
;
582 if (ai
->ai_addr
!= NULL
)
584 if (ai
->ai_canonname
)
585 free(ai
->ai_canonname
);
593 get_local(const char *name
, int socktype
, struct addrinfo
**res
) {
595 struct sockaddr_un
*sun
;
598 return (EAI_SOCKTYPE
);
600 ai
= ai_alloc(AF_LOCAL
, sizeof(*sun
));
604 sun
= SUN(ai
->ai_addr
);
605 strncpy(sun
->sun_path
, name
, sizeof(sun
->sun_path
));
607 ai
->ai_socktype
= socktype
;
609 * ai->ai_flags, ai->ai_protocol, ai->ai_canonname,
610 * and ai->ai_next were initialized to zero.
619 * Allocate an addrinfo structure, and a sockaddr structure
620 * of the specificed length. We initialize:
625 * ai_addr->sa_len (LWRES_PLATFORM_HAVESALEN)
626 * and everything else is initialized to zero.
628 static struct addrinfo
*
629 ai_alloc(int family
, int addrlen
) {
632 ai
= (struct addrinfo
*)calloc(1, sizeof(*ai
));
636 ai
->ai_addr
= SA(calloc(1, addrlen
));
637 if (ai
->ai_addr
== NULL
) {
641 ai
->ai_addrlen
= addrlen
;
642 ai
->ai_family
= family
;
643 ai
->ai_addr
->sa_family
= family
;
644 #ifdef LWRES_PLATFORM_HAVESALEN
645 ai
->ai_addr
->sa_len
= addrlen
;
650 static struct addrinfo
*
651 ai_clone(struct addrinfo
*oai
, int family
) {
654 ai
= ai_alloc(family
, ((family
== AF_INET6
) ?
655 sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
)));
658 lwres_freeaddrinfo(oai
);
664 ai
->ai_flags
= oai
->ai_flags
;
665 ai
->ai_socktype
= oai
->ai_socktype
;
666 ai
->ai_protocol
= oai
->ai_protocol
;
667 ai
->ai_canonname
= NULL
;
672 static struct addrinfo
*
673 ai_reverse(struct addrinfo
*oai
) {
674 struct addrinfo
*nai
, *tai
;
678 while (oai
!= NULL
) {
680 * Grab one off the old list.
685 * Put it on the front of the new list.