Libinfo-330.10.tar.gz
[apple/libinfo.git] / lookup.subproj / si_getaddrinfo.c
1 /*
2 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <netdb.h>
25 #include <sys/types.h>
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <ifaddrs.h>
34 #include <net/if.h>
35 #include <string.h>
36 #include <sys/param.h>
37 #include <notify.h>
38 #include <notify_keys.h>
39 #include <pthread.h>
40 #include <TargetConditionals.h>
41 #include "netdb_async.h"
42 #include "si_module.h"
43
44 #define SOCK_UNSPEC 0
45 #define IPPROTO_UNSPEC 0
46
47 #define IPV6_ADDR_LEN 16
48 #define IPV4_ADDR_LEN 4
49
50 #define WANT_NOTHING 0
51 #define WANT_A4_ONLY 1
52 #define WANT_A6_ONLY 2
53 #define WANT_A6_PLUS_MAPPED_A4 3
54 #define WANT_A6_OR_MAPPED_A4_IF_NO_A6 4
55
56 #define TEREDO_PREFIX_32 0x20010000
57 #define V6TO4_PREFIX_16 0x2002
58 #define ULA_PREFIX_8 0xfc
59
60 #define NET_TYPE_UNKNOWN 0x00000000
61 #define NET_TYPE_V4 0x00000001
62 #define NET_TYPE_V6 0x00000002
63 #define NET_TYPE_TEREDO 0x00000012
64 #define NET_TYPE_6TO4 0x00000022
65 #define NET_TYPE_LINKLOCAL 0x00000042
66 #define NET_TYPE_SITELOCAL 0x00000082
67 #define NET_TYPE_ULA 0x00000102
68 #define NET_MASK_T6LSU 0x000001f0
69
70 typedef struct
71 {
72 int v4_count;
73 int v6_count;
74 int v6to4_count;
75 int teredo_count;
76 int ula_count;
77 int ll_count;
78 int sl_count;
79 int gai_v6_preferred;
80 } config_stats_t;
81
82 static int net_config_token = -1;
83 static config_stats_t config_stats_global;
84 static pthread_mutex_t net_config_mutex = PTHREAD_MUTEX_INITIALIZER;
85
86 typedef struct {
87 struct hostent host;
88 int alias_count;
89 int addr_count;
90 uint64_t ttl;
91 } build_hostent_t;
92
93 static int
94 _si_net_addr_type(const struct sockaddr *s)
95 {
96 const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *)s;
97
98 if (s == NULL) return NET_TYPE_UNKNOWN;
99 else if (s->sa_family == AF_INET) return NET_TYPE_V4;
100 else if (s->sa_family != AF_INET6) return NET_TYPE_UNKNOWN;
101 else if (sa6->sin6_addr.__u6_addr.__u6_addr32[0] == ntohs(TEREDO_PREFIX_32)) return NET_TYPE_TEREDO;
102 else if (sa6->sin6_addr.__u6_addr.__u6_addr16[0] == ntohs(V6TO4_PREFIX_16)) return NET_TYPE_6TO4;
103 else if (IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))) return NET_TYPE_LINKLOCAL;
104 else if (IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr))) return NET_TYPE_SITELOCAL;
105 else if ((sa6->sin6_addr.__u6_addr.__u6_addr8[0] & 0xfe) == ULA_PREFIX_8) return NET_TYPE_ULA;
106
107 return NET_TYPE_V6;
108 }
109
110 static int
111 _si_net_type_is_IPv6_globably_reachable_non_transitional(int t)
112 {
113 if (t & NET_TYPE_V6)
114 {
115 if (t & NET_MASK_T6LSU) return 0;
116 return 1;
117 }
118
119 return 0;
120 }
121
122 static int
123 _si_netconfig(config_stats_t *stats)
124 {
125 int status, checkit, net_type;
126 struct ifaddrs *ifa, *ifap;
127
128 if (stats == NULL) return 0;
129
130 pthread_mutex_lock(&net_config_mutex);
131
132 status = 0;
133 checkit = 1;
134
135 if (net_config_token < 0)
136 {
137 status = notify_register_check(kNotifySCNetworkChange, &net_config_token);
138 if (status != 0) net_config_token = -1;
139 }
140
141 if (net_config_token >= 0)
142 {
143 status = notify_check(net_config_token, &checkit);
144 if (status != 0) checkit = 1;
145 }
146
147 if (checkit != 0)
148 {
149 if (getifaddrs(&ifa) < 0)
150 {
151 status = -1;
152 }
153 else
154 {
155 memset(&config_stats_global, 0, sizeof(config_stats_t));
156
157 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
158 {
159 if (ifap->ifa_addr == NULL) continue;
160 if ((ifap->ifa_flags & IFF_UP) == 0) continue;
161
162 net_type = _si_net_addr_type(ifap->ifa_addr);
163
164 if (net_type == NET_TYPE_UNKNOWN)
165 {
166 }
167 else if (net_type == NET_TYPE_V4)
168 {
169 config_stats_global.v4_count++;
170 }
171 else
172 {
173 config_stats_global.v6_count++;
174
175 if (net_type == NET_TYPE_LINKLOCAL)
176 {
177 config_stats_global.ll_count++;
178 continue;
179 }
180 else if (net_type == NET_TYPE_ULA)
181 {
182 config_stats_global.ula_count++;
183 if (!strncmp(ifap->ifa_name, "utun", 4)) continue;
184 }
185 if (net_type == NET_TYPE_TEREDO)
186 {
187 config_stats_global.teredo_count++;
188 }
189 else if (net_type == NET_TYPE_6TO4)
190 {
191 config_stats_global.v6to4_count++;
192 }
193 else if (net_type == NET_TYPE_SITELOCAL)
194 {
195 config_stats_global.sl_count++;
196 }
197
198 if (_si_net_type_is_IPv6_globably_reachable_non_transitional(net_type))
199 {
200 config_stats_global.gai_v6_preferred = 1;
201 }
202 }
203 }
204
205 freeifaddrs(ifa);
206 }
207 }
208
209 *stats = config_stats_global;
210
211 pthread_mutex_unlock(&net_config_mutex);
212
213 return status;
214 }
215
216 __private_extern__ int
217 si_inet_config(uint32_t *inet4, uint32_t *inet6)
218 {
219 config_stats_t x;
220
221 if (_si_netconfig(&x) < 0) return -1;
222 if (inet4 != NULL) *inet4 = x.v4_count;
223 if (inet6 != NULL) *inet6 = x.v6_count;
224 return 0;
225 }
226
227 void
228 freeaddrinfo(struct addrinfo *a)
229 {
230 struct addrinfo *next;
231
232 while (a != NULL)
233 {
234 next = a->ai_next;
235 if (a->ai_addr != NULL) free(a->ai_addr);
236 if (a->ai_canonname != NULL) free(a->ai_canonname);
237 free(a);
238 a = next;
239 }
240 }
241
242 const char *
243 gai_strerror(int32_t err)
244 {
245 switch (err)
246 {
247 case EAI_ADDRFAMILY: return "Address family for nodename not supported";
248 case EAI_AGAIN: return "Temporary failure in name resolution";
249 case EAI_BADFLAGS: return "Invalid value for ai_flags";
250 case EAI_FAIL: return "Non-recoverable failure in name resolution";
251 case EAI_FAMILY: return "ai_family not supported";
252 case EAI_MEMORY: return "Memory allocation failure";
253 case EAI_NODATA: return "No address associated with nodename";
254 case EAI_NONAME: return "nodename nor servname provided, or not known";
255 case EAI_SERVICE: return "servname not supported for ai_socktype";
256 case EAI_SOCKTYPE: return "ai_socktype not supported";
257 case EAI_SYSTEM: return "System error";
258 case EAI_BADHINTS: return "Bad hints";
259 case EAI_PROTOCOL: return "ai_protocol not supported";
260 case EAI_OVERFLOW: return "argument buffer overflow";
261 }
262
263 return "Unknown error";
264 }
265
266 /*
267 * getnameinfo
268 *
269 * We handle some "trival" cases locally. If the caller passes
270 * NI_NUMERICHOST (only), then this call turns into a getservbyport
271 * to get the service name + inet_pton() to create a host string.
272 * If the caller passes NI_NUMERICSERV (only), then we zero out the port
273 * number, complete the getnameinfo, and use printf() to create a service
274 * string. If the caller specifies both NI_NUMERICHOST and NI_NUMERICSERV,
275 * we inet_ntop() and printf() and return the results.
276 */
277 __private_extern__ si_item_t *
278 si_nameinfo(si_mod_t *si, const struct sockaddr *sa, int flags, const char *interface, uint32_t *err)
279 {
280 si_item_t *out = NULL;
281 const struct sockaddr *lookup_sa;
282 struct sockaddr_in s4;
283 struct in_addr a4;
284 struct in6_addr a6;
285 const uint64_t unused = 0;
286 void *addr = NULL;
287 char *host = NULL;
288 char *serv = NULL;
289 uint32_t ifnum = 0;
290 uint16_t port = 0;
291
292 int do_host_lookup = ((flags & NI_NUMERICHOST) == 0);
293 int do_serv_lookup = ((flags & NI_NUMERICSERV) == 0);
294
295 /* check input */
296 if ((si == NULL) || (sa == NULL))
297 {
298 if (err != NULL) *err = SI_STATUS_EAI_FAIL;
299 return NULL;
300 }
301
302 if (err != NULL) *err = SI_STATUS_NO_ERROR;
303
304 lookup_sa = sa;
305
306 if (sa->sa_family == AF_INET)
307 {
308 struct sockaddr_in *s4 = (struct sockaddr_in *)sa;
309 memcpy(&a4, &s4->sin_addr, sizeof(a4));
310 port = s4->sin_port;
311 addr = &a4;
312 }
313 else if (sa->sa_family == AF_INET6)
314 {
315 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)sa;
316 memcpy(&a6, &s6->sin6_addr, sizeof(a6));
317 port = s6->sin6_port;
318
319 /* Look for link-local IPv6 scope id */
320 if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr))
321 {
322 ifnum = ntohs(a6.__u6_addr.__u6_addr16[1]);
323 if (ifnum == 0)
324 {
325 ifnum = s6->sin6_scope_id;
326 a6.__u6_addr.__u6_addr16[1] = htons(ifnum);
327 }
328
329 if (ifnum != s6->sin6_scope_id &&
330 s6->sin6_scope_id != 0)
331 {
332 if (err != NULL) *err = SI_STATUS_EAI_FAIL;
333 return NULL;
334 }
335 }
336
337 /* v4 mapped and compat addresses are converted to plain v4 */
338 if (IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr) || IN6_IS_ADDR_V4COMPAT(&s6->sin6_addr))
339 {
340 memcpy(&a4, &s6->sin6_addr.s6_addr[12], sizeof(a4));
341 addr = &a4;
342 memset(&s4, 0, sizeof(s4));
343 s4.sin_len = sizeof(s4);
344 s4.sin_family = AF_INET;
345 s4.sin_port = port;
346 memcpy(&s4.sin_addr, &a4, sizeof(s4.sin_addr));
347 lookup_sa = (const struct sockaddr *)&s4;
348 }
349 else
350 {
351 addr = &a6;
352 }
353 }
354 else
355 {
356 if (err != NULL) *err = SI_STATUS_EAI_FAMILY;
357 return NULL;
358 }
359
360 if (do_host_lookup == 1)
361 {
362 #if 0
363 if ((do_serv_lookup == 1) && (si->sim_nameinfo != NULL))
364 {
365 return si->sim_nameinfo(si, lookup_sa, flags, interface, err);
366 }
367 #endif
368 si_item_t *item = si_host_byaddr(si, addr, lookup_sa->sa_family, interface, NULL);
369 if (item != NULL)
370 {
371 struct hostent *h;
372 h = (struct hostent *)((uintptr_t)item + sizeof(si_item_t));
373 host = strdup(h->h_name);
374 si_item_release(item);
375 if (host == NULL)
376 {
377 if (err != NULL) *err = SI_STATUS_EAI_MEMORY;
378 return NULL;
379 }
380 }
381 }
382
383 if ((do_serv_lookup == 1) && (port != 0))
384 {
385 si_item_t *item = si_service_byport(si, port, NULL);
386 if (item != NULL)
387 {
388 struct servent *s;
389 s = (struct servent *)((uintptr_t)item + sizeof(si_item_t));
390 serv = strdup(s->s_name);
391 si_item_release(item);
392 if (serv == NULL)
393 {
394 free(host);
395 if (err != NULL) *err = SI_STATUS_EAI_MEMORY;
396 return NULL;
397 }
398 }
399 }
400
401 /*
402 * Return numeric host name for NI_NUMERICHOST or if lookup failed, but not
403 * if NI_NAMEREQD is specified (so that we later fail with EAI_NONAME).
404 */
405 if (host == NULL && (flags & NI_NAMEREQD) == 0)
406 {
407 char tmp[INET6_ADDRSTRLEN + 1 + IF_NAMESIZE + 1];
408 tmp[0] = '\0';
409 if (sa->sa_family == AF_INET)
410 {
411 char buf[INET_ADDRSTRLEN];
412 if (inet_ntop(AF_INET, &a4, buf, sizeof(buf)) != 0)
413 {
414 host = strdup(buf);
415 }
416 }
417 else if (sa->sa_family == AF_INET6)
418 {
419 char buf[INET6_ADDRSTRLEN];
420
421 /* zero the embedded scope ID */
422 if (ifnum != 0)
423 {
424 a6.__u6_addr.__u6_addr16[1] = 0;
425 }
426
427 if (inet_ntop(AF_INET6, &a6, buf, sizeof(buf)) != 0)
428 {
429 if (ifnum != 0)
430 {
431 char ifname[IF_NAMESIZE];
432 if (if_indextoname(ifnum, ifname) != NULL)
433 {
434 asprintf(&host, "%s%%%s", buf, ifname);
435 }
436 else
437 {
438 /* ENXIO */
439 if (err != NULL) *err = SI_STATUS_EAI_FAIL;
440 return NULL;
441 }
442 }
443 else
444 {
445 host = strdup(buf);
446 }
447 }
448 }
449 }
450
451 /* Return numeric service name for NI_NUMERICSERV or if lookup failed. */
452 if (serv == NULL)
453 {
454 asprintf(&serv, "%hu", ntohs(port));
455 }
456
457 if ((host == NULL) || (serv == NULL))
458 {
459 if (err != NULL)
460 {
461 if ((flags & NI_NAMEREQD) != 0)
462 {
463 *err = SI_STATUS_EAI_NONAME;
464 }
465 else
466 {
467 *err = SI_STATUS_EAI_MEMORY;
468 }
469 }
470 }
471 else
472 {
473 out = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_NAMEINFO, 1, unused, unused, host, serv);
474 }
475
476 free(host);
477 free(serv);
478 return out;
479 }
480
481 static int
482 _gai_numericserv(const char *serv, uint16_t *port)
483 {
484 int numeric;
485 char *endptr;
486 long num;
487
488 numeric = 0;
489
490 if (serv == NULL)
491 {
492 if (port) *port = 0;
493 numeric = 1;
494 }
495 else
496 {
497 num = strtol(serv, &endptr, 10);
498 if ((serv[0] != '\0') && (*endptr == '\0') && (num >= 0) && (num <= UINT16_MAX))
499 {
500 numeric = 1;
501 if (port != NULL) *port = (uint16_t)num;
502 }
503 }
504
505 return numeric;
506 }
507
508 __private_extern__ int
509 _gai_serv_to_port(const char *serv, uint32_t proto, uint16_t *port)
510 {
511 si_item_t *item;
512 struct servent *s;
513 const char *protoname = NULL;
514
515 if (_gai_numericserv(serv, port)) return 0;
516
517 if (proto == IPPROTO_UDP) protoname = "udp";
518 if (proto == IPPROTO_TCP) protoname = "tcp";
519
520 item = si_service_byname(si_search(), serv, protoname);
521 if (item == NULL) return -1;
522
523 s = (struct servent *)((uintptr_t)item + sizeof(si_item_t));
524 if (port) *port = ntohs(s->s_port);
525 si_item_release(item);
526
527 return 0;
528 }
529
530 __private_extern__ si_item_t *
531 si_addrinfo_v4(si_mod_t *si, int32_t flags, int32_t sock, int32_t proto, uint16_t port, struct in_addr *addr, uint16_t iface, const char *cname)
532 {
533 socket_data_t sockdata;
534 struct sockaddr_in *sa;
535 int32_t len, v32;
536 uint64_t unused;
537
538 unused = 0;
539 len = sizeof(struct sockaddr_in);
540 memset(&sockdata, 0, sizeof(socket_data_t));
541 sa = (struct sockaddr_in *)&sockdata;
542
543 sa->sin_len = len;
544 sa->sin_family = AF_INET;
545 sa->sin_port = htons(port);
546 memcpy(&sa->sin_addr, addr, sizeof(sa->sin_addr));
547
548 /* Kludge: Jam the interface number into sin_zero (4 bytes). */
549 v32 = iface;
550 memmove(sa->sin_zero, &v32, sizeof(uint32_t));
551
552 return (si_item_t *)LI_ils_create("L448844444Ss", (unsigned long)si, CATEGORY_ADDRINFO, 1, unused, unused, flags, AF_INET, sock, proto, len, sockdata, cname);
553 }
554
555 __private_extern__ si_item_t *
556 si_addrinfo_v6(si_mod_t *si, int32_t flags, int32_t sock, int32_t proto, uint16_t port, struct in6_addr *addr, uint16_t iface, const char *cname)
557 {
558 socket_data_t sockdata;
559 struct sockaddr_in6 *sa;
560 int32_t len;
561 uint64_t unused;
562
563 unused = 0;
564 len = sizeof(struct sockaddr_in6);
565 memset(&sockdata, 0, sizeof(socket_data_t));
566 sa = (struct sockaddr_in6 *)&sockdata;
567
568 sa->sin6_len = len;
569 sa->sin6_family = AF_INET6;
570 sa->sin6_port = htons(port);
571 memcpy(&sa->sin6_addr, addr, sizeof(sa->sin6_addr));
572
573 /* sin6_scope_id is in host byte order */
574 sa->sin6_scope_id = iface;
575
576 if (IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
577 {
578 /* check for embedded scopeid */
579 uint16_t esid = ntohs(sa->sin6_addr.__u6_addr.__u6_addr16[1]);
580 if (esid != 0)
581 {
582 sa->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
583 if (iface == 0) sa->sin6_scope_id = esid;
584 }
585 }
586
587 return (si_item_t *)LI_ils_create("L448844444Ss", (unsigned long)si, CATEGORY_ADDRINFO, 1, unused, unused, flags, AF_INET6, sock, proto, len, sockdata, cname);
588 }
589
590 __private_extern__ si_list_t *
591 si_addrinfo_list(si_mod_t *si, int socktype, int proto, struct in_addr *a4, struct in6_addr *a6, int port, int scopeid, const char *cname4, const char *cname6)
592 {
593 si_item_t *item = NULL;
594 si_list_t *out4 = NULL, *out6 = NULL;
595 if (a4 != NULL)
596 {
597 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
598 {
599 item = si_addrinfo_v4(si, 0, SOCK_DGRAM, IPPROTO_UDP, port, a4, 0, cname4);
600 out4 = si_list_add(out4, item);
601 si_item_release(item);
602 }
603
604 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
605 {
606 item = si_addrinfo_v4(si, 0, SOCK_STREAM, IPPROTO_TCP, port, a4, 0, cname4);
607 out4 = si_list_add(out4, item);
608 si_item_release(item);
609 }
610
611 if (proto == IPPROTO_ICMP)
612 {
613 item = si_addrinfo_v4(si, 0, SOCK_RAW, IPPROTO_ICMP, port, a4, 0, cname4);
614 out4 = si_list_add(out4, item);
615 si_item_release(item);
616 }
617 }
618
619 if (a6 != NULL)
620 {
621 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
622 {
623 item = si_addrinfo_v6(si, 0, SOCK_DGRAM, IPPROTO_UDP, port, a6, scopeid, cname6);
624 out6 = si_list_add(out6, item);
625 si_item_release(item);
626 }
627
628 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
629 {
630 item = si_addrinfo_v6(si, 0, SOCK_STREAM, IPPROTO_TCP, port, a6, scopeid, cname6);
631 out6 = si_list_add(out6, item);
632 si_item_release(item);
633 }
634
635 if (proto == IPPROTO_ICMPV6)
636 {
637 item = si_addrinfo_v6(si, 0, SOCK_RAW, IPPROTO_ICMPV6, port, a6, scopeid, cname6);
638 out6 = si_list_add(out6, item);
639 si_item_release(item);
640 }
641 }
642
643 out6 = si_list_concat(out6, out4);
644 si_list_release(out4);
645
646 return out6;
647 }
648
649 /*
650 * _gai_numerichost
651 * Determines whether the given host name is a numeric IPv4 or IPv6 address,
652 * based on the address family input value. If the input addres family is
653 * unspecified, a more specific value will be provided on output if possible.
654 * Returns 1 if host name is numeric or 0 if not, or -1 on error.
655 */
656 static int
657 _gai_numerichost(const char* nodename, uint32_t *family, int flags, struct in_addr *a4, struct in6_addr *a6)
658 {
659 int numerichost, passive;
660
661 numerichost = 0;
662
663 if (nodename == NULL)
664 {
665 /* return loopback or passive addresses */
666 passive = (flags & AI_PASSIVE);
667
668 if ((*family == AF_UNSPEC) || (*family == AF_INET))
669 {
670 if (passive) a4->s_addr = 0;
671 else a4->s_addr = htonl(INADDR_LOOPBACK);
672 }
673
674 if ((*family == AF_UNSPEC) || (*family == AF_INET6))
675 {
676 memset(a6, 0, sizeof(*a6));
677 if (!passive) a6->__u6_addr.__u6_addr32[3] = htonl(1);
678 }
679
680 numerichost = 1;
681 }
682 else
683 {
684 /* numeric IPv4 host valid for AF_UNSPEC and AF_INET */
685 numerichost = inet_pton(AF_INET, nodename, a4);
686 if (numerichost == 1)
687 {
688 if (*family == AF_UNSPEC) *family = AF_INET;
689 else if (*family == AF_INET6) numerichost = -1;
690 return numerichost;
691 }
692
693 /* numeric IPv6 host valid for AF_UNSPEC and AF_INET6 */
694 numerichost = inet_pton(AF_INET6, nodename, a6);
695 if (numerichost == 1)
696 {
697 if (*family == AF_UNSPEC) *family = AF_INET6;
698 else if (*family == AF_INET) numerichost = -1;
699 return numerichost;
700 }
701 }
702
703 return numerichost;
704 }
705
706 /* si_addrinfo_list_from_hostent
707 * Returns an addrinfo list from IPv4 and IPv6 hostent entries
708 */
709 __private_extern__ si_list_t *
710 si_addrinfo_list_from_hostent(si_mod_t *si, uint32_t socktype, uint32_t proto, uint16_t port, uint16_t scope, const struct hostent *h4, const struct hostent *h6)
711 {
712 int i;
713 si_list_t *out = NULL;
714 si_list_t *list;
715
716 if ((h6 != NULL) && (h6->h_addr_list != NULL))
717 {
718 for (i = 0; h6->h_addr_list[i] != NULL; i++)
719 {
720 struct in6_addr a6;
721 memcpy(&a6, h6->h_addr_list[i], h6->h_length);
722 list = si_addrinfo_list(si, socktype, proto, NULL, &a6, port, scope, NULL, h6->h_name);
723 out = si_list_concat(out, list);
724 si_list_release(list);
725 }
726 }
727
728 if ((h4 != NULL) && (h4->h_addr_list != NULL))
729 {
730 for (i = 0; h4->h_addr_list[i] != NULL; i++)
731 {
732 struct in_addr a4;
733 memcpy(&a4, h4->h_addr_list[i], h4->h_length);
734 list = si_addrinfo_list(si, socktype, proto, &a4, NULL, port, 0, h4->h_name, NULL);
735 out = si_list_concat(out, list);
736 si_list_release(list);
737 }
738 }
739
740 return out;
741 }
742
743 int
744 _gai_sa_dst_compare(const struct sockaddr *a, const struct sockaddr *b, int unused)
745 {
746 int ta, tb, posn_a, posn_b;
747
748 ta = _si_net_addr_type(a);
749 tb = _si_net_addr_type(b);
750
751 posn_a = 2;
752 posn_b = 2;
753
754 if (ta & NET_TYPE_V4) posn_a = 1;
755 if (tb & NET_TYPE_V4) posn_b = 1;
756
757 if (_si_net_type_is_IPv6_globably_reachable_non_transitional(ta)) posn_a = 0;
758 if (_si_net_type_is_IPv6_globably_reachable_non_transitional(tb)) posn_b = 0;
759
760 return (posn_a - posn_b);
761 }
762
763 int
764 _gai_addr_sort(void *thunk, const void *a, const void *b)
765 {
766 si_item_t **ia, **ib;
767 si_addrinfo_t *p, *q;
768 struct sockaddr *sp, *sq;
769 uint32_t *v4first = (uint32_t *)thunk;
770
771 ia = (si_item_t **)a;
772 ib = (si_item_t **)b;
773
774 p = (si_addrinfo_t *)((uintptr_t)*ia + sizeof(si_item_t));
775 q = (si_addrinfo_t *)((uintptr_t)*ib + sizeof(si_item_t));
776
777 sp = (struct sockaddr *)p->ai_addr.x;
778 sq = (struct sockaddr *)q->ai_addr.x;
779
780 if (*v4first == 1)
781 {
782 if (sp->sa_family == sq->sa_family) return 0;
783 if (sp->sa_family == AF_INET) return -1;
784 return 1;
785 }
786
787 return _gai_sa_dst_compare(sp, sq, 0);
788 }
789
790 static si_list_t *
791 _gai_sort_list(si_list_t *in)
792 {
793 uint32_t v4first;
794 config_stats_t x;
795
796 if (in == NULL) return NULL;
797
798 memset(&x, 0, sizeof(config_stats_t));
799 if (_si_netconfig(&x) < 0) return in;
800
801 v4first = 1;
802 if (x.gai_v6_preferred != 0) v4first = 0;
803
804 qsort_r(&in->entry[0], in->count, sizeof(si_item_t *), (void *)&v4first, _gai_addr_sort);
805 return in;
806 }
807
808 /* _gai_simple
809 * Simple lookup via gethostbyname2(3) mechanism.
810 */
811 __private_extern__ si_list_t *
812 _gai_simple(si_mod_t *si, const void *nodeptr, const void *servptr, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
813 {
814 si_item_t *h4_item = NULL, *h6_item = NULL;
815 struct hostent *h4 = NULL, *h6 = NULL;
816 si_list_t *out = NULL;
817 uint16_t port;
818
819 if ((flags & AI_NUMERICSERV) != 0)
820 {
821 port = *(uint16_t*)servptr;
822 }
823 else
824 {
825 if (_gai_serv_to_port(servptr, proto, &port) != 0)
826 {
827 if (err) *err = SI_STATUS_EAI_NONAME;
828 return NULL;
829 }
830 }
831
832 if ((flags & AI_NUMERICHOST) != 0)
833 {
834 if (family == AF_INET)
835 {
836 h4_item = si_host_byaddr(si, nodeptr, AF_INET, interface, NULL);
837 }
838 else if (family == AF_INET6)
839 {
840 h6_item = si_host_byaddr(si, nodeptr, AF_INET6, interface, NULL);
841 }
842 }
843 else
844 {
845 if ((family == AF_INET) || (family == AF_UNSPEC))
846 {
847 h4_item = si_host_byname(si, nodeptr, AF_INET, interface, NULL);
848 }
849
850 if ((family == AF_INET6) || (family == AF_UNSPEC))
851 {
852 h6_item = si_host_byname(si, nodeptr, AF_INET6, interface, NULL);
853 }
854 }
855
856 if (h4_item != NULL)
857 {
858 h4 = (struct hostent *)((uintptr_t)h4_item + sizeof(si_item_t));
859 }
860
861 if (h6_item != NULL)
862 {
863 h6 = (struct hostent *)((uintptr_t)h6_item + sizeof(si_item_t));
864 }
865
866 out = si_addrinfo_list_from_hostent(si, socktype, proto, port, 0, h4, h6);
867 si_item_release(h4_item);
868 si_item_release(h6_item);
869
870 return _gai_sort_list(out);
871 }
872
873 __private_extern__ si_list_t *
874 si_srv_byname(si_mod_t *si, const char *qname, const char *interface, uint32_t *err)
875 {
876 if (si == NULL) return 0;
877 if (si->sim_srv_byname == NULL) return 0;
878
879 return si->sim_srv_byname(si, qname, interface, err);
880 }
881
882 __private_extern__ int
883 si_wants_addrinfo(si_mod_t *si)
884 {
885 if (si == NULL) return 0;
886 if (si->sim_addrinfo == NULL) return 0;
887 if (si->sim_wants_addrinfo == NULL) return 0;
888
889 return si->sim_wants_addrinfo(si);
890 }
891
892 static si_list_t *
893 _gai_srv(si_mod_t *si, const char *node, const char *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
894 {
895 int i;
896 char *qname;
897 si_srv_t *srv;
898 si_item_t *item;
899
900 si_list_t *list = NULL;
901 si_list_t *result = NULL;
902
903 /* Minimum SRV priority is zero. Start below that. */
904 int lastprio = -1;
905 int currprio;
906
907 if (node == NULL || serv == NULL) return NULL;
908
909 asprintf(&qname, "%s.%s", serv, node);
910 list = si_srv_byname(si, qname, interface, err);
911 free(qname);
912
913 /* Iterate the SRV records starting at lowest priority and attempt to
914 * lookup the target host name. Returns the first successful lookup.
915 * It's an O(n^2) algorithm but data sets are small (less than 100) and
916 * sorting overhead is dwarfed by network I/O for each element.
917 */
918 while (list != NULL && result == NULL)
919 {
920 /* Find the next lowest priority level. */
921 /* Maximum SRV priority is UINT16_MAX. Start above that. */
922 currprio = INT_MAX;
923
924 for (i = 0; i < list->count; ++i)
925 {
926 item = list->entry[i];
927 srv = (si_srv_t *)((uintptr_t)item + sizeof(si_item_t));
928
929 if (srv->priority > lastprio && srv->priority < currprio)
930 {
931 currprio = srv->priority;
932 }
933 }
934
935 if (currprio == INT_MAX)
936 {
937 /* All priorities have been evaluated. Done. */
938 break;
939 }
940 else
941 {
942 lastprio = currprio;
943 }
944
945 /* Lookup hosts at the current priority level. Return first match. */
946 for (i = 0; i < list->count; ++i)
947 {
948 item = list->entry[i];
949 srv = (si_srv_t *)((uintptr_t)item + sizeof(si_item_t));
950
951 if (srv->priority == currprio)
952 {
953 /* So that _gai_simple expects an integer service. */
954 flags |= AI_NUMERICSERV;
955
956 result = _gai_simple(si, srv->target, &srv->port, family, socktype, proto, flags, interface, err);
957 if (result)
958 {
959 break;
960 }
961 }
962 }
963 }
964
965 if (list != NULL)
966 {
967 si_list_release(list);
968 }
969
970 return result;
971 }
972
973 __private_extern__ si_list_t *
974 si_addrinfo(si_mod_t *si, const char *node, const char *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
975 {
976 int numerichost, numericserv = 0;
977 const void *nodeptr = NULL, *servptr = NULL;
978 uint16_t port;
979 struct in_addr a4, *p4;
980 struct in6_addr a6, *p6;
981 const char *cname;
982 si_list_t *out;
983
984 if (err != NULL) *err = SI_STATUS_NO_ERROR;
985
986 if (si == NULL)
987 {
988 if (err != NULL) *err = SI_STATUS_EAI_FAIL;
989 return NULL;
990 }
991
992 /* treat empty strings as NULL */
993 if ((node != NULL) && (node[0] == '\0')) node = NULL;
994 if ((serv != NULL) && (serv[0] == '\0')) serv = NULL;
995
996 /* make sure we have a query */
997 if ((node == NULL) && (serv == NULL))
998 {
999 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
1000 return NULL;
1001 }
1002
1003 /* verify family is supported */
1004 switch (family)
1005 {
1006 case AF_UNSPEC:
1007 case AF_INET:
1008 case AF_INET6:
1009 break;
1010 default:
1011 if (err != NULL) *err = SI_STATUS_EAI_FAMILY;
1012 return NULL;
1013 }
1014
1015 /* verify socket type is supported */
1016 switch (socktype)
1017 {
1018 case SOCK_UNSPEC:
1019 case SOCK_RAW:
1020 case SOCK_DGRAM:
1021 case SOCK_STREAM:
1022 break;
1023 default:
1024 if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
1025 return NULL;
1026 }
1027
1028 /* verify protocol is supported */
1029 switch (proto)
1030 {
1031 case IPPROTO_UNSPEC:
1032 case IPPROTO_UDP:
1033 case IPPROTO_TCP:
1034 case IPPROTO_ICMP:
1035 case IPPROTO_ICMPV6:
1036 break;
1037 default:
1038 if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
1039 return NULL;
1040 }
1041
1042 /* verify socket type compatible with protocol */
1043 if (((socktype == SOCK_DGRAM) && (proto != IPPROTO_UNSPEC) && (proto != IPPROTO_UDP)) || ((socktype == SOCK_STREAM) && (proto != IPPROTO_UNSPEC) && (proto != IPPROTO_TCP)))
1044 {
1045 if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
1046 return NULL;
1047 }
1048
1049 memset(&a4, 0, sizeof(struct in_addr));
1050 memset(&a6, 0, sizeof(struct in6_addr));
1051
1052 /* determine the protocol if possible */
1053 if ((proto == IPPROTO_UNSPEC) && (socktype == SOCK_DGRAM)) proto = IPPROTO_UDP;
1054 if ((proto == IPPROTO_UNSPEC) && (socktype == SOCK_STREAM)) proto = IPPROTO_TCP;
1055
1056 port = 0;
1057
1058 if ((flags & AI_SRV) != 0)
1059 {
1060 /* AI_SRV SPI */
1061 out = _gai_srv(si, node, serv, family, socktype, proto, flags, interface, err);
1062 return _gai_sort_list(out);
1063 }
1064 else
1065 {
1066 /* Usual service lookup */
1067 numericserv = _gai_numericserv(serv, &port);
1068 }
1069
1070 if ((flags & AI_NUMERICSERV) && (numericserv == 0))
1071 {
1072 /* FreeBSD returns SI_STATUS_EAI_SERVICE */
1073 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
1074 return NULL;
1075 }
1076
1077 if (serv != NULL)
1078 {
1079 if (numericserv == 1)
1080 {
1081 flags |= AI_NUMERICSERV;
1082 servptr = &port;
1083 }
1084 else
1085 {
1086 servptr = serv;
1087 }
1088 }
1089
1090 numerichost = _gai_numerichost(node, &family, flags, &a4, &a6);
1091 if ((numerichost == -1) || ((flags & AI_NUMERICHOST) && (numerichost == 0)))
1092 {
1093 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
1094 return NULL;
1095 }
1096
1097 if (numerichost == 1)
1098 {
1099 flags |= AI_NUMERICHOST;
1100 if (family == AF_INET)
1101 {
1102 nodeptr = &a4;
1103 }
1104 else if (family == AF_INET6)
1105 {
1106 nodeptr = &a6;
1107 }
1108 }
1109 else
1110 {
1111 nodeptr = node;
1112 }
1113
1114 if ((numerichost == 1) && (numericserv == 0))
1115 {
1116 /* only service lookup needed. convert to port and perform a trivial getaddrinfo */
1117 if (_gai_serv_to_port(serv, proto, &port) != 0)
1118 {
1119 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
1120 return NULL;
1121 }
1122 else
1123 {
1124 flags |= AI_NUMERICSERV;
1125 servptr = &port;
1126 numericserv = 1;
1127 }
1128 }
1129
1130 if ((numerichost == 1) && (numericserv == 1))
1131 {
1132 p4 = &a4;
1133 p6 = &a6;
1134 cname = NULL;
1135
1136 if (family == AF_INET) p6 = NULL;
1137 if (family == AF_INET6) p4 = NULL;
1138 if (node == NULL) cname = "localhost";
1139
1140 /* handle trivial questions */
1141 out = si_addrinfo_list(si, socktype, proto, p4, p6, port, 0, cname, cname);
1142 return _gai_sort_list(out);
1143 }
1144 else if ((si->sim_wants_addrinfo != NULL) && si->sim_wants_addrinfo(si))
1145 {
1146 /* or let the current module handle the host lookups intelligently */
1147 out = si->sim_addrinfo(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
1148 return _gai_sort_list(out);
1149 }
1150
1151 /* fall back to a default path */
1152 out = _gai_simple(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
1153 return _gai_sort_list(out);
1154 }
1155
1156 static struct addrinfo *
1157 si_item_to_addrinfo(si_item_t *item)
1158 {
1159 si_addrinfo_t *a;
1160 struct addrinfo *out;
1161
1162 if (item == NULL) return NULL;
1163
1164 a = (si_addrinfo_t *)((uintptr_t)item + sizeof(si_item_t));
1165
1166 out = (struct addrinfo *)calloc(1, sizeof(struct addrinfo));
1167 if (out == NULL) return NULL;
1168
1169 out->ai_flags = a->ai_flags;
1170 out->ai_family = a->ai_family;
1171 out->ai_socktype = a->ai_socktype;
1172 out->ai_protocol = a->ai_protocol;
1173 out->ai_addrlen = a->ai_addrlen;
1174
1175 out->ai_addr = (struct sockaddr *)calloc(1, out->ai_addrlen);
1176 if (out->ai_addr == NULL)
1177 {
1178 free(out);
1179 return NULL;
1180 }
1181
1182 memcpy(out->ai_addr, a->ai_addr.x, out->ai_addrlen);
1183
1184 if (a->ai_canonname != NULL)
1185 {
1186 out->ai_canonname = strdup(a->ai_canonname);
1187 if (out->ai_canonname == NULL)
1188 {
1189 free(out);
1190 return NULL;
1191 }
1192 }
1193
1194 return out;
1195 }
1196
1197 __private_extern__ struct addrinfo *
1198 si_list_to_addrinfo(si_list_t *list)
1199 {
1200 struct addrinfo *tail, *out;
1201 int i;
1202
1203 if (list == NULL) return NULL;
1204 if (list->count == 0) return NULL;
1205
1206 i = list->count - 1;
1207
1208 out = si_item_to_addrinfo(list->entry[i]);
1209 tail = out;
1210
1211 for (i--; i >= 0; i--)
1212 {
1213 out = si_item_to_addrinfo(list->entry[i]);
1214 if (out == NULL)
1215 {
1216 freeaddrinfo(tail);
1217 return NULL;
1218 }
1219
1220 out->ai_next = tail;
1221 tail = out;
1222 }
1223
1224 return out;
1225 }
1226
1227 /* getipnodebyname */
1228
1229 static si_item_t *
1230 make_hostent(si_mod_t *si, const char *name, struct in_addr addr)
1231 {
1232 char *addrs[2];
1233 char *aliases[1];
1234 uint64_t unused;
1235
1236 if (name == NULL) return NULL;
1237
1238 unused = 0;
1239
1240 addrs[0] = (char *)&(addr.s_addr);
1241 addrs[1] = NULL;
1242 aliases[0] = NULL;
1243
1244 return (si_item_t *)LI_ils_create("L4488s*44a", (unsigned long)si, CATEGORY_HOST_IPV4, 1, unused, unused, name, aliases, AF_INET, IPV4_ADDR_LEN, addrs);
1245 }
1246
1247 static si_item_t *
1248 make_hostent6(si_mod_t *si, const char *name, struct in6_addr addr)
1249 {
1250 char *addrs[2];
1251 char *aliases[1];
1252 uint64_t unused;
1253
1254 if (name == NULL) return NULL;
1255
1256 unused = 0;
1257
1258 addrs[0] = (char *)&(addr.__u6_addr.__u6_addr32[0]);
1259 addrs[1] = NULL;
1260 aliases[0] = NULL;
1261
1262 return (si_item_t *)LI_ils_create("L4488s*44c", (unsigned long)si, CATEGORY_HOST_IPV6, 1, unused, unused, name, aliases, AF_INET6, IPV6_ADDR_LEN, addrs);
1263 }
1264
1265 static char *
1266 lower_case(const char *s)
1267 {
1268 int i;
1269 char *t;
1270
1271 if (s == NULL) return NULL;
1272
1273 t = malloc(strlen(s) + 1);
1274
1275 for (i = 0; s[i] != '\0'; i++)
1276 {
1277 if ((s[i] >= 'A') && (s[i] <= 'Z')) t[i] = s[i] + 32;
1278 else t[i] = s[i];
1279 }
1280
1281 t[i] = '\0';
1282
1283 return t;
1284 }
1285
1286 static int
1287 merge_alias(const char *name, build_hostent_t *h)
1288 {
1289 int i;
1290
1291 if (name == NULL) return 0;
1292 if (h == NULL) return 0;
1293
1294 if ((h->host.h_name != NULL) && (string_equal(name, h->host.h_name))) return 0;
1295
1296 for (i = 0; i < h->alias_count; i++)
1297 {
1298 if (string_equal(name, h->host.h_aliases[i])) return 0;
1299 }
1300
1301 if (h->alias_count == 0) h->host.h_aliases = (char **)calloc(2, sizeof(char *));
1302 else h->host.h_aliases = (char **)reallocf(h->host.h_aliases, (h->alias_count + 2) * sizeof(char *));
1303
1304 if (h->host.h_aliases == NULL)
1305 {
1306 h->alias_count = 0;
1307 return -1;
1308 }
1309
1310 h->host.h_aliases[h->alias_count] = lower_case(name);
1311 h->alias_count++;
1312 h->host.h_aliases[h->alias_count] = NULL;
1313
1314 return 0;
1315 }
1316
1317 static int
1318 append_addr(const char *addr, uint32_t len, build_hostent_t *h)
1319 {
1320 if (addr == NULL) return 0;
1321 if (h == NULL) return 0;
1322
1323 if (h->addr_count == 0) h->host.h_addr_list = (char **)calloc(2, sizeof(char *));
1324 else h->host.h_addr_list = (char **)reallocf(h->host.h_addr_list, (h->addr_count + 2) * sizeof(char *));
1325
1326 if (h->host.h_addr_list == NULL)
1327 {
1328 h->addr_count = 0;
1329 return -1;
1330 }
1331
1332 h->host.h_addr_list[h->addr_count] = malloc(len);
1333 if (h->host.h_addr_list[h->addr_count] == NULL) return -1;
1334
1335 memcpy(h->host.h_addr_list[h->addr_count], addr, len);
1336 h->addr_count++;
1337 h->host.h_addr_list[h->addr_count] = NULL;
1338
1339 return 0;
1340 }
1341
1342 static void
1343 free_build_hostent(build_hostent_t *h)
1344 {
1345 uint32_t i;
1346 char **aliases;
1347
1348 if (h == NULL) return;
1349
1350 if (h->host.h_name != NULL) free(h->host.h_name);
1351 h->host.h_name = NULL;
1352
1353 aliases = h->host.h_aliases;
1354 if (aliases != NULL)
1355 {
1356 while (*aliases != NULL) free(*aliases++);
1357 free(h->host.h_aliases);
1358 }
1359
1360 h->host.h_aliases = NULL;
1361
1362 if (h->host.h_addr_list != NULL)
1363 {
1364 for (i = 0; h->host.h_addr_list[i] != NULL; i++) free(h->host.h_addr_list[i]);
1365 free(h->host.h_addr_list);
1366 }
1367
1368 h->host.h_addr_list = NULL;
1369 free(h);
1370 }
1371
1372 __private_extern__ si_item_t *
1373 si_ipnode_byname(si_mod_t *si, const char *name, int family, int flags, const char *interface, uint32_t *err)
1374 {
1375 int i, status, want;
1376 struct in_addr addr4;
1377 struct in6_addr addr6;
1378 si_item_t *item4, *item6;
1379 build_hostent_t *out;
1380 struct hostent *h;
1381 uint64_t unused;
1382 config_stats_t ifstats;
1383
1384 memset(&addr4, 0, sizeof(struct in_addr));
1385 memset(&addr6, 0, sizeof(struct in6_addr));
1386
1387 if (err != NULL) *err = 0;
1388
1389 if (family == AF_INET)
1390 {
1391 status = inet_aton(name, &addr4);
1392 if (status == 1)
1393 {
1394 /* create a host entry */
1395 item4 = make_hostent(si, name, addr4);
1396 if (item4 == NULL)
1397 {
1398 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1399 return NULL;
1400 }
1401
1402 return item4;
1403 }
1404 }
1405 else if (family == AF_INET6)
1406 {
1407 status = inet_pton(family, name, &addr6);
1408 if (status == 1)
1409 {
1410 /* create a host entry */
1411 item6 = make_hostent6(si, name, addr6);
1412 if (item6 == NULL)
1413 {
1414 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1415 return NULL;
1416 }
1417
1418 return item6;
1419 }
1420
1421 status = inet_aton(name, &addr4);
1422 if (status == 1)
1423 {
1424 if (!(flags & (AI_V4MAPPED | AI_V4MAPPED_CFG)))
1425 {
1426 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1427 return NULL;
1428 }
1429
1430 addr6.__u6_addr.__u6_addr32[0] = 0x00000000;
1431 addr6.__u6_addr.__u6_addr32[1] = 0x00000000;
1432 addr6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
1433 memmove(&(addr6.__u6_addr.__u6_addr32[3]), &(addr4.s_addr), IPV4_ADDR_LEN);
1434
1435 /* create a host entry */
1436 item6 = make_hostent6(si, name, addr6);
1437 if (item6 == NULL)
1438 {
1439 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1440 return NULL;
1441 }
1442
1443 return item6;
1444 }
1445 }
1446 else
1447 {
1448 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1449 return NULL;
1450 }
1451
1452 /*
1453 * IF AI_ADDRCONFIG is set, we need to know what interface flavors we really have.
1454 */
1455
1456 memset(&ifstats, 0, sizeof(config_stats_t));
1457
1458 if (flags & AI_ADDRCONFIG)
1459 {
1460 if (_si_netconfig(&ifstats) < 0)
1461 {
1462 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1463 return NULL;
1464 }
1465
1466 /* Bail out if there are no interfaces */
1467 if ((ifstats.v4_count == 0) && (ifstats.v6_count == 0))
1468 {
1469 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1470 return NULL;
1471 }
1472 }
1473
1474 /*
1475 * Figure out what we want.
1476 * If user asked for AF_INET, we only want V4 addresses.
1477 */
1478 want = WANT_A4_ONLY;
1479
1480 if (family == AF_INET)
1481 {
1482 if ((flags & AI_ADDRCONFIG) && (ifstats.v4_count == 0))
1483 {
1484 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1485 return NULL;
1486 }
1487 }
1488 else if (family == AF_INET6)
1489 {
1490 /* family == AF_INET6 */
1491 want = WANT_A6_ONLY;
1492
1493 if (flags & (AI_V4MAPPED | AI_V4MAPPED_CFG))
1494 {
1495 if (flags & AI_ALL)
1496 {
1497 want = WANT_A6_PLUS_MAPPED_A4;
1498 }
1499 else
1500 {
1501 want = WANT_A6_OR_MAPPED_A4_IF_NO_A6;
1502 }
1503 }
1504 else
1505 {
1506 if ((flags & AI_ADDRCONFIG) && (ifstats.v6_count == 0))
1507 {
1508 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1509 return NULL;
1510 }
1511 }
1512 }
1513 else
1514 {
1515 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1516 return NULL;
1517 }
1518
1519 item6 = NULL;
1520 item4 = NULL;
1521
1522 /* fetch IPv6 data if required */
1523 if ((want == WANT_A6_ONLY) || (want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) || (want == WANT_A6_PLUS_MAPPED_A4))
1524 {
1525 item6 = si_host_byname(si, name, AF_INET6, interface, (uint32_t *)err);
1526 }
1527
1528 /* fetch IPv4 data if required */
1529 if ((want == WANT_A4_ONLY) || (want == WANT_A6_PLUS_MAPPED_A4) || ((want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) && (item6 == NULL)))
1530 {
1531 item4 = si_host_byname(si, name, AF_INET, interface, (uint32_t *)err);
1532 }
1533
1534 if (want == WANT_A4_ONLY)
1535 {
1536 si_item_release(item6);
1537 if ((item4 == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1538 return item4;
1539 }
1540
1541 if (want == WANT_A6_ONLY)
1542 {
1543 si_item_release(item4);
1544 if ((item6 == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1545 return item6;
1546 }
1547
1548 if ((item6 == NULL) && (item4 == NULL))
1549 {
1550 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1551 return NULL;
1552 }
1553
1554 /* output item will have IPv6 + mapped IPv4 addresses */
1555
1556 out = (build_hostent_t *)calloc(1, sizeof(build_hostent_t));
1557 if (out == NULL)
1558 {
1559 si_item_release(item4);
1560 si_item_release(item6);
1561 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1562 return NULL;
1563 }
1564
1565 if (item4 != NULL)
1566 {
1567 h = (struct hostent *)((uintptr_t)item4 + sizeof(si_item_t));
1568
1569 out->host.h_name = lower_case(h->h_name);
1570
1571 if (h->h_aliases != NULL)
1572 {
1573 for (i = 0; h->h_aliases[i] != NULL; i++) merge_alias(h->h_aliases[i], out);
1574 }
1575
1576 for (i = 0; h->h_addr_list[i] != 0; i++)
1577 {
1578 addr6.__u6_addr.__u6_addr32[0] = 0x00000000;
1579 addr6.__u6_addr.__u6_addr32[1] = 0x00000000;
1580 addr6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
1581 memmove(&(addr6.__u6_addr.__u6_addr32[3]), h->h_addr_list[i], IPV4_ADDR_LEN);
1582 append_addr((const char *)&addr6, IPV6_ADDR_LEN, out);
1583 }
1584 }
1585
1586 if (item6 != NULL)
1587 {
1588 h = (struct hostent *)((uintptr_t)item6 + sizeof(si_item_t));
1589
1590 if (out->host.h_name == NULL) out->host.h_name = lower_case(h->h_name);
1591
1592 if (h->h_aliases != NULL)
1593 {
1594 for (i = 0; h->h_aliases[i] != NULL; i++) merge_alias(h->h_aliases[i], out);
1595 }
1596
1597 for (i = 0; h->h_addr_list[i] != 0; i++) append_addr(h->h_addr_list[i], IPV6_ADDR_LEN, out);
1598 }
1599
1600 si_item_release(item4);
1601 si_item_release(item6);
1602
1603 unused = 0;
1604
1605 item6 = (si_item_t *)LI_ils_create("L4488s*44c", (unsigned long)si, CATEGORY_HOST_IPV6, 1, unused, unused, out->host.h_name, out->host.h_aliases, AF_INET6, IPV6_ADDR_LEN, out->host.h_addr_list);
1606
1607 free_build_hostent(out);
1608
1609 return item6;
1610 }