2 * Copyright (C) 2004-2006 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.206.6 2006/11/13 11:57:41 marka Exp $ */
28 #include <lwres/lwres.h>
29 #include <lwres/net.h>
30 #include <lwres/netdb.h>
31 #include <lwres/stdlib.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
) {
329 lwres_freeaddrinfo(ai_list
);
333 /* XXX raise error? */
334 ai
->ai_canonname
= NULL
;
338 } else if ((flags
& AI_NUMERICHOST
) != 0) {
343 set_order(family
, net_order
);
344 for (i
= 0; i
< FOUND_MAX
; i
++) {
345 if (net_order
[i
] == NULL
)
347 err
= (net_order
[i
])(hostname
, flags
, &ai_list
,
357 ai_list
= ai_reverse(ai_list
);
364 lwres_strsep(char **stringp
, const char *delim
) {
365 char *string
= *stringp
;
373 for (s
= string
; *s
!= '\0'; s
++) {
375 for (d
= delim
; (dc
= *d
) != '\0'; d
++)
387 set_order(int family
, int (**net_order
)(const char *, int, struct addrinfo
**,
396 *net_order
++ = add_ipv4
;
399 *net_order
++ = add_ipv6
;
403 order
= getenv("NET_ORDER");
405 while (order
!= NULL
) {
407 * We ignore any unknown names.
409 tok
= lwres_strsep(&order
, ":");
410 if (strcasecmp(tok
, "inet6") == 0) {
411 if ((found
& FOUND_IPV6
) == 0)
412 *net_order
++ = add_ipv6
;
414 } else if (strcasecmp(tok
, "inet") == 0 ||
415 strcasecmp(tok
, "inet4") == 0) {
416 if ((found
& FOUND_IPV4
) == 0)
417 *net_order
++ = add_ipv4
;
423 * Add in anything that we didn't find.
425 if ((found
& FOUND_IPV4
) == 0)
426 *net_order
++ = add_ipv4
;
427 if ((found
& FOUND_IPV6
) == 0)
428 *net_order
++ = add_ipv6
;
434 static char v4_loop
[4] = { 127, 0, 0, 1 };
437 * The test against 0 is there to keep the Solaris compiler
438 * from complaining about "end-of-loop code not reached".
440 #define SETERROR(code) \
441 do { result = (code); \
442 if (result != 0) goto cleanup; \
446 add_ipv4(const char *hostname
, int flags
, struct addrinfo
**aip
,
447 int socktype
, int port
)
450 lwres_context_t
*lwrctx
= NULL
;
451 lwres_gabnresponse_t
*by
= NULL
;
453 lwres_result_t lwres
;
456 lwres
= lwres_context_create(&lwrctx
, NULL
, NULL
, NULL
, 0);
457 if (lwres
!= LWRES_R_SUCCESS
)
459 (void) lwres_conf_parse(lwrctx
, lwres_resolv_conf
);
460 if (hostname
== NULL
&& (flags
& AI_PASSIVE
) == 0) {
461 ai
= ai_clone(*aip
, AF_INET
);
463 lwres_freeaddrinfo(*aip
);
464 SETERROR(EAI_MEMORY
);
468 ai
->ai_socktype
= socktype
;
469 SIN(ai
->ai_addr
)->sin_port
= port
;
470 memcpy(&SIN(ai
->ai_addr
)->sin_addr
, v4_loop
, 4);
472 lwres
= lwres_getaddrsbyname(lwrctx
, hostname
,
473 LWRES_ADDRTYPE_V4
, &by
);
474 if (lwres
!= LWRES_R_SUCCESS
) {
475 if (lwres
== LWRES_R_NOTFOUND
)
480 addr
= LWRES_LIST_HEAD(by
->addrs
);
481 while (addr
!= NULL
) {
482 ai
= ai_clone(*aip
, AF_INET
);
484 lwres_freeaddrinfo(*aip
);
485 SETERROR(EAI_MEMORY
);
488 ai
->ai_socktype
= socktype
;
489 SIN(ai
->ai_addr
)->sin_port
= port
;
490 memcpy(&SIN(ai
->ai_addr
)->sin_addr
,
492 if (flags
& AI_CANONNAME
) {
493 ai
->ai_canonname
= strdup(by
->realname
);
494 if (ai
->ai_canonname
== NULL
)
495 SETERROR(EAI_MEMORY
);
497 addr
= LWRES_LIST_NEXT(addr
, link
);
502 lwres_gabnresponse_free(lwrctx
, &by
);
503 if (lwrctx
!= NULL
) {
504 lwres_conf_clear(lwrctx
);
505 lwres_context_destroy(&lwrctx
);
510 static char v6_loop
[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
513 add_ipv6(const char *hostname
, int flags
, struct addrinfo
**aip
,
514 int socktype
, int port
)
517 lwres_context_t
*lwrctx
= NULL
;
518 lwres_gabnresponse_t
*by
= NULL
;
520 lwres_result_t lwres
;
523 lwres
= lwres_context_create(&lwrctx
, NULL
, NULL
, NULL
, 0);
524 if (lwres
!= LWRES_R_SUCCESS
)
526 (void) lwres_conf_parse(lwrctx
, lwres_resolv_conf
);
528 if (hostname
== NULL
&& (flags
& AI_PASSIVE
) == 0) {
529 ai
= ai_clone(*aip
, AF_INET6
);
531 lwres_freeaddrinfo(*aip
);
532 SETERROR(EAI_MEMORY
);
536 ai
->ai_socktype
= socktype
;
537 SIN6(ai
->ai_addr
)->sin6_port
= port
;
538 memcpy(&SIN6(ai
->ai_addr
)->sin6_addr
, v6_loop
, 16);
540 lwres
= lwres_getaddrsbyname(lwrctx
, hostname
,
541 LWRES_ADDRTYPE_V6
, &by
);
542 if (lwres
!= LWRES_R_SUCCESS
) {
543 if (lwres
== LWRES_R_NOTFOUND
)
548 addr
= LWRES_LIST_HEAD(by
->addrs
);
549 while (addr
!= NULL
) {
550 ai
= ai_clone(*aip
, AF_INET6
);
552 lwres_freeaddrinfo(*aip
);
553 SETERROR(EAI_MEMORY
);
556 ai
->ai_socktype
= socktype
;
557 SIN6(ai
->ai_addr
)->sin6_port
= port
;
558 memcpy(&SIN6(ai
->ai_addr
)->sin6_addr
,
560 if (flags
& AI_CANONNAME
) {
561 ai
->ai_canonname
= strdup(by
->realname
);
562 if (ai
->ai_canonname
== NULL
)
563 SETERROR(EAI_MEMORY
);
565 addr
= LWRES_LIST_NEXT(addr
, link
);
570 lwres_gabnresponse_free(lwrctx
, &by
);
571 if (lwrctx
!= NULL
) {
572 lwres_conf_clear(lwrctx
);
573 lwres_context_destroy(&lwrctx
);
579 lwres_freeaddrinfo(struct addrinfo
*ai
) {
580 struct addrinfo
*ai_next
;
583 ai_next
= ai
->ai_next
;
584 if (ai
->ai_addr
!= NULL
)
586 if (ai
->ai_canonname
)
587 free(ai
->ai_canonname
);
595 get_local(const char *name
, int socktype
, struct addrinfo
**res
) {
597 struct sockaddr_un
*sun
;
600 return (EAI_SOCKTYPE
);
602 ai
= ai_alloc(AF_LOCAL
, sizeof(*sun
));
606 sun
= SUN(ai
->ai_addr
);
607 strncpy(sun
->sun_path
, name
, sizeof(sun
->sun_path
));
609 ai
->ai_socktype
= socktype
;
611 * ai->ai_flags, ai->ai_protocol, ai->ai_canonname,
612 * and ai->ai_next were initialized to zero.
621 * Allocate an addrinfo structure, and a sockaddr structure
622 * of the specificed length. We initialize:
627 * ai_addr->sa_len (LWRES_PLATFORM_HAVESALEN)
628 * and everything else is initialized to zero.
630 static struct addrinfo
*
631 ai_alloc(int family
, int addrlen
) {
634 ai
= (struct addrinfo
*)calloc(1, sizeof(*ai
));
638 ai
->ai_addr
= SA(calloc(1, addrlen
));
639 if (ai
->ai_addr
== NULL
) {
643 ai
->ai_addrlen
= addrlen
;
644 ai
->ai_family
= family
;
645 ai
->ai_addr
->sa_family
= family
;
646 #ifdef LWRES_PLATFORM_HAVESALEN
647 ai
->ai_addr
->sa_len
= addrlen
;
652 static struct addrinfo
*
653 ai_clone(struct addrinfo
*oai
, int family
) {
656 ai
= ai_alloc(family
, ((family
== AF_INET6
) ?
657 sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
)));
660 lwres_freeaddrinfo(oai
);
666 ai
->ai_flags
= oai
->ai_flags
;
667 ai
->ai_socktype
= oai
->ai_socktype
;
668 ai
->ai_protocol
= oai
->ai_protocol
;
669 ai
->ai_canonname
= NULL
;
674 static struct addrinfo
*
675 ai_reverse(struct addrinfo
*oai
) {
676 struct addrinfo
*nai
, *tai
;
680 while (oai
!= NULL
) {
682 * Grab one off the old list.
687 * Put it on the front of the new list.