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