Libinfo-324.1.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, 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, err);
199 }
200 #endif
201 si_item_t *item = si_host_byaddr(si, addr, lookup_sa->sa_family, 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 static 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, 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, NULL);
605 }
606 else if (family == AF_INET6)
607 {
608 h6_item = si_host_byaddr(si, nodeptr, AF_INET6, 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, NULL);
616 }
617 #if !TARGET_OS_EMBEDDED
618 if ((family == AF_INET6) || (family == AF_UNSPEC))
619 {
620 h6_item = si_host_byname(si, nodeptr, AF_INET6, NULL);
621 }
622 #endif /* !TARGET_OS_EMBEDDED */
623 }
624
625 if (h4_item != NULL)
626 {
627 h4 = (struct hostent *)((uintptr_t)h4_item + sizeof(si_item_t));
628 }
629
630 if (h6_item != NULL)
631 {
632 h6 = (struct hostent *)((uintptr_t)h6_item + sizeof(si_item_t));
633 }
634
635 out = si_addrinfo_list_from_hostent(si, socktype, proto, port, 0, h4, h6);
636 si_item_release(h4_item);
637 si_item_release(h6_item);
638
639 return out;
640 }
641
642 __private_extern__ si_list_t *
643 si_srv_byname(si_mod_t *si, const char *qname, uint32_t *err)
644 {
645 if (si == NULL) return 0;
646 if (si->sim_srv_byname == NULL) return 0;
647
648 return si->sim_srv_byname(si, qname, err);
649 }
650
651 __private_extern__ int
652 si_wants_addrinfo(si_mod_t *si)
653 {
654 if (si == NULL) return 0;
655 if (si->sim_addrinfo == NULL) return 0;
656 if (si->sim_wants_addrinfo == NULL) return 0;
657
658 return si->sim_wants_addrinfo(si);
659 }
660
661 static si_list_t *
662 _gai_srv(si_mod_t *si, const char *node, const char *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, uint32_t *err)
663 {
664 int i;
665 char *qname;
666 si_srv_t *srv;
667 si_item_t *item;
668
669 si_list_t *list = NULL;
670 si_list_t *result = NULL;
671
672 /* Minimum SRV priority is zero. Start below that. */
673 int lastprio = -1;
674 int currprio;
675
676 if (node == NULL || serv == NULL) return NULL;
677
678 asprintf(&qname, "%s.%s", serv, node);
679 list = si_srv_byname(si, qname, err);
680 free(qname);
681
682 /* Iterate the SRV records starting at lowest priority and attempt to
683 * lookup the target host name. Returns the first successful lookup.
684 * It's an O(n^2) algorithm but data sets are small (less than 100) and
685 * sorting overhead is dwarfed by network I/O for each element.
686 */
687 while (list != NULL && result == NULL)
688 {
689 /* Find the next lowest priority level. */
690 /* Maximum SRV priority is UINT16_MAX. Start above that. */
691 currprio = INT_MAX;
692
693 for (i = 0; i < list->count; ++i)
694 {
695 item = list->entry[i];
696 srv = (si_srv_t *)((uintptr_t)item + sizeof(si_item_t));
697
698 if (srv->priority > lastprio && srv->priority < currprio)
699 {
700 currprio = srv->priority;
701 }
702 }
703
704 if (currprio == INT_MAX)
705 {
706 /* All priorities have been evaluated. Done. */
707 break;
708 }
709 else
710 {
711 lastprio = currprio;
712 }
713
714 /* Lookup hosts at the current priority level. Return first match. */
715 for (i = 0; i < list->count; ++i)
716 {
717 item = list->entry[i];
718 srv = (si_srv_t *)((uintptr_t)item + sizeof(si_item_t));
719
720 if (srv->priority == currprio)
721 {
722 /* So that _gai_simple expects an integer service. */
723 flags |= AI_NUMERICSERV;
724
725 result = _gai_simple(si, srv->target, &srv->port, family, socktype, proto, flags, err);
726 if (result)
727 {
728 break;
729 }
730 }
731 }
732 }
733
734 if (list != NULL)
735 {
736 si_list_release(list);
737 }
738
739 return result;
740 }
741
742 __private_extern__ si_list_t *
743 si_addrinfo(si_mod_t *si, const char *node, const char *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, uint32_t *err)
744 {
745 int numerichost, numericserv = 0;
746 const void *nodeptr = NULL, *servptr = NULL;
747 uint16_t port;
748 struct in_addr a4, *p4;
749 struct in6_addr a6, *p6;
750 const char *cname;
751
752 if (err != NULL) *err = SI_STATUS_NO_ERROR;
753
754 if (si == NULL)
755 {
756 if (err != NULL) *err = SI_STATUS_EAI_FAIL;
757 return NULL;
758 }
759
760 /* treat empty strings as NULL */
761 if ((node != NULL) && (node[0] == '\0')) node = NULL;
762 if ((serv != NULL) && (serv[0] == '\0')) serv = NULL;
763
764 /* make sure we have a query */
765 if ((node == NULL) && (serv == NULL))
766 {
767 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
768 return NULL;
769 }
770
771 /* verify family is supported */
772 switch (family)
773 {
774 case AF_UNSPEC:
775 #if TARGET_OS_EMBEDDED
776 family = AF_INET;
777 #endif
778 case AF_INET:
779 case AF_INET6:
780 break;
781 default:
782 if (err != NULL) *err = SI_STATUS_EAI_FAMILY;
783 return NULL;
784 }
785
786 /* verify socket type is supported */
787 switch (socktype)
788 {
789 case SOCK_UNSPEC:
790 case SOCK_RAW:
791 case SOCK_DGRAM:
792 case SOCK_STREAM:
793 break;
794 default:
795 if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
796 return NULL;
797 }
798
799 /* verify protocol is supported */
800 switch (proto)
801 {
802 case IPPROTO_UNSPEC:
803 case IPPROTO_UDP:
804 case IPPROTO_TCP:
805 case IPPROTO_ICMP:
806 case IPPROTO_ICMPV6:
807 break;
808 default:
809 if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
810 return NULL;
811 }
812
813 /* verify socket type compatible with protocol */
814 if (((socktype == SOCK_DGRAM) && (proto != IPPROTO_UNSPEC) && (proto != IPPROTO_UDP)) || ((socktype == SOCK_STREAM) && (proto != IPPROTO_UNSPEC) && (proto != IPPROTO_TCP)))
815 {
816 if (err != NULL) *err = SI_STATUS_EAI_BADHINTS;
817 return NULL;
818 }
819
820 memset(&a4, 0, sizeof(struct in_addr));
821 memset(&a6, 0, sizeof(struct in6_addr));
822
823 /* determine the protocol if possible */
824 if ((proto == IPPROTO_UNSPEC) && (socktype == SOCK_DGRAM)) proto = IPPROTO_UDP;
825 if ((proto == IPPROTO_UNSPEC) && (socktype == SOCK_STREAM)) proto = IPPROTO_TCP;
826
827 port = 0;
828
829 if ((flags & AI_SRV) != 0)
830 {
831 /* AI_SRV SPI */
832 return _gai_srv(si, node, serv, family, socktype, proto, flags, err);
833 }
834 else
835 {
836 /* Usual service lookup */
837 numericserv = _gai_numericserv(serv, &port);
838 }
839
840 if ((flags & AI_NUMERICSERV) && (numericserv == 0))
841 {
842 /* FreeBSD returns SI_STATUS_EAI_SERVICE */
843 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
844 return NULL;
845 }
846
847 if (serv != NULL)
848 {
849 if (numericserv == 1)
850 {
851 flags |= AI_NUMERICSERV;
852 servptr = &port;
853 }
854 else
855 {
856 servptr = serv;
857 }
858 }
859
860 numerichost = _gai_numerichost(node, &family, flags, &a4, &a6);
861 if ((numerichost == -1) || ((flags & AI_NUMERICHOST) && (numerichost == 0)))
862 {
863 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
864 return NULL;
865 }
866
867 if (numerichost == 1)
868 {
869 flags |= AI_NUMERICHOST;
870 if (family == AF_INET)
871 {
872 nodeptr = &a4;
873 }
874 else if (family == AF_INET6)
875 {
876 nodeptr = &a6;
877 }
878 }
879 else
880 {
881 nodeptr = node;
882 }
883
884 if ((numerichost == 1) && (numericserv == 0))
885 {
886 /* only service lookup needed. convert to port and perform a trivial getaddrinfo */
887 if (_gai_serv_to_port(serv, proto, &port) != 0)
888 {
889 if (err != NULL) *err = SI_STATUS_EAI_NONAME;
890 return NULL;
891 }
892 else
893 {
894 flags |= AI_NUMERICSERV;
895 servptr = &port;
896 numericserv = 1;
897 }
898 }
899
900 if ((numerichost == 1) && (numericserv == 1))
901 {
902 p4 = &a4;
903 p6 = &a6;
904 cname = NULL;
905
906 if (family == AF_INET) p6 = NULL;
907 if (family == AF_INET6) p4 = NULL;
908 if (node == NULL) cname = "localhost";
909
910 /* handle trivial questions */
911 return si_addrinfo_list(si, socktype, proto, p4, p6, port, 0, cname, cname);
912 }
913 else if ((si->sim_wants_addrinfo != NULL) && si->sim_wants_addrinfo(si))
914 {
915 /* or let the current module handle the host lookups intelligently */
916 return si->sim_addrinfo(si, nodeptr, servptr, family, socktype, proto, flags, err);
917 }
918
919 /* fall back to a default path */
920 return _gai_simple(si, nodeptr, servptr, family, socktype, proto, flags, err);
921 }
922
923 static struct addrinfo *
924 si_item_to_addrinfo(si_item_t *item)
925 {
926 si_addrinfo_t *a;
927 struct addrinfo *out;
928
929 if (item == NULL) return NULL;
930
931 a = (si_addrinfo_t *)((uintptr_t)item + sizeof(si_item_t));
932
933 out = (struct addrinfo *)calloc(1, sizeof(struct addrinfo));
934 if (out == NULL) return NULL;
935
936 out->ai_flags = a->ai_flags;
937 out->ai_family = a->ai_family;
938 out->ai_socktype = a->ai_socktype;
939 out->ai_protocol = a->ai_protocol;
940 out->ai_addrlen = a->ai_addrlen;
941
942 out->ai_addr = (struct sockaddr *)calloc(1, out->ai_addrlen);
943 if (out->ai_addr == NULL)
944 {
945 free(out);
946 return NULL;
947 }
948
949 memcpy(out->ai_addr, a->ai_addr.x, out->ai_addrlen);
950
951 if (a->ai_canonname != NULL)
952 {
953 out->ai_canonname = strdup(a->ai_canonname);
954 if (out->ai_canonname == NULL)
955 {
956 free(out);
957 return NULL;
958 }
959 }
960
961 return out;
962 }
963
964 __private_extern__ struct addrinfo *
965 si_list_to_addrinfo(si_list_t *list)
966 {
967 struct addrinfo *tail, *out;
968 int i;
969
970 if (list == NULL) return NULL;
971 if (list->count == 0) return NULL;
972
973 i = list->count - 1;
974
975 out = si_item_to_addrinfo(list->entry[i]);
976 tail = out;
977
978 for (i--; i >= 0; i--)
979 {
980 out = si_item_to_addrinfo(list->entry[i]);
981 if (out == NULL)
982 {
983 freeaddrinfo(tail);
984 return NULL;
985 }
986
987 out->ai_next = tail;
988 tail = out;
989 }
990
991 return out;
992 }
993
994 /* getipnodebyname */
995
996 static si_item_t *
997 make_hostent(si_mod_t *si, const char *name, struct in_addr addr)
998 {
999 char *addrs[2];
1000 char *aliases[1];
1001 uint64_t unused;
1002
1003 if (name == NULL) return NULL;
1004
1005 unused = 0;
1006
1007 addrs[0] = (char *)&(addr.s_addr);
1008 addrs[1] = NULL;
1009 aliases[0] = NULL;
1010
1011 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);
1012 }
1013
1014 static si_item_t *
1015 make_hostent6(si_mod_t *si, const char *name, struct in6_addr addr)
1016 {
1017 char *addrs[2];
1018 char *aliases[1];
1019 uint64_t unused;
1020
1021 if (name == NULL) return NULL;
1022
1023 unused = 0;
1024
1025 addrs[0] = (char *)&(addr.__u6_addr.__u6_addr32[0]);
1026 addrs[1] = NULL;
1027 aliases[0] = NULL;
1028
1029 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);
1030 }
1031
1032 static char *
1033 lower_case(const char *s)
1034 {
1035 int i;
1036 char *t;
1037
1038 if (s == NULL) return NULL;
1039
1040 t = malloc(strlen(s) + 1);
1041
1042 for (i = 0; s[i] != '\0'; i++)
1043 {
1044 if ((s[i] >= 'A') && (s[i] <= 'Z')) t[i] = s[i] + 32;
1045 else t[i] = s[i];
1046 }
1047
1048 t[i] = '\0';
1049
1050 return t;
1051 }
1052
1053 static int
1054 merge_alias(const char *name, build_hostent_t *h)
1055 {
1056 int i;
1057
1058 if (name == NULL) return 0;
1059 if (h == NULL) return 0;
1060
1061 if ((h->host.h_name != NULL) && (string_equal(name, h->host.h_name))) return 0;
1062
1063 for (i = 0; i < h->alias_count; i++)
1064 {
1065 if (string_equal(name, h->host.h_aliases[i])) return 0;
1066 }
1067
1068 if (h->alias_count == 0) h->host.h_aliases = (char **)calloc(2, sizeof(char *));
1069 else h->host.h_aliases = (char **)reallocf(h->host.h_aliases, (h->alias_count + 2) * sizeof(char *));
1070
1071 if (h->host.h_aliases == NULL)
1072 {
1073 h->alias_count = 0;
1074 return -1;
1075 }
1076
1077 h->host.h_aliases[h->alias_count] = lower_case(name);
1078 h->alias_count++;
1079 h->host.h_aliases[h->alias_count] = NULL;
1080
1081 return 0;
1082 }
1083
1084 static int
1085 append_addr(const char *addr, uint32_t len, build_hostent_t *h)
1086 {
1087 if (addr == NULL) return 0;
1088 if (h == NULL) return 0;
1089
1090 if (h->addr_count == 0) h->host.h_addr_list = (char **)calloc(2, sizeof(char *));
1091 else h->host.h_addr_list = (char **)reallocf(h->host.h_addr_list, (h->addr_count + 2) * sizeof(char *));
1092
1093 if (h->host.h_addr_list == NULL)
1094 {
1095 h->addr_count = 0;
1096 return -1;
1097 }
1098
1099 h->host.h_addr_list[h->addr_count] = malloc(len);
1100 if (h->host.h_addr_list[h->addr_count] == NULL) return -1;
1101
1102 memcpy(h->host.h_addr_list[h->addr_count], addr, len);
1103 h->addr_count++;
1104 h->host.h_addr_list[h->addr_count] = NULL;
1105
1106 return 0;
1107 }
1108
1109 static void
1110 free_build_hostent(build_hostent_t *h)
1111 {
1112 uint32_t i;
1113 char **aliases;
1114
1115 if (h == NULL) return;
1116
1117 if (h->host.h_name != NULL) free(h->host.h_name);
1118 h->host.h_name = NULL;
1119
1120 aliases = h->host.h_aliases;
1121 if (aliases != NULL)
1122 {
1123 while (*aliases != NULL) free(*aliases++);
1124 free(h->host.h_aliases);
1125 }
1126
1127 h->host.h_aliases = NULL;
1128
1129 if (h->host.h_addr_list != NULL)
1130 {
1131 for (i = 0; h->host.h_addr_list[i] != NULL; i++) free(h->host.h_addr_list[i]);
1132 free(h->host.h_addr_list);
1133 }
1134
1135 h->host.h_addr_list = NULL;
1136 free(h);
1137 }
1138
1139 __private_extern__ si_item_t *
1140 si_ipnode_byname(si_mod_t *si, const char *name, int family, int flags, uint32_t *err)
1141 {
1142 int i, status, want, if4, if6;
1143 struct ifaddrs *ifa, *ifap;
1144 struct in_addr addr4;
1145 struct in6_addr addr6;
1146 si_item_t *item4, *item6;
1147 build_hostent_t *out;
1148 struct hostent *h;
1149 uint64_t unused;
1150
1151 memset(&addr4, 0, sizeof(struct in_addr));
1152 memset(&addr6, 0, sizeof(struct in6_addr));
1153
1154 if (err != NULL) *err = 0;
1155
1156 if (family == AF_INET)
1157 {
1158 status = inet_aton(name, &addr4);
1159 if (status == 1)
1160 {
1161 /* create a host entry */
1162 item4 = make_hostent(si, name, addr4);
1163 if (item4 == NULL)
1164 {
1165 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1166 return NULL;
1167 }
1168
1169 return item4;
1170 }
1171 }
1172 else if (family == AF_INET6)
1173 {
1174 status = inet_pton(family, name, &addr6);
1175 if (status == 1)
1176 {
1177 /* create a host entry */
1178 item6 = make_hostent6(si, name, addr6);
1179 if (item6 == NULL)
1180 {
1181 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1182 return NULL;
1183 }
1184
1185 return item6;
1186 }
1187
1188 status = inet_aton(name, &addr4);
1189 if (status == 1)
1190 {
1191 if (!(flags & (AI_V4MAPPED | AI_V4MAPPED_CFG)))
1192 {
1193 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1194 return NULL;
1195 }
1196
1197 addr6.__u6_addr.__u6_addr32[0] = 0x00000000;
1198 addr6.__u6_addr.__u6_addr32[1] = 0x00000000;
1199 addr6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
1200 memmove(&(addr6.__u6_addr.__u6_addr32[3]), &(addr4.s_addr), IPV4_ADDR_LEN);
1201
1202 /* create a host entry */
1203 item6 = make_hostent6(si, name, addr6);
1204 if (item6 == NULL)
1205 {
1206 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1207 return NULL;
1208 }
1209
1210 return item6;
1211 }
1212 }
1213 else
1214 {
1215 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1216 return NULL;
1217 }
1218
1219 /*
1220 * IF AI_ADDRCONFIG is set, we need to know what interface flavors we really have.
1221 */
1222
1223 if4 = 0;
1224 if6 = 0;
1225
1226 if (flags & AI_ADDRCONFIG)
1227 {
1228 if (getifaddrs(&ifa) < 0)
1229 {
1230 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1231 return NULL;
1232 }
1233
1234 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
1235 {
1236 if (ifap->ifa_addr == NULL) continue;
1237 if ((ifap->ifa_flags & IFF_UP) == 0) continue;
1238 if (ifap->ifa_addr->sa_family == AF_INET) if4++;
1239 else if (ifap->ifa_addr->sa_family == AF_INET6) if6++;
1240 }
1241
1242 freeifaddrs(ifa);
1243
1244 /* Bail out if there are no interfaces */
1245 if ((if4 == 0) && (if6 == 0))
1246 {
1247 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1248 return NULL;
1249 }
1250 }
1251
1252 /*
1253 * Figure out what we want.
1254 * If user asked for AF_INET, we only want V4 addresses.
1255 */
1256 want = WANT_A4_ONLY;
1257
1258 if (family == AF_INET)
1259 {
1260 if ((flags & AI_ADDRCONFIG) && (if4 == 0))
1261 {
1262 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1263 return NULL;
1264 }
1265 }
1266 else if (family == AF_INET6)
1267 {
1268 /* family == AF_INET6 */
1269 want = WANT_A6_ONLY;
1270
1271 if (flags & (AI_V4MAPPED | AI_V4MAPPED_CFG))
1272 {
1273 if (flags & AI_ALL)
1274 {
1275 want = WANT_A6_PLUS_MAPPED_A4;
1276 }
1277 else
1278 {
1279 want = WANT_A6_OR_MAPPED_A4_IF_NO_A6;
1280 }
1281 }
1282 else
1283 {
1284 if ((flags & AI_ADDRCONFIG) && (if6 == 0))
1285 {
1286 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1287 return NULL;
1288 }
1289 }
1290 }
1291 else
1292 {
1293 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1294 return NULL;
1295 }
1296
1297 item6 = NULL;
1298 item4 = NULL;
1299
1300 /* fetch IPv6 data if required */
1301 if ((want == WANT_A6_ONLY) || (want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) || (want == WANT_A6_PLUS_MAPPED_A4))
1302 {
1303 item6 = si_host_byname(si, name, AF_INET6, (uint32_t *)err);
1304 }
1305
1306 /* fetch IPv4 data if required */
1307 if ((want == WANT_A4_ONLY) || (want == WANT_A6_PLUS_MAPPED_A4) || ((want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) && (item6 == NULL)))
1308 {
1309 item4 = si_host_byname(si, name, AF_INET, (uint32_t *)err);
1310 }
1311
1312 if (want == WANT_A4_ONLY)
1313 {
1314 si_item_release(item6);
1315 if ((item4 == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1316 return item4;
1317 }
1318
1319 if (want == WANT_A6_ONLY)
1320 {
1321 si_item_release(item4);
1322 if ((item6 == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1323 return item6;
1324 }
1325
1326 if ((item6 == NULL) && (item4 == NULL))
1327 {
1328 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
1329 return NULL;
1330 }
1331
1332 /* output item will have IPv6 + mapped IPv4 addresses */
1333
1334 out = (build_hostent_t *)calloc(1, sizeof(build_hostent_t));
1335 if (out == NULL)
1336 {
1337 si_item_release(item4);
1338 si_item_release(item6);
1339 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
1340 return NULL;
1341 }
1342
1343 if (item4 != NULL)
1344 {
1345 h = (struct hostent *)((uintptr_t)item4 + sizeof(si_item_t));
1346
1347 out->host.h_name = lower_case(h->h_name);
1348
1349 if (h->h_aliases != NULL)
1350 {
1351 for (i = 0; h->h_aliases[i] != NULL; i++) merge_alias(h->h_aliases[i], out);
1352 }
1353
1354 for (i = 0; h->h_addr_list[i] != 0; i++)
1355 {
1356 addr6.__u6_addr.__u6_addr32[0] = 0x00000000;
1357 addr6.__u6_addr.__u6_addr32[1] = 0x00000000;
1358 addr6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
1359 memmove(&(addr6.__u6_addr.__u6_addr32[3]), h->h_addr_list[i], IPV4_ADDR_LEN);
1360 append_addr((const char *)&addr6, IPV6_ADDR_LEN, out);
1361 }
1362 }
1363
1364 if (item6 != NULL)
1365 {
1366 h = (struct hostent *)((uintptr_t)item6 + sizeof(si_item_t));
1367
1368 if (out->host.h_name == NULL) out->host.h_name = lower_case(h->h_name);
1369
1370 if (h->h_aliases != NULL)
1371 {
1372 for (i = 0; h->h_aliases[i] != NULL; i++) merge_alias(h->h_aliases[i], out);
1373 }
1374
1375 for (i = 0; h->h_addr_list[i] != 0; i++) append_addr(h->h_addr_list[i], IPV6_ADDR_LEN, out);
1376 }
1377
1378 si_item_release(item4);
1379 si_item_release(item6);
1380
1381 unused = 0;
1382
1383 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);
1384
1385 free_build_hostent(out);
1386
1387 return item6;
1388 }