Libinfo-89.tar.gz
[apple/libinfo.git] / gen.subproj / getaddrinfo.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <netdb.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/socket.h>
29 #include <net/if.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <rpc/types.h>
37 #include <rpc/xdr.h>
38 #include <mach/mach.h>
39 #include <servers/bootstrap.h>
40 #include <nameser.h>
41 #include <resolv.h>
42
43 #define SOCK_UNSPEC 0
44 #define IPPROTO_UNSPEC 0
45
46 #define LONG_STRING_LENGTH 8192
47 #define _LU_MAXLUSTRLEN 256
48
49 static char *LOOKUPD_NAME = "lookup daemon";
50
51 extern int _lookup_link();
52 extern int _lookup_one();
53 extern int _lookup_all();
54
55 struct lu_dict
56 {
57 int count;
58 char *type;
59 char *name;
60 char *cname;
61 char *mx;
62 char *ipv4;
63 char *ipv6;
64 char *service;
65 char *port;
66 char *protocol;
67 char *target;
68 char *priority;
69 char *weight;
70 struct lu_dict *lu_next;
71 };
72
73 #define LU_NAME 1
74 #define LU_CNAME 2
75 #define LU_MX 3
76 #define LU_IPV4 4
77 #define LU_IPV6 5
78 #define LU_PORT 6
79 #define LU_TARGET 7
80 #define LU_PRIORITY 8
81 #define LU_WEIGHT 9
82
83 static int supported_family[] =
84 {
85 PF_UNSPEC,
86 PF_INET,
87 PF_INET6
88 };
89 static int supported_family_count = 3;
90
91 static int supported_socket[] =
92 {
93 SOCK_UNSPEC,
94 SOCK_DGRAM,
95 SOCK_STREAM
96 };
97 static int supported_socket_count = 3;
98
99 static int supported_protocol[] =
100 {
101 IPPROTO_UNSPEC,
102 IPPROTO_UDP,
103 IPPROTO_TCP
104 };
105 static int supported_protocol_count = 3;
106
107 static int supported_socket_protocol_pair[] =
108 {
109 SOCK_UNSPEC, IPPROTO_UNSPEC,
110 SOCK_UNSPEC, IPPROTO_UDP,
111 SOCK_UNSPEC, IPPROTO_TCP,
112 SOCK_DGRAM, IPPROTO_UNSPEC,
113 SOCK_DGRAM, IPPROTO_UDP,
114 SOCK_STREAM, IPPROTO_UNSPEC,
115 SOCK_STREAM, IPPROTO_TCP
116 };
117 static int supported_socket_protocol_pair_count = 7;
118
119 static int
120 gai_family_type_check(int f)
121 {
122 int i;
123
124 for (i = 0; i < supported_family_count; i++)
125 {
126 if (f == supported_family[i]) return 0;
127 }
128
129 return 1;
130 }
131
132 static int
133 gai_socket_type_check(int s)
134 {
135 int i;
136
137 for (i = 0; i < supported_socket_count; i++)
138 {
139 if (s == supported_socket[i]) return 0;
140 }
141
142 return 1;
143 }
144
145 static int
146 gai_protocol_type_check(int p)
147 {
148 int i;
149
150 for (i = 0; i < supported_protocol_count; i++)
151 {
152 if (p == supported_protocol[i]) return 0;
153 }
154
155 return 1;
156 }
157
158 static int
159 gai_socket_protocol_type_check(int s, int p)
160 {
161 int i, j, ss, sp;
162
163 for (i = 0, j = 0; i < supported_socket_protocol_pair_count; i++, j+=2)
164 {
165 ss = supported_socket_protocol_pair[j];
166 sp = supported_socket_protocol_pair[j+1];
167 if ((s == ss) && (p == sp)) return 0;
168 }
169
170 return 1;
171 }
172
173 static int
174 gai_inet_pton(const char *s, struct in6_addr *a6)
175 {
176 int run, ncolon;
177 unsigned short x[8];
178 char *p, buf[4];
179
180 if (s == NULL) return 0;
181 if (a6 == NULL) return 0;
182
183 for (ncolon = 0; ncolon < 8; ncolon++) x[ncolon] = 0;
184 memset(buf, 0, 4);
185
186 ncolon = 0;
187 run = 0;
188 for (p = (char *)s; *p != '\0'; p++)
189 {
190 if (*p == ':')
191 {
192 if (run > 0) sscanf(buf, "%hx", &(x[ncolon]));
193 ncolon++;
194 if (ncolon > 7) return 0;
195 run = 0;
196 memset(buf, 0, 4);
197 }
198 else if (((*p >= '0') && (*p <= '9')) || ((*p >= 'a') && (*p <= 'f')) || ((*p >= 'A') && (*p <= 'F')))
199 {
200 buf[run] = *p;
201 run++;
202 if (run > 4) return 0;
203 }
204 }
205
206 if (ncolon != 7) return 0;
207
208 if (run > 0) sscanf(buf, "%hx", &(x[7]));
209
210 a6->__u6_addr.__u6_addr32[0] = (x[0] << 16) + x[1];
211 a6->__u6_addr.__u6_addr32[1] = (x[2] << 16) + x[3];
212 a6->__u6_addr.__u6_addr32[2] = (x[4] << 16) + x[5];
213 a6->__u6_addr.__u6_addr32[3] = (x[6] << 16) + x[7];
214
215 return 1;
216 }
217
218 static char *
219 gai_inet_ntop(struct in6_addr a)
220 {
221 static char buf[128];
222 char t[32];
223 unsigned short x;
224 char *p;
225 int i;
226
227 memset(buf, 0, 128);
228
229 p = (char *)&a.__u6_addr.__u6_addr32;
230 for (i = 0; i < 8; i++, x += 1)
231 {
232 memmove(&x, p, 2);
233 p += 2;
234 sprintf(t, "%hx", x);
235 strcat(buf, t);
236 if (i < 7) strcat(buf, ":");
237 }
238
239 return buf;
240 }
241
242 char *
243 gai_strerror(int 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 }
261
262 return "Unknown error";
263 }
264
265 static int
266 is_ipv4_address(const char *s)
267 {
268 struct in_addr a;
269
270 if (s == NULL) return 0;
271 return inet_aton(s, &a);
272 }
273
274 static int
275 is_ipv6_address(const char *s)
276 {
277 struct in6_addr a;
278
279 if (s == NULL) return 0;
280 return gai_inet_pton(s, &a);
281 }
282
283 static void
284 append_addrinfo(struct addrinfo **l, struct addrinfo *a)
285 {
286 struct addrinfo *x;
287
288 if (l == NULL) return;
289 if (a == NULL) return;
290
291 if (*l == NULL)
292 {
293 *l = a;
294 return;
295 }
296
297 x = *l;
298 while (x->ai_next != NULL) x = x->ai_next;
299 x->ai_next = a;
300 }
301
302 static void
303 free_lu_dict(struct lu_dict *d)
304 {
305 struct lu_dict *next;
306
307 while (d != NULL)
308 {
309 next = d->lu_next;
310 if (d->type != NULL) free(d->type);
311 if (d->name != NULL) free(d->name);
312 if (d->cname != NULL) free(d->cname);
313 if (d->mx != NULL) free(d->mx);
314 if (d->ipv4 != NULL) free(d->ipv4);
315 if (d->ipv6 != NULL) free(d->ipv6);
316 if (d->service != NULL) free(d->service);
317 if (d->port != NULL) free(d->port);
318 if (d->protocol != NULL) free(d->protocol);
319 if (d->target != NULL) free(d->target);
320 if (d->priority != NULL) free(d->priority);
321 if (d->weight != NULL) free(d->weight);
322 free(d);
323 d = next;
324 }
325 }
326
327 static void
328 append_lu_dict(struct lu_dict **l, struct lu_dict *d)
329 {
330 struct lu_dict *x;
331
332 if (l == NULL) return;
333 if (d == NULL) return;
334
335 if (*l == NULL)
336 {
337 *l = d;
338 return;
339 }
340
341 x = *l;
342 while (x->lu_next != NULL) x = x->lu_next;
343 x->lu_next = d;
344 }
345
346 /*
347 * We collect values for the following keys:
348 *
349 * name
350 * cname
351 * port
352 * ip_address
353 * ipv6_address
354 * target
355 * mail_exchanger
356 * priority
357 * preference
358 * weight
359 */
360 static void
361 lookupd_process_dictionary(XDR *inxdr, struct lu_dict **l)
362 {
363 int i, nkeys, j, nvals;
364 int addme;
365 char *key, *val;
366 struct lu_dict *d;
367
368 if (!xdr_int(inxdr, &nkeys)) return;
369
370 d = (struct lu_dict *)malloc(sizeof(struct lu_dict));
371 memset(d, 0, sizeof(struct lu_dict));
372
373 for (i = 0; i < nkeys; i++)
374 {
375 key = NULL;
376
377 if (!xdr_string(inxdr, &key, LONG_STRING_LENGTH))
378 {
379 free_lu_dict(d);
380 return;
381 }
382
383 addme = 0;
384 if (!strcmp(key, "name")) addme = LU_NAME;
385 else if (!strcmp(key, "cname")) addme = LU_CNAME;
386 else if (!strcmp(key, "mail_exchanger")) addme = LU_MX;
387 else if (!strcmp(key, "ip_address")) addme = LU_IPV4;
388 else if (!strcmp(key, "ipv6_address")) addme = LU_IPV6;
389 else if (!strcmp(key, "port")) addme = LU_PORT;
390 else if (!strcmp(key, "target")) addme = LU_TARGET;
391 else if (!strcmp(key, "priority")) addme = LU_PRIORITY;
392 else if (!strcmp(key, "preference")) addme = LU_PRIORITY;
393 else if (!strcmp(key, "weight")) addme = LU_WEIGHT;
394 free(key);
395
396 if (!xdr_int(inxdr, &nvals))
397 {
398 free_lu_dict(d);
399 return;
400 }
401
402 for (j = 0; j < nvals; j++)
403 {
404 val = NULL;
405 if (!xdr_string(inxdr, &val, LONG_STRING_LENGTH))
406 {
407 free_lu_dict(d);
408 return;
409 }
410
411 if (addme == 0) free(val);
412 else if (addme == LU_NAME) d->name = val;
413 else if (addme == LU_CNAME) d->cname = val;
414 else if (addme == LU_MX) d->mx = val;
415 else if (addme == LU_IPV4) d->ipv4 = val;
416 else if (addme == LU_IPV6) d->ipv6 = val;
417 else if (addme == LU_PORT) d->port = val;
418 else if (addme == LU_TARGET) d->target = val;
419 else if (addme == LU_PRIORITY) d->priority = val;
420 else if (addme == LU_WEIGHT) d->weight = val;
421
422 if (addme != 0) d->count++;
423 addme = 0;
424 }
425 }
426
427 append_lu_dict(l, d);
428 }
429
430 static mach_port_t
431 lookupd_port(char *name)
432 {
433 mach_port_t p;
434 kern_return_t status;
435
436 status = bootstrap_look_up(bootstrap_port, name, &p);
437 if (status == KERN_SUCCESS) return p;
438 return MACH_PORT_NULL;
439 }
440
441 static int
442 encode_kv(XDR *x, char *k, char *v)
443 {
444 int n = 1;
445
446 if (!xdr_string(x, &k, _LU_MAXLUSTRLEN)) return 1;
447 if (!xdr_int(x, &n)) return 1;
448 if (!xdr_string(x, &v, _LU_MAXLUSTRLEN)) return 1;
449
450 return 0;
451 }
452
453 static int
454 gai_files(struct lu_dict *q, struct lu_dict **list)
455 {
456 int port, i;
457 struct servent *s;
458 struct hostent *h;
459 struct lu_dict *d;
460 char str[64];
461 struct in_addr a4;
462
463 if (!strcmp(q->type, "service"))
464 {
465 s = NULL;
466 if (q->name != NULL)
467 {
468 s = getservbyname(q->name, q->protocol);
469 }
470 else if (q->port != NULL)
471 {
472 port = atoi(q->port);
473 s = getservbyport(port, q->protocol);
474 }
475 if (s == NULL) return 0;
476
477 d = (struct lu_dict *)malloc(sizeof(struct lu_dict));
478 memset(d, 0, sizeof(struct lu_dict));
479
480 if (s->s_name != NULL) d->name = strdup(s->s_name);
481 sprintf(str, "%u", ntohl(s->s_port));
482 d->port = strdup(str);
483 if (s->s_proto != NULL) d->protocol = strdup(s->s_proto);
484
485 append_lu_dict(list, d);
486 return 1;
487 }
488
489 else if (!strcmp(q->type, "host"))
490 {
491 h = NULL;
492 if (q->name != NULL)
493 {
494 h = gethostbyname(q->name);
495 }
496 else if (q->ipv4 != NULL)
497 {
498 if (inet_aton(q->ipv4, &a4) == 0) return -1;
499 h = gethostbyaddr((char *)&a4, sizeof(struct in_addr), PF_INET);
500 }
501 else
502 {
503 /* gethostbyaddr for IPV6? */
504 return 0;
505 }
506
507 if (h == NULL) return 0;
508
509 for (i = 0; h->h_addr_list[i] != 0; i++)
510 {
511 d = (struct lu_dict *)malloc(sizeof(struct lu_dict));
512 memset(d, 0, sizeof(struct lu_dict));
513
514 if (h->h_name != NULL) d->name = strdup(h->h_name);
515 memmove((void *)&a4.s_addr, h->h_addr_list[i], h->h_length);
516
517 sprintf(str, "%s", inet_ntoa(a4));
518 d->ipv4 = strdup(str);
519
520 append_lu_dict(list, d);
521 }
522 return i;
523 }
524
525 return 0;
526 }
527
528 static int
529 gai_lookupd(struct lu_dict *q, struct lu_dict **list)
530 {
531 unsigned datalen;
532 XDR outxdr;
533 XDR inxdr;
534 int proc;
535 char *listbuf;
536 char databuf[_LU_MAXLUSTRLEN * BYTES_PER_XDR_UNIT];
537 int n, i, na;
538 kern_return_t status;
539 mach_port_t server_port;
540
541 if (q == NULL) return 0;
542 if (q->count == 0) return 0;
543 if (q->type == NULL) return 0;
544
545 if (list == NULL) return -1;
546
547 server_port = lookupd_port(LOOKUPD_NAME);
548 if (server_port == NULL) return gai_files(q, list);
549
550 status = _lookup_link(server_port, "query", &proc);
551 if (status != KERN_SUCCESS) return gai_files(q, list);
552
553 xdrmem_create(&outxdr, databuf, sizeof(databuf), XDR_ENCODE);
554
555 /* Encode attribute count */
556 na = q->count;
557 if (!xdr_int(&outxdr, &na))
558 {
559 xdr_destroy(&outxdr);
560 return -1;
561 }
562
563 if (encode_kv(&outxdr, "_lookup_category", q->type) != 0)
564 {
565 xdr_destroy(&outxdr);
566 return -1;
567 }
568
569 if (q->name != NULL)
570 {
571 if (encode_kv(&outxdr, "name", q->name) != 0)
572 {
573 xdr_destroy(&outxdr);
574 return -1;
575 }
576 }
577
578 if (q->ipv4 != NULL)
579 {
580 if (encode_kv(&outxdr, "ip_address", q->ipv4) != 0)
581 {
582 xdr_destroy(&outxdr);
583 return -1;
584 }
585 }
586
587 if (q->ipv6 != NULL)
588 {
589 if (encode_kv(&outxdr, "ipv6_address", q->ipv6) != 0)
590 {
591 xdr_destroy(&outxdr);
592 return -1;
593 }
594 }
595
596 if (q->service != NULL)
597 {
598 if (encode_kv(&outxdr, "service", q->service) != 0)
599 {
600 xdr_destroy(&outxdr);
601 return -1;
602 }
603 }
604
605 if (q->port != NULL)
606 {
607 if (encode_kv(&outxdr, "port", q->port) != 0)
608 {
609 xdr_destroy(&outxdr);
610 return -1;
611 }
612 }
613
614 if (q->protocol != NULL)
615 {
616 if (encode_kv(&outxdr, "protocol", q->protocol) != 0)
617 {
618 xdr_destroy(&outxdr);
619 return -1;
620 }
621 }
622
623 listbuf = NULL;
624 datalen = 0;
625
626 n = xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT;
627 status = _lookup_all(server_port, proc, databuf, n, &listbuf, &datalen);
628 if (status != KERN_SUCCESS)
629 {
630 xdr_destroy(&outxdr);
631 return -1;
632 }
633
634 xdr_destroy(&outxdr);
635
636 #ifdef NOTDEF
637 /* NOTDEF because OOL buffers are counted in bytes with untyped IPC */
638 datalen *= BYTES_PER_XDR_UNIT;
639 #endif
640 xdrmem_create(&inxdr, listbuf, datalen, XDR_DECODE);
641
642 if (!xdr_int(&inxdr, &n))
643 {
644 xdr_destroy(&inxdr);
645 return -1;
646 }
647
648 for (i = 0; i < n; i++)
649 {
650 lookupd_process_dictionary(&inxdr, list);
651 }
652
653 xdr_destroy(&inxdr);
654
655 vm_deallocate(mach_task_self(), (vm_address_t)listbuf, datalen);
656
657 return n;
658 }
659
660 void
661 freeaddrinfo(struct addrinfo *a)
662 {
663 struct addrinfo *next;
664
665 while (a != NULL)
666 {
667 next = a->ai_next;
668 if (a->ai_addr != NULL) free(a->ai_addr);
669 if (a->ai_canonname != NULL) free(a->ai_canonname);
670 free(a);
671 a = next;
672 }
673 }
674
675 static struct addrinfo *
676 new_addrinfo_v4(int flags, int sock, int proto, unsigned short port, struct in_addr addr, char *cname)
677 {
678 struct addrinfo *a;
679 struct sockaddr_in *sa;
680 int len;
681
682 a = (struct addrinfo *)malloc(sizeof(struct addrinfo));
683 memset(a, 0, sizeof(struct addrinfo));
684 a->ai_next = NULL;
685
686 a->ai_flags = flags;
687 a->ai_family = PF_INET;
688 a->ai_socktype = sock;
689 a->ai_protocol = proto;
690
691 a->ai_addrlen = sizeof(struct sockaddr_in);
692 sa = (struct sockaddr_in *)malloc(a->ai_addrlen);
693 memset(sa, 0, a->ai_addrlen);
694 sa->sin_len = a->ai_addrlen;
695 sa->sin_family = PF_INET;
696 sa->sin_port = port;
697 sa->sin_addr = addr;
698 a->ai_addr = (struct sockaddr *)sa;
699
700 if (cname != NULL)
701 {
702 len = strlen(cname) + 1;
703 a->ai_canonname = malloc(len);
704 memmove(a->ai_canonname, cname, len);
705 }
706
707 return a;
708 }
709
710 static struct addrinfo *
711 new_addrinfo_v6(int flags, int sock, int proto, unsigned short port, struct in6_addr addr, char *cname)
712 {
713 struct addrinfo *a;
714 struct sockaddr_in6 *sa;
715 int len;
716
717 a = (struct addrinfo *)malloc(sizeof(struct addrinfo));
718 memset(a, 0, sizeof(struct addrinfo));
719 a->ai_next = NULL;
720
721 a->ai_flags = flags;
722 a->ai_family = PF_INET6;
723 a->ai_socktype = sock;
724 a->ai_protocol = proto;
725
726 a->ai_addrlen = sizeof(struct sockaddr_in6);
727 sa = (struct sockaddr_in6 *)malloc(a->ai_addrlen);
728 memset(sa, 0, a->ai_addrlen);
729 sa->sin6_len = a->ai_addrlen;
730 sa->sin6_family = PF_INET6;
731 sa->sin6_port = port;
732 sa->sin6_addr = addr;
733 a->ai_addr = (struct sockaddr *)sa;
734
735 if (cname != NULL)
736 {
737 len = strlen(cname) + 1;
738 a->ai_canonname = malloc(len);
739 memmove(a->ai_canonname, cname, len);
740 }
741
742 return a;
743 }
744
745 static void
746 grok_nodename(const char *nodename, int family, struct lu_dict *q)
747 {
748 if (nodename == NULL) return;
749 if (q == NULL) return;
750
751 if (((family == PF_UNSPEC) || (family == PF_INET)) && is_ipv4_address(nodename))
752 {
753 q->ipv4 = (char *)nodename;
754 }
755 else if (((family == PF_UNSPEC) || (family == PF_INET6)) && is_ipv6_address(nodename))
756 {
757 q->ipv6 = (char *)nodename;
758 }
759 else
760 {
761 q->name = (char *)nodename;
762 }
763 }
764
765 static void
766 grok_service(const char *servname, struct lu_dict *q)
767 {
768 int port;
769 char *p;
770
771 if (servname == NULL) return;
772 if (q == NULL) return;
773
774 port = 0;
775 for (p = (char *)servname; (port == 0) && (*p != '\0'); p++)
776 {
777 if (!isdigit(*p)) port = -1;
778 }
779
780 if (port == 0) port = atoi(servname);
781 if ((port > 0) && (port < 0xffff)) q->port = (char *)servname;
782 else q->service = (char *)servname;
783 }
784
785 static int
786 gai_numerichost(struct lu_dict *h, struct lu_dict **list)
787 {
788 struct lu_dict *a;
789 int n;
790
791 if (h == NULL) return 0;
792 if (list == NULL) return -1;
793
794 n = 0;
795
796 if (h->ipv4 != NULL)
797 {
798 a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
799 memset(a, 0, sizeof(struct lu_dict));
800 a->ipv4 = strdup(h->ipv4);
801 append_lu_dict(list, a);
802 n++;
803 }
804
805 if (h->ipv6 != NULL)
806 {
807 a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
808 memset(a, 0, sizeof(struct lu_dict));
809 a->ipv6 = strdup(h->ipv6);
810 append_lu_dict(list, a);
811 n++;
812 }
813
814 return n;
815 }
816
817 static int
818 gai_numericserv(struct lu_dict *s, struct lu_dict **list)
819 {
820 struct lu_dict *a;
821
822 if (s == NULL) return 0;
823 if (s->port == NULL) return 0;
824 if (list == NULL) return -1;
825
826 a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
827 memset(a, 0, sizeof(struct lu_dict));
828 a->port = strdup(s->port);
829 append_lu_dict(list, a);
830 return 1;
831 }
832
833 static void
834 merge_addr4(struct in_addr a, struct sockaddr_in ***l, int *n)
835 {
836 int i;
837 struct sockaddr_in *sa4;
838
839 for (i = 0; i < *n; i++)
840 {
841 if (((*l)[i]->sin_family == PF_INET) && (a.s_addr == (*l)[i]->sin_addr.s_addr)) return;
842 }
843
844 sa4 = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
845 memset(sa4, 0, sizeof(struct sockaddr_in));
846
847 sa4->sin_family = PF_INET;
848 sa4->sin_addr = a;
849
850 if (*n == 0) *l = (struct sockaddr_in **)malloc(sizeof(struct sockaddr_in *));
851 else *l = (struct sockaddr_in **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in *));
852
853 (*l)[*n] = sa4;
854 (*n)++;
855 }
856
857 static void
858 merge_addr6(struct in6_addr a, struct sockaddr_in6 ***l, int *n)
859 {
860 int i;
861 struct sockaddr_in6 *sa6;
862
863 for (i = 0; i < *n; i++)
864 {
865 if (((*l)[i]->sin6_family == PF_INET6)
866 && (a.__u6_addr.__u6_addr32[0] == (*l)[i]->sin6_addr.__u6_addr.__u6_addr32[0])) return;
867 }
868
869 sa6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
870 memset(sa6, 0, sizeof(struct sockaddr_in6));
871
872 sa6->sin6_family = PF_INET6;
873 sa6->sin6_addr = a;
874
875 if (*n == 0) *l = (struct sockaddr_in6 **)malloc(sizeof(struct sockaddr_in6 *));
876 else *l = (struct sockaddr_in6 **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in6 *));
877
878 (*l)[*n] = sa6;
879 (*n)++;
880 }
881
882 /*
883 * N.B. We use sim_family to store protocol in the sockaddr.
884 * sockaddr is just used here as a data structure to keep
885 * (port, protocol) pairs.
886 */
887 static void
888 merge_plist(unsigned short port, unsigned short proto, struct sockaddr_in ***l, int *n)
889 {
890 int i;
891 struct sockaddr_in *sa4;
892
893 for (i = 0; i < *n; i++)
894 {
895 if ((port == (*l)[i]->sin_port) && (proto == (*l)[i]->sin_family)) return;
896 }
897
898 sa4 = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
899 memset(sa4, 0, sizeof(struct sockaddr_in));
900
901 sa4->sin_port = port;
902 sa4->sin_family = proto;
903
904 if (*n == 0) *l = (struct sockaddr_in **)malloc(sizeof(struct sockaddr_in *));
905 else *l = (struct sockaddr_in **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in *));
906
907 (*l)[*n] = (struct sockaddr_in *)sa4;
908 (*n)++;
909 }
910
911 /*
912 * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
913 * From "Random number generators: good ones are hard to find",
914 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
915 * October 1988, p. 1195.
916 */
917 static int
918 gai_random()
919 {
920 static int did_init = 0;
921 static unsigned int randseed = 1;
922 int x, hi, lo, t;
923 struct timeval tv;
924
925 if (did_init++ == 0)
926 {
927 gettimeofday(&tv, NULL);
928 randseed = tv.tv_usec;
929 if(randseed == 0) randseed = 1;
930 }
931
932 x = randseed;
933 hi = x / 127773;
934 lo = x % 127773;
935 t = 16807 * lo - 2836 * hi;
936 if (t <= 0) t += 0x7fffffff;
937 randseed = t;
938 return t;
939 }
940
941 /*
942 * Sort by priority and weight.
943 */
944 void
945 lu_prioity_sort(struct lu_dict **l)
946 {
947 struct lu_dict *d, **nodes;
948 unsigned int x, count, i, j, bottom, *pri, *wgt, swap, t;
949
950 if (*l == NULL) return;
951
952 count = 0;
953 for (d = *l; d != NULL; d = d->lu_next) count++;
954 nodes = (struct lu_dict **)malloc(count * sizeof(struct lu_dict *));
955 pri = (unsigned int *)malloc(count * sizeof(unsigned int));
956 wgt = (unsigned int *)malloc(count * sizeof(unsigned int));
957
958 for (i = 0, d = *l; d != NULL; d = d->lu_next, i++)
959 {
960 nodes[i] = d;
961
962 x = (unsigned int)-1;
963 if (d->priority != NULL) x = atoi(d->priority);
964 pri[i] = x;
965
966 x = 0;
967 if (d->weight != NULL) x = atoi(d->weight);
968 wgt[i] = (gai_random() % 10000) * x;
969 }
970
971 /* bubble sort by priority */
972 swap = 1;
973 bottom = count - 1;
974
975 while (swap == 1)
976 {
977 swap = 0;
978 for (i = 0, j = 1; i < bottom; i++, j++)
979 {
980 if (pri[i] < pri[j]) continue;
981 if ((pri[i] == pri[j]) && (wgt[i] < wgt[j])) continue;
982 swap = 1;
983
984 t = pri[i];
985 pri[i] = pri[j];
986 pri[j] = t;
987
988 t = wgt[i];
989 wgt[i] = wgt[j];
990 wgt[j] = t;
991
992 d = nodes[i];
993 nodes[i] = nodes[j];
994 nodes[j] = d;
995 }
996
997 bottom--;
998 }
999
1000 *l = nodes[0];
1001 bottom = count - 1;
1002 for (i = 0, j = 1; i < bottom; i++, j++) nodes[i]->lu_next = nodes[j];
1003 nodes[bottom]->lu_next = NULL;
1004
1005 free(pri);
1006 free(wgt);
1007 free(nodes);
1008 }
1009
1010 /*
1011 * Get service records from lookupd
1012 */
1013 static void
1014 gai_serv_lookupd(const char *servname, int proto, int numericserv, struct lu_dict **list)
1015 {
1016 struct lu_dict q, *sub;
1017
1018 memset(&q, 0, sizeof(struct lu_dict));
1019 q.count = 3;
1020 q.type = "service";
1021
1022 grok_service(servname, &q);
1023 if (q.service != NULL)
1024 {
1025 q.name = q.service;
1026 q.service = NULL;
1027 }
1028
1029 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
1030 {
1031 sub = NULL;
1032 q.protocol = "udp";
1033 if (numericserv == 1) gai_numericserv(&q, &sub);
1034 else gai_lookupd(&q, &sub);
1035 append_lu_dict(list, sub);
1036 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("udp");
1037 }
1038
1039 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
1040 {
1041 sub = NULL;
1042 q.protocol = "tcp";
1043 if (numericserv == 1) gai_numericserv(&q, &sub);
1044 else gai_lookupd(&q, &sub);
1045 append_lu_dict(list, sub);
1046 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("tcp");
1047 }
1048 }
1049
1050 /*
1051 * Find a service.
1052 */
1053 static int
1054 gai_serv(const char *servname, const struct addrinfo *hints, struct addrinfo **res)
1055 {
1056 struct lu_dict *list, *d;
1057 int proto, family, socktype, setcname, wantv4, wantv6;
1058 unsigned short port;
1059 char *loopv4, *loopv6;
1060 struct addrinfo *a;
1061 struct in_addr a4;
1062 struct in6_addr a6;
1063
1064 loopv4 = "127.0.0.1";
1065 loopv6 = "0:0:0:0:0:0:0:1";
1066
1067 family = PF_UNSPEC;
1068 proto = IPPROTO_UNSPEC;
1069 setcname = 0;
1070
1071 if (hints != NULL)
1072 {
1073 proto = hints->ai_protocol;
1074 if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
1075 if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
1076
1077 if (hints->ai_flags & AI_CANONNAME) setcname = 1;
1078
1079 if ((hints->ai_flags & AI_PASSIVE) == 1)
1080 {
1081 loopv4 = "0.0.0.0";
1082 loopv6 = "0:0:0:0:0:0:0:0";
1083 }
1084
1085 family = hints->ai_family;
1086 }
1087
1088 wantv4 = 1;
1089 wantv6 = 1;
1090 if (family == PF_INET6) wantv4 = 0;
1091 if (family == PF_INET) wantv6 = 0;
1092
1093 list = NULL;
1094 gai_serv_lookupd(servname, proto, 0, &list);
1095 if (list == NULL) gai_serv_lookupd(servname, proto, 1, &list);
1096
1097 for (d = list; d != NULL; d = d->lu_next)
1098 {
1099 /* We only want records with port and protocol specified */
1100 if ((d->port == NULL) || (d->protocol == NULL)) continue;
1101
1102 port = htons(atoi(d->port));
1103 proto = IPPROTO_UDP;
1104 socktype = SOCK_DGRAM;
1105 if (!strcasecmp(d->protocol, "tcp"))
1106 {
1107 proto = IPPROTO_TCP;
1108 socktype = SOCK_STREAM;
1109 }
1110
1111 if (wantv4 == 1)
1112 {
1113 inet_aton(loopv4, &a4);
1114 a = new_addrinfo_v4(0, socktype, proto, port, a4, NULL);
1115 append_addrinfo(res, a);
1116 }
1117
1118 if (wantv6 == 1)
1119 {
1120 gai_inet_pton(loopv6, &a6);
1121 a = new_addrinfo_v6(0, socktype, proto, port, a6, NULL);
1122 append_addrinfo(res, a);
1123 }
1124 }
1125
1126 free_lu_dict(list);
1127
1128 /* Set cname in first result */
1129 if ((setcname == 1) && (*res != NULL))
1130 {
1131 if (res[0]->ai_canonname == NULL) res[0]->ai_canonname = strdup("localhost");
1132 }
1133
1134 return 0;
1135 }
1136
1137 /*
1138 * Find a node.
1139 */
1140 static void
1141 gai_node_lookupd(const char *nodename, int family, int numerichost, struct lu_dict **list)
1142 {
1143 struct lu_dict q;
1144
1145 memset(&q, 0, sizeof(struct lu_dict));
1146 q.count = 2;
1147 q.type = "host";
1148
1149 grok_nodename(nodename, family, &q);
1150 if (numerichost) gai_numerichost(&q, list);
1151 else gai_lookupd(&q, list);
1152 }
1153
1154 /*
1155 * Find a node.
1156 */
1157 static int
1158 gai_node(const char *nodename, const struct addrinfo *hints, struct addrinfo **res)
1159 {
1160 int i, family, numerichost, setcname, a_list_count, wantv4, wantv6;
1161 struct lu_dict *list, *d, *t;
1162 char *cname;
1163 struct in_addr a4;
1164 struct in6_addr a6;
1165 struct addrinfo *a;
1166 struct sockaddr **a_list;
1167 struct sockaddr_in *sa4;
1168 struct sockaddr_in6 *sa6;
1169
1170 a_list_count = 0;
1171 a_list = NULL;
1172
1173 numerichost = 0;
1174 family = PF_UNSPEC;
1175 setcname = 0;
1176 cname = NULL;
1177
1178 if (hints != NULL)
1179 {
1180 family = hints->ai_family;
1181 if (hints->ai_flags & AI_NUMERICHOST) numerichost = 1;
1182 if (hints->ai_flags & AI_CANONNAME) setcname = 1;
1183 }
1184
1185 wantv4 = 1;
1186 wantv6 = 1;
1187 if (family == PF_INET6) wantv4 = 0;
1188 if (family == PF_INET) wantv6 = 0;
1189
1190 t = NULL;
1191 gai_node_lookupd(nodename, family, numerichost, &t);
1192 if ((t == NULL) && (numerichost == 0))
1193 {
1194 gai_node_lookupd(nodename, family, 1, &t);
1195 }
1196
1197 /* If the nodename is an alias, look up the real name */
1198 list = NULL;
1199 for (d = t; d != NULL; d = d->lu_next)
1200 {
1201 if (d->cname != NULL)
1202 {
1203 if (cname == NULL) cname = strdup(d->cname);
1204 gai_node_lookupd(d->cname, family, 0, &t);
1205 }
1206 }
1207
1208 append_lu_dict(&list, t);
1209
1210 for (d = list; d != NULL; d = d->lu_next)
1211 {
1212 /* Check for cname */
1213 if ((cname == NULL) && (d->name != NULL) && (strchr(d->name, '.') != NULL))
1214 {
1215 cname = strdup(d->name);
1216 }
1217
1218 /* Check for ipv4 address */
1219 if ((d->ipv4 != NULL) && (wantv4 == 1))
1220 {
1221 inet_aton(d->ipv4, &a4);
1222 merge_addr4(a4, (struct sockaddr_in ***)&a_list, &a_list_count);
1223 }
1224
1225 /* Check for ipv6 address */
1226 if ((d->ipv6 != NULL) && (wantv6 == 1))
1227 {
1228 gai_inet_pton(d->ipv6, &a6);
1229 merge_addr6(a6, (struct sockaddr_in6 ***)&a_list, &a_list_count);
1230 }
1231 }
1232
1233 /* Last chance for a name */
1234 for (d = list; (cname == NULL) && (d != NULL); d = d->lu_next)
1235 {
1236 if (d->name != NULL) cname = strdup(d->name);
1237 }
1238
1239 free_lu_dict(list);
1240
1241 for (i = 0; i < a_list_count; i++)
1242 {
1243 if (a_list[i]->sa_family == PF_INET)
1244 {
1245 sa4 = (struct sockaddr_in *)a_list[i];
1246 a = new_addrinfo_v4(0, 0, 0, 0, sa4->sin_addr, NULL);
1247 append_addrinfo(res, a);
1248 }
1249 else if (a_list[i]->sa_family == PF_INET6)
1250 {
1251 sa6 = (struct sockaddr_in6 *)a_list[i];
1252 a = new_addrinfo_v6(0, 0, 0, 0, sa6->sin6_addr, NULL);
1253 append_addrinfo(res, a);
1254 }
1255
1256 free(a_list[i]);
1257 }
1258
1259 if (a_list_count > 0) free(a_list);
1260
1261 /* Set cname in first result */
1262 if ((setcname == 1) && (*res != NULL))
1263 {
1264 if (res[0]->ai_canonname == NULL)
1265 {
1266 res[0]->ai_canonname = cname;
1267 cname = NULL;
1268 }
1269 }
1270
1271 if (cname != NULL) free(cname);
1272
1273 return 0;
1274 }
1275
1276 /*
1277 * Find a service+node.
1278 */
1279 static void
1280 gai_nodeserv_lookupd(const char *nodename, const char *servname, const struct addrinfo *hints, struct lu_dict **list)
1281 {
1282 struct lu_dict q, *sub;
1283 int proto, family;
1284
1285 family = PF_UNSPEC;
1286 proto = IPPROTO_UNSPEC;
1287
1288 if (hints != NULL)
1289 {
1290 if (hints->ai_flags & AI_NUMERICHOST) return;
1291 family = hints->ai_family;
1292
1293 proto = hints->ai_protocol;
1294 if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
1295 if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
1296 }
1297
1298 memset(&q, 0, sizeof(struct lu_dict));
1299 q.count = 4;
1300 q.type = "host";
1301
1302 grok_nodename(nodename, family, &q);
1303 grok_service(servname, &q);
1304
1305 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
1306 {
1307 sub = NULL;
1308 q.protocol = "udp";
1309 gai_lookupd(&q, &sub);
1310 append_lu_dict(list, sub);
1311 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("udp");
1312 }
1313
1314 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
1315 {
1316 sub = NULL;
1317 q.protocol = "tcp";
1318 gai_lookupd(&q, &sub);
1319 append_lu_dict(list, sub);
1320 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("tcp");
1321 }
1322 }
1323
1324 static void
1325 gai_node_pp(const char *nodename, unsigned short port, int proto, int family, int setcname, struct addrinfo **res)
1326 {
1327 struct lu_dict *list, *d;
1328 int i, wantv4, wantv6, a_list_count, socktype;
1329 char *cname;
1330 struct sockaddr **a_list;
1331 struct in_addr a4;
1332 struct in6_addr a6;
1333 struct sockaddr_in *sa4;
1334 struct sockaddr_in6 *sa6;
1335 struct addrinfo *a;
1336
1337 socktype = SOCK_UNSPEC;
1338 if (proto == IPPROTO_UDP) socktype = SOCK_DGRAM;
1339 if (proto == IPPROTO_TCP) socktype = SOCK_STREAM;
1340
1341 cname = NULL;
1342
1343 wantv4 = 1;
1344 wantv6 = 1;
1345 if (family == PF_INET6) wantv4 = 0;
1346 if (family == PF_INET) wantv6 = 0;
1347
1348 /* Resolve node name */
1349 list = NULL;
1350 gai_node_lookupd(nodename, family, 0, &list);
1351 if (list == NULL)
1352 {
1353 gai_node_lookupd(nodename, family, 1, &list);
1354 }
1355
1356 /* Resolve aliases */
1357 for (d = list; d != NULL; d = d->lu_next)
1358 {
1359 if (d->cname != NULL)
1360 {
1361 cname = strdup(d->cname);
1362 gai_node_lookupd(d->cname, family, 0, &list);
1363 }
1364 }
1365
1366 a_list_count = 0;
1367
1368 for (d = list; d != NULL; d = d->lu_next)
1369 {
1370 /* Check for cname */
1371 if ((cname == NULL) && (d->name != NULL) && (strchr(d->name, '.') != NULL))
1372 {
1373 cname = strdup(d->name);
1374 }
1375
1376 /* Check for ipv4 address */
1377 if ((d->ipv4 != NULL) && (wantv4 == 1))
1378 {
1379 inet_aton(d->ipv4, &a4);
1380 merge_addr4(a4, (struct sockaddr_in ***)&a_list, &a_list_count);
1381 }
1382
1383 /* Check for ipv6 address */
1384 if ((d->ipv6 != NULL) && (wantv6 == 1))
1385 {
1386 gai_inet_pton(d->ipv6, &a6);
1387 merge_addr6(a6, (struct sockaddr_in6 ***)&a_list, &a_list_count);
1388 }
1389 }
1390
1391 free_lu_dict(list);
1392
1393 for (i = 0; i < a_list_count; i++)
1394 {
1395 if (a_list[i]->sa_family == PF_INET)
1396 {
1397 sa4 = (struct sockaddr_in *)a_list[i];
1398 a = new_addrinfo_v4(0, socktype, proto, port, sa4->sin_addr, NULL);
1399 append_addrinfo(res, a);
1400 }
1401 else if (a_list[i]->sa_family == PF_INET6)
1402 {
1403 sa6 = (struct sockaddr_in6 *)a_list[i];
1404 a = new_addrinfo_v6(0, socktype, proto, port, sa6->sin6_addr, NULL);
1405 append_addrinfo(res, a);
1406 }
1407
1408 free(a_list[i]);
1409 }
1410
1411 if (a_list_count > 0) free(a_list);
1412
1413 /* Set cname in first result */
1414 if ((setcname == 1) && (*res != NULL))
1415 {
1416 if (res[0]->ai_canonname == NULL)
1417 {
1418 res[0]->ai_canonname = cname;
1419 cname = NULL;
1420 }
1421 }
1422
1423 if (cname != NULL) free(cname);
1424 }
1425
1426 static int
1427 gai_nodeserv(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
1428 {
1429 struct lu_dict *srv_list, *node_list, *s, *n;
1430 int numerichost, family, proto, setcname;
1431 int wantv4, wantv6, i, j, gotmx, p_list_count;
1432 unsigned short port;
1433 char *cname;
1434 struct sockaddr **p_list;
1435 struct sockaddr_in *sa4;
1436
1437 numerichost = 0;
1438 family = PF_UNSPEC;
1439 proto = IPPROTO_UNSPEC;
1440 setcname = 0;
1441 cname = NULL;
1442 wantv4 = 1;
1443 wantv6 = 1;
1444
1445 if (hints != NULL)
1446 {
1447 family = hints->ai_family;
1448 if (hints->ai_flags & AI_NUMERICHOST) numerichost = 1;
1449 if (hints->ai_flags & AI_CANONNAME) setcname = 1;
1450
1451 proto = hints->ai_protocol;
1452 if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
1453 if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
1454 }
1455
1456 if (family == PF_INET6) wantv4 = 0;
1457 if (family == PF_INET) wantv6 = 0;
1458
1459 /* First check for this particular host / service (e.g. DNS_SRV) */
1460
1461 srv_list = NULL;
1462 gai_nodeserv_lookupd(nodename, servname, hints, &srv_list);
1463 lu_prioity_sort(&srv_list);
1464
1465 if (srv_list != NULL)
1466 {
1467 for (s = srv_list; s != NULL; s = s->lu_next)
1468 {
1469 if (s->port == NULL) continue;
1470 if (s->protocol == NULL) continue;
1471
1472 i = htons(atoi(s->port));
1473 j = IPPROTO_UDP;
1474 if (!strcmp(s->protocol, "tcp")) j = IPPROTO_TCP;
1475 gai_node_pp(s->target, i, j, family, setcname, res);
1476 }
1477
1478 free_lu_dict(srv_list);
1479 return 0;
1480 }
1481
1482 /*
1483 * Special case for smtp: collect mail_exchangers.
1484 */
1485 gotmx = 0;
1486 node_list = NULL;
1487
1488 if (!strcmp(servname, "smtp"))
1489 {
1490 gai_node_lookupd(nodename, family, numerichost, &node_list);
1491 if ((node_list == NULL) && (numerichost == 0))
1492 {
1493 gai_node_lookupd(nodename, family, 1, &node_list);
1494 }
1495
1496 lu_prioity_sort(&node_list);
1497
1498 for (n = node_list; (n != NULL) && (gotmx == 0); n = n->lu_next)
1499 {
1500 if (n->mx != NULL) gotmx = 1;
1501 }
1502
1503 if ((gotmx == 0) && (node_list != NULL))
1504 {
1505 free_lu_dict(node_list);
1506 node_list = NULL;
1507 }
1508 }
1509
1510 /*
1511 * Look up service, look up node, and combine port/proto with node addresses.
1512 */
1513 srv_list = NULL;
1514 gai_serv_lookupd(servname, proto, 0, &srv_list);
1515 if (srv_list == NULL) gai_serv_lookupd(servname, proto, 1, &srv_list);
1516 if (srv_list == NULL) return 0;
1517
1518 p_list_count = 0;
1519 for (s = srv_list; s != NULL; s = s->lu_next)
1520 {
1521 if (s->port == NULL) continue;
1522 if (s->protocol == NULL) continue;
1523
1524 i = htons(atoi(s->port));
1525 j = IPPROTO_UDP;
1526 if (!strcmp(s->protocol, "tcp")) j = IPPROTO_TCP;
1527
1528 merge_plist(i, j, (struct sockaddr_in ***)&p_list, &p_list_count);
1529 }
1530
1531 free_lu_dict(srv_list);
1532
1533 for (i = 0; i < p_list_count; i++)
1534 {
1535 sa4 = (struct sockaddr_in *)p_list[i];
1536 port = sa4->sin_port;
1537 /* N.B. sin_family is overloaded */
1538 proto = sa4->sin_family;
1539
1540 if (gotmx == 1)
1541 {
1542 for (n = node_list; n != NULL; n = n->lu_next)
1543 {
1544 if (n->mx != NULL) gai_node_pp(n->mx, port, proto, family, setcname, res);
1545 }
1546 }
1547 else
1548 {
1549 gai_node_pp(nodename, port, proto, family, setcname, res);
1550 }
1551
1552 free(p_list[i]);
1553 }
1554
1555 if (node_list != NULL) free_lu_dict(node_list);
1556
1557 if (p_list_count > 0) free(p_list);
1558 return 0;
1559 }
1560
1561 int
1562 getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
1563 {
1564 int status;
1565 struct lu_dict *list;
1566
1567 if (res == NULL) return 0;
1568 *res = NULL;
1569 list = NULL;
1570
1571 /* Check input */
1572 if ((nodename == NULL) && (servname == NULL)) return EAI_NONAME;
1573
1574 /* Check hints */
1575 if (hints != NULL)
1576 {
1577 if (hints->ai_addrlen != 0) return EAI_BADHINTS;
1578 if (hints->ai_canonname != NULL) return EAI_BADHINTS;
1579 if (hints->ai_addr != NULL) return EAI_BADHINTS;
1580 if (hints->ai_next != NULL) return EAI_BADHINTS;
1581
1582 /* Check for supported protocol family */
1583 if (gai_family_type_check(hints->ai_family) != 0) return EAI_FAMILY;
1584
1585 /* Check for supported socket */
1586 if (gai_socket_type_check(hints->ai_socktype) != 0) return EAI_BADHINTS;
1587
1588 /* Check for supported protocol */
1589 if (gai_protocol_type_check(hints->ai_protocol) != 0) return EAI_BADHINTS;
1590
1591 /* Check that socket type is compatible with protocol */
1592 if (gai_socket_protocol_type_check(hints->ai_socktype, hints->ai_protocol) != 0) return EAI_BADHINTS;
1593 }
1594
1595 status = 0;
1596
1597 if (nodename == NULL)
1598 {
1599 /* If node is NULL, find service */
1600 return gai_serv(servname, hints, res);
1601 }
1602
1603 if (servname == NULL)
1604 {
1605 /* If service is NULL, find node */
1606 return gai_node(nodename, hints, res);
1607 }
1608
1609 /* Find node + service */
1610 return gai_nodeserv(nodename, servname, hints, res);
1611 }