Libinfo-78.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", 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_canonname != NULL) free(a->ai_canonname);
669 free(a);
670 a = next;
671 }
672 }
673
674 static struct addrinfo *
675 new_addrinfo_v4(int flags, int sock, int proto, unsigned short port, struct in_addr addr, char *cname)
676 {
677 struct addrinfo *a;
678 struct sockaddr_in *sa;
679 int len;
680
681 a = (struct addrinfo *)malloc(sizeof(struct addrinfo));
682 memset(a, 0, sizeof(struct addrinfo));
683 a->ai_next = NULL;
684
685 a->ai_flags = flags;
686 a->ai_family = PF_INET;
687 a->ai_socktype = sock;
688 a->ai_protocol = proto;
689
690 a->ai_addrlen = sizeof(struct sockaddr_in);
691 sa = (struct sockaddr_in *)malloc(a->ai_addrlen);
692 memset(sa, 0, a->ai_addrlen);
693 sa->sin_len = a->ai_addrlen;
694 sa->sin_family = PF_INET;
695 sa->sin_port = port;
696 sa->sin_addr = addr;
697 a->ai_addr = (struct sockaddr *)sa;
698
699 if (cname != NULL)
700 {
701 len = strlen(cname) + 1;
702 a->ai_canonname = malloc(len);
703 memmove(a->ai_canonname, cname, len);
704 }
705
706 return a;
707 }
708
709 static struct addrinfo *
710 new_addrinfo_v6(int flags, int sock, int proto, unsigned short port, struct in6_addr addr, char *cname)
711 {
712 struct addrinfo *a;
713 struct sockaddr_in6 *sa;
714 int len;
715
716 a = (struct addrinfo *)malloc(sizeof(struct addrinfo));
717 memset(a, 0, sizeof(struct addrinfo));
718 a->ai_next = NULL;
719
720 a->ai_flags = flags;
721 a->ai_family = PF_INET6;
722 a->ai_socktype = sock;
723 a->ai_protocol = proto;
724
725 a->ai_addrlen = sizeof(struct sockaddr_in6);
726 sa = (struct sockaddr_in6 *)malloc(a->ai_addrlen);
727 memset(sa, 0, a->ai_addrlen);
728 sa->sin6_len = a->ai_addrlen;
729 sa->sin6_family = PF_INET6;
730 sa->sin6_port = port;
731 sa->sin6_addr = addr;
732 a->ai_addr = (struct sockaddr *)sa;
733
734 if (cname != NULL)
735 {
736 len = strlen(cname) + 1;
737 a->ai_canonname = malloc(len);
738 memmove(a->ai_canonname, cname, len);
739 }
740
741 return a;
742 }
743
744 static void
745 grok_nodename(const char *nodename, int family, struct lu_dict *q)
746 {
747 if (nodename == NULL) return;
748 if (q == NULL) return;
749
750 if (((family == PF_UNSPEC) || (family == PF_INET)) && is_ipv4_address(nodename))
751 {
752 q->ipv4 = (char *)nodename;
753 }
754 else if (((family == PF_UNSPEC) || (family == PF_INET6)) && is_ipv6_address(nodename))
755 {
756 q->ipv6 = (char *)nodename;
757 }
758 else
759 {
760 q->name = (char *)nodename;
761 }
762 }
763
764 static void
765 grok_service(const char *servname, struct lu_dict *q)
766 {
767 int port;
768 char *p;
769
770 if (servname == NULL) return;
771 if (q == NULL) return;
772
773 port = 0;
774 for (p = (char *)servname; (port == 0) && (*p != '\0'); p++)
775 {
776 if (!isdigit(*p)) port = -1;
777 }
778
779 if (port == 0) port = atoi(servname);
780 if ((port > 0) && (port < 0xffff)) q->port = (char *)servname;
781 else q->service = (char *)servname;
782 }
783
784 static int
785 gai_numerichost(struct lu_dict *h, struct lu_dict **list)
786 {
787 struct lu_dict *a;
788 int n;
789
790 if (h == NULL) return 0;
791 if (list == NULL) return -1;
792
793 n = 0;
794
795 if (h->ipv4 != NULL)
796 {
797 a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
798 memset(a, 0, sizeof(struct lu_dict));
799 a->ipv4 = strdup(h->ipv4);
800 append_lu_dict(list, a);
801 n++;
802 }
803
804 if (h->ipv6 != NULL)
805 {
806 a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
807 memset(a, 0, sizeof(struct lu_dict));
808 a->ipv6 = strdup(h->ipv6);
809 append_lu_dict(list, a);
810 n++;
811 }
812
813 return n;
814 }
815
816 static int
817 gai_numericserv(struct lu_dict *s, struct lu_dict **list)
818 {
819 struct lu_dict *a;
820
821 if (s == NULL) return 0;
822 if (s->port == NULL) return 0;
823 if (list == NULL) return -1;
824
825 a = (struct lu_dict *)malloc(sizeof(struct lu_dict));
826 memset(a, 0, sizeof(struct lu_dict));
827 a->port = strdup(s->port);
828 append_lu_dict(list, a);
829 return 1;
830 }
831
832 static void
833 merge_addr4(struct in_addr a, struct sockaddr_in ***l, int *n)
834 {
835 int i;
836 struct sockaddr_in *sa4;
837
838 for (i = 0; i < *n; i++)
839 {
840 if (((*l)[i]->sin_family == PF_INET) && (a.s_addr == (*l)[i]->sin_addr.s_addr)) return;
841 }
842
843 sa4 = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
844 memset(sa4, 0, sizeof(struct sockaddr_in));
845
846 sa4->sin_family = PF_INET;
847 sa4->sin_addr = a;
848
849 if (*n == 0) *l = (struct sockaddr_in **)malloc(sizeof(struct sockaddr_in *));
850 else *l = (struct sockaddr_in **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in *));
851
852 (*l)[*n] = sa4;
853 (*n)++;
854 }
855
856 static void
857 merge_addr6(struct in6_addr a, struct sockaddr_in6 ***l, int *n)
858 {
859 int i;
860 struct sockaddr_in6 *sa6;
861
862 for (i = 0; i < *n; i++)
863 {
864 if (((*l)[i]->sin6_family == PF_INET6)
865 && (a.__u6_addr.__u6_addr32[0] == (*l)[i]->sin6_addr.__u6_addr.__u6_addr32[0])) return;
866 }
867
868 sa6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
869 memset(sa6, 0, sizeof(struct sockaddr_in6));
870
871 sa6->sin6_family = PF_INET6;
872 sa6->sin6_addr = a;
873
874 if (*n == 0) *l = (struct sockaddr_in6 **)malloc(sizeof(struct sockaddr_in6 *));
875 else *l = (struct sockaddr_in6 **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in6 *));
876
877 (*l)[*n] = sa6;
878 (*n)++;
879 }
880
881 /*
882 * N.B. We use sim_family to store protocol in the sockaddr.
883 * sockaddr is just used here as a data structure to keep
884 * (port, protocol) pairs.
885 */
886 static void
887 merge_plist(unsigned short port, unsigned short proto, struct sockaddr_in ***l, int *n)
888 {
889 int i;
890 struct sockaddr_in *sa4;
891
892 for (i = 0; i < *n; i++)
893 {
894 if ((port == (*l)[i]->sin_port) && (proto == (*l)[i]->sin_family)) return;
895 }
896
897 sa4 = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
898 memset(sa4, 0, sizeof(struct sockaddr_in));
899
900 sa4->sin_port = port;
901 sa4->sin_family = proto;
902
903 if (*n == 0) *l = (struct sockaddr_in **)malloc(sizeof(struct sockaddr_in *));
904 else *l = (struct sockaddr_in **)realloc(*l, (*n + 1) * sizeof(struct sockaddr_in *));
905
906 (*l)[*n] = (struct sockaddr_in *)sa4;
907 (*n)++;
908 }
909
910 /*
911 * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
912 * From "Random number generators: good ones are hard to find",
913 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
914 * October 1988, p. 1195.
915 */
916 static int
917 gai_random()
918 {
919 static int did_init = 0;
920 static unsigned int randseed = 1;
921 int x, hi, lo, t;
922 struct timeval tv;
923
924 if (did_init++ == 0)
925 {
926 gettimeofday(&tv, NULL);
927 randseed = tv.tv_usec;
928 if(randseed == 0) randseed = 1;
929 }
930
931 x = randseed;
932 hi = x / 127773;
933 lo = x % 127773;
934 t = 16807 * lo - 2836 * hi;
935 if (t <= 0) t += 0x7fffffff;
936 randseed = t;
937 return t;
938 }
939
940 /*
941 * Sort by priority and weight.
942 */
943 void
944 lu_prioity_sort(struct lu_dict **l)
945 {
946 struct lu_dict *d, **nodes;
947 unsigned int x, count, i, j, bottom, *pri, *wgt, swap, t;
948
949 if (*l == NULL) return;
950
951 count = 0;
952 for (d = *l; d != NULL; d = d->lu_next) count++;
953 nodes = (struct lu_dict **)malloc(count * sizeof(struct lu_dict *));
954 pri = (unsigned int *)malloc(count * sizeof(unsigned int));
955 wgt = (unsigned int *)malloc(count * sizeof(unsigned int));
956
957 for (i = 0, d = *l; d != NULL; d = d->lu_next, i++)
958 {
959 nodes[i] = d;
960
961 x = (unsigned int)-1;
962 if (d->priority != NULL) x = atoi(d->priority);
963 pri[i] = x;
964
965 x = 0;
966 if (d->weight != NULL) x = atoi(d->weight);
967 wgt[i] = (gai_random() % 10000) * x;
968 }
969
970 /* bubble sort by priority */
971 swap = 1;
972 bottom = count - 1;
973
974 while (swap == 1)
975 {
976 swap = 0;
977 for (i = 0, j = 1; i < bottom; i++, j++)
978 {
979 if (pri[i] < pri[j]) continue;
980 if ((pri[i] == pri[j]) && (wgt[i] < wgt[j])) continue;
981 swap = 1;
982
983 t = pri[i];
984 pri[i] = pri[j];
985 pri[j] = t;
986
987 t = wgt[i];
988 wgt[i] = wgt[j];
989 wgt[j] = t;
990
991 d = nodes[i];
992 nodes[i] = nodes[j];
993 nodes[j] = d;
994 }
995
996 bottom--;
997 }
998
999 *l = nodes[0];
1000 bottom = count - 1;
1001 for (i = 0, j = 1; i < bottom; i++, j++) nodes[i]->lu_next = nodes[j];
1002 nodes[bottom]->lu_next = NULL;
1003
1004 free(pri);
1005 free(wgt);
1006 free(nodes);
1007 }
1008
1009 /*
1010 * Get service records from lookupd
1011 */
1012 static void
1013 gai_serv_lookupd(const char *servname, int proto, int numericserv, struct lu_dict **list)
1014 {
1015 struct lu_dict q, *sub;
1016
1017 memset(&q, 0, sizeof(struct lu_dict));
1018 q.count = 3;
1019 q.type = "service";
1020
1021 grok_service(servname, &q);
1022 if (q.service != NULL)
1023 {
1024 q.name = q.service;
1025 q.service = NULL;
1026 }
1027
1028 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
1029 {
1030 sub = NULL;
1031 q.protocol = "udp";
1032 if (numericserv == 1) gai_numericserv(&q, &sub);
1033 else gai_lookupd(&q, &sub);
1034 append_lu_dict(list, sub);
1035 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("udp");
1036 }
1037
1038 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
1039 {
1040 sub = NULL;
1041 q.protocol = "tcp";
1042 if (numericserv == 1) gai_numericserv(&q, &sub);
1043 else gai_lookupd(&q, &sub);
1044 append_lu_dict(list, sub);
1045 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("tcp");
1046 }
1047 }
1048
1049 /*
1050 * Find a service.
1051 */
1052 static int
1053 gai_serv(const char *servname, const struct addrinfo *hints, struct addrinfo **res)
1054 {
1055 struct lu_dict *list, *d;
1056 int port, proto, family, socktype, setcname, wantv4, wantv6;
1057 char *loopv4, *loopv6;
1058 struct addrinfo *a;
1059 struct in_addr a4;
1060 struct in6_addr a6;
1061
1062 loopv4 = "127.0.0.1";
1063 loopv6 = "0:0:0:0:0:0:0:1";
1064
1065 family = PF_UNSPEC;
1066 proto = IPPROTO_UNSPEC;
1067 setcname = 0;
1068
1069 if (hints != NULL)
1070 {
1071 proto = hints->ai_protocol;
1072 if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
1073 if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
1074
1075 if (hints->ai_flags & AI_CANONNAME) setcname = 1;
1076
1077 if ((hints->ai_flags & AI_PASSIVE) == 1)
1078 {
1079 loopv4 = "0.0.0.0";
1080 loopv6 = "0:0:0:0:0:0:0:0";
1081 }
1082
1083 family = hints->ai_family;
1084 }
1085
1086 wantv4 = 1;
1087 wantv6 = 1;
1088 if (family == PF_INET6) wantv4 = 0;
1089 if (family == PF_INET) wantv6 = 0;
1090
1091 list = NULL;
1092 gai_serv_lookupd(servname, proto, 0, &list);
1093 if (list == NULL) gai_serv_lookupd(servname, proto, 1, &list);
1094
1095 for (d = list; d != NULL; d = d->lu_next)
1096 {
1097 /* We only want records with port and protocol specified */
1098 if ((d->port == NULL) || (d->protocol == NULL)) continue;
1099
1100 port = atoi(d->port);
1101 proto = IPPROTO_UDP;
1102 socktype = SOCK_DGRAM;
1103 if (!strcasecmp(d->protocol, "tcp"))
1104 {
1105 proto = IPPROTO_TCP;
1106 socktype = SOCK_STREAM;
1107 }
1108
1109 if (wantv4 == 1)
1110 {
1111 inet_aton(loopv4, &a4);
1112 a = new_addrinfo_v4(0, socktype, proto, port, a4, NULL);
1113 append_addrinfo(res, a);
1114 }
1115
1116 if (wantv6 == 1)
1117 {
1118 gai_inet_pton(loopv6, &a6);
1119 a = new_addrinfo_v6(0, socktype, proto, port, a6, NULL);
1120 append_addrinfo(res, a);
1121 }
1122 }
1123
1124 free_lu_dict(list);
1125
1126 /* Set cname in first result */
1127 if ((setcname == 1) && (*res != NULL))
1128 {
1129 if (res[0]->ai_canonname == NULL) res[0]->ai_canonname = strdup("localhost");
1130 }
1131
1132 return 0;
1133 }
1134
1135 /*
1136 * Find a node.
1137 */
1138 static void
1139 gai_node_lookupd(const char *nodename, int family, int numerichost, struct lu_dict **list)
1140 {
1141 struct lu_dict q;
1142
1143 memset(&q, 0, sizeof(struct lu_dict));
1144 q.count = 2;
1145 q.type = "host";
1146
1147 grok_nodename(nodename, family, &q);
1148 if (numerichost) gai_numerichost(&q, list);
1149 else gai_lookupd(&q, list);
1150 }
1151
1152 /*
1153 * Find a node.
1154 */
1155 static int
1156 gai_node(const char *nodename, const struct addrinfo *hints, struct addrinfo **res)
1157 {
1158 int i, family, numerichost, setcname, a_list_count, wantv4, wantv6;
1159 struct lu_dict *list, *d, *t;
1160 char *cname;
1161 struct in_addr a4;
1162 struct in6_addr a6;
1163 struct addrinfo *a;
1164 struct sockaddr **a_list;
1165 struct sockaddr_in *sa4;
1166 struct sockaddr_in6 *sa6;
1167
1168 a_list_count = 0;
1169 a_list = NULL;
1170
1171 numerichost = 0;
1172 family = PF_UNSPEC;
1173 setcname = 0;
1174 cname = NULL;
1175
1176 if (hints != NULL)
1177 {
1178 family = hints->ai_family;
1179 if (hints->ai_flags & AI_NUMERICHOST) numerichost = 1;
1180 if (hints->ai_flags & AI_CANONNAME) setcname = 1;
1181 }
1182
1183 wantv4 = 1;
1184 wantv6 = 1;
1185 if (family == PF_INET6) wantv4 = 0;
1186 if (family == PF_INET) wantv6 = 0;
1187
1188 t = NULL;
1189 gai_node_lookupd(nodename, family, numerichost, &t);
1190 if ((t == NULL) && (numerichost == 0))
1191 {
1192 gai_node_lookupd(nodename, family, 1, &t);
1193 }
1194
1195 /* If the nodename is an alias, look up the real name */
1196 list = NULL;
1197 for (d = t; d != NULL; d = d->lu_next)
1198 {
1199 if (d->cname != NULL)
1200 {
1201 if (cname == NULL) cname = strdup(d->cname);
1202 gai_node_lookupd(d->cname, family, 0, &t);
1203 }
1204 }
1205
1206 append_lu_dict(&list, t);
1207
1208 for (d = list; d != NULL; d = d->lu_next)
1209 {
1210 /* Check for cname */
1211 if ((cname == NULL) && (d->name != NULL) && (strchr(d->name, '.') != NULL))
1212 {
1213 cname = strdup(d->name);
1214 }
1215
1216 /* Check for ipv4 address */
1217 if ((d->ipv4 != NULL) && (wantv4 == 1))
1218 {
1219 inet_aton(d->ipv4, &a4);
1220 merge_addr4(a4, (struct sockaddr_in ***)&a_list, &a_list_count);
1221 }
1222
1223 /* Check for ipv6 address */
1224 if ((d->ipv6 != NULL) && (wantv6 == 1))
1225 {
1226 gai_inet_pton(d->ipv6, &a6);
1227 merge_addr6(a6, (struct sockaddr_in6 ***)&a_list, &a_list_count);
1228 }
1229 }
1230
1231 /* Last chance for a name */
1232 for (d = list; (cname == NULL) && (d != NULL); d = d->lu_next)
1233 {
1234 if (d->name != NULL) cname = strdup(d->name);
1235 }
1236
1237 free_lu_dict(list);
1238
1239 for (i = 0; i < a_list_count; i++)
1240 {
1241 if (a_list[i]->sa_family == PF_INET)
1242 {
1243 sa4 = (struct sockaddr_in *)a_list[i];
1244 a = new_addrinfo_v4(0, 0, 0, 0, sa4->sin_addr, NULL);
1245 append_addrinfo(res, a);
1246 }
1247 else if (a_list[i]->sa_family == PF_INET6)
1248 {
1249 sa6 = (struct sockaddr_in6 *)a_list[i];
1250 a = new_addrinfo_v6(0, 0, 0, 0, sa6->sin6_addr, NULL);
1251 append_addrinfo(res, a);
1252 }
1253
1254 free(a_list[i]);
1255 }
1256
1257 if (a_list_count > 0) free(a_list);
1258
1259 /* Set cname in first result */
1260 if ((setcname == 1) && (*res != NULL))
1261 {
1262 if (res[0]->ai_canonname == NULL)
1263 {
1264 res[0]->ai_canonname = cname;
1265 cname = NULL;
1266 }
1267 }
1268
1269 if (cname != NULL) free(cname);
1270
1271 return 0;
1272 }
1273
1274 /*
1275 * Find a service+node.
1276 */
1277 static void
1278 gai_nodeserv_lookupd(const char *nodename, const char *servname, const struct addrinfo *hints, struct lu_dict **list)
1279 {
1280 struct lu_dict q, *sub;
1281 int proto, family;
1282
1283 family = PF_UNSPEC;
1284 proto = IPPROTO_UNSPEC;
1285
1286 if (hints != NULL)
1287 {
1288 if (hints->ai_flags & AI_NUMERICHOST) return;
1289 family = hints->ai_family;
1290
1291 proto = hints->ai_protocol;
1292 if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
1293 if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
1294 }
1295
1296 memset(&q, 0, sizeof(struct lu_dict));
1297 q.count = 4;
1298 q.type = "host";
1299
1300 grok_nodename(nodename, family, &q);
1301 grok_service(servname, &q);
1302
1303 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_UDP))
1304 {
1305 sub = NULL;
1306 q.protocol = "udp";
1307 gai_lookupd(&q, &sub);
1308 append_lu_dict(list, sub);
1309 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("udp");
1310 }
1311
1312 if ((proto == IPPROTO_UNSPEC) || (proto == IPPROTO_TCP))
1313 {
1314 sub = NULL;
1315 q.protocol = "tcp";
1316 gai_lookupd(&q, &sub);
1317 append_lu_dict(list, sub);
1318 for (; sub != NULL; sub = sub->lu_next) sub->protocol = strdup("tcp");
1319 }
1320 }
1321
1322 static void
1323 gai_node_pp(const char *nodename, int port, int proto, int family, int setcname, struct addrinfo **res)
1324 {
1325 struct lu_dict *list, *d;
1326 int i, wantv4, wantv6, a_list_count, socktype;
1327 char *cname;
1328 struct sockaddr **a_list;
1329 struct in_addr a4;
1330 struct in6_addr a6;
1331 struct sockaddr_in *sa4;
1332 struct sockaddr_in6 *sa6;
1333 struct addrinfo *a;
1334
1335 socktype = SOCK_UNSPEC;
1336 if (proto == IPPROTO_UDP) socktype = SOCK_DGRAM;
1337 if (proto == IPPROTO_TCP) socktype = SOCK_STREAM;
1338
1339 cname = NULL;
1340
1341 wantv4 = 1;
1342 wantv6 = 1;
1343 if (family == PF_INET6) wantv4 = 0;
1344 if (family == PF_INET) wantv6 = 0;
1345
1346 /* Resolve node name */
1347 list = NULL;
1348 gai_node_lookupd(nodename, family, 0, &list);
1349 if (list == NULL)
1350 {
1351 gai_node_lookupd(nodename, family, 1, &list);
1352 }
1353
1354 /* Resolve aliases */
1355 for (d = list; d != NULL; d = d->lu_next)
1356 {
1357 if (d->cname != NULL)
1358 {
1359 cname = strdup(d->cname);
1360 gai_node_lookupd(d->cname, family, 0, &list);
1361 }
1362 }
1363
1364 a_list_count = 0;
1365
1366 for (d = list; d != NULL; d = d->lu_next)
1367 {
1368 /* Check for cname */
1369 if ((cname == NULL) && (d->name != NULL) && (strchr(d->name, '.') != NULL))
1370 {
1371 cname = strdup(d->name);
1372 }
1373
1374 /* Check for ipv4 address */
1375 if ((d->ipv4 != NULL) && (wantv4 == 1))
1376 {
1377 inet_aton(d->ipv4, &a4);
1378 merge_addr4(a4, (struct sockaddr_in ***)&a_list, &a_list_count);
1379 }
1380
1381 /* Check for ipv6 address */
1382 if ((d->ipv6 != NULL) && (wantv6 == 1))
1383 {
1384 gai_inet_pton(d->ipv6, &a6);
1385 merge_addr6(a6, (struct sockaddr_in6 ***)&a_list, &a_list_count);
1386 }
1387 }
1388
1389 free_lu_dict(list);
1390
1391 for (i = 0; i < a_list_count; i++)
1392 {
1393 if (a_list[i]->sa_family == PF_INET)
1394 {
1395 sa4 = (struct sockaddr_in *)a_list[i];
1396 a = new_addrinfo_v4(0, socktype, proto, port, sa4->sin_addr, NULL);
1397 append_addrinfo(res, a);
1398 }
1399 else if (a_list[i]->sa_family == PF_INET6)
1400 {
1401 sa6 = (struct sockaddr_in6 *)a_list[i];
1402 a = new_addrinfo_v6(0, socktype, proto, port, sa6->sin6_addr, NULL);
1403 append_addrinfo(res, a);
1404 }
1405
1406 free(a_list[i]);
1407 }
1408
1409 if (a_list_count > 0) free(a_list);
1410
1411 /* Set cname in first result */
1412 if ((setcname == 1) && (*res != NULL))
1413 {
1414 if (res[0]->ai_canonname == NULL)
1415 {
1416 res[0]->ai_canonname = cname;
1417 cname = NULL;
1418 }
1419 }
1420
1421 if (cname != NULL) free(cname);
1422 }
1423
1424 static int
1425 gai_nodeserv(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
1426 {
1427 struct lu_dict *srv_list, *node_list, *s, *n;
1428 int numerichost, family, port, proto, setcname;
1429 int wantv4, wantv6, i, j, gotmx, p_list_count;
1430 char *cname;
1431 struct sockaddr **p_list;
1432 struct sockaddr_in *sa4;
1433
1434 numerichost = 0;
1435 family = PF_UNSPEC;
1436 proto = IPPROTO_UNSPEC;
1437 setcname = 0;
1438 cname = NULL;
1439 wantv4 = 1;
1440 wantv6 = 1;
1441
1442 if (hints != NULL)
1443 {
1444 family = hints->ai_family;
1445 if (hints->ai_flags & AI_NUMERICHOST) numerichost = 1;
1446 if (hints->ai_flags & AI_CANONNAME) setcname = 1;
1447
1448 proto = hints->ai_protocol;
1449 if (hints->ai_socktype == SOCK_DGRAM) proto = IPPROTO_UDP;
1450 if (hints->ai_socktype == SOCK_STREAM) proto = IPPROTO_TCP;
1451 }
1452
1453 if (family == PF_INET6) wantv4 = 0;
1454 if (family == PF_INET) wantv6 = 0;
1455
1456 /* First check for this particular host / service (e.g. DNS_SRV) */
1457
1458 srv_list = NULL;
1459 gai_nodeserv_lookupd(nodename, servname, hints, &srv_list);
1460 lu_prioity_sort(&srv_list);
1461
1462 if (srv_list != NULL)
1463 {
1464 for (s = srv_list; s != NULL; s = s->lu_next)
1465 {
1466 if (s->port == NULL) continue;
1467 if (s->protocol == NULL) continue;
1468
1469 i = atoi(s->port);
1470 j = IPPROTO_UDP;
1471 if (!strcmp(s->protocol, "tcp")) j = IPPROTO_TCP;
1472 gai_node_pp(s->target, i, j, family, setcname, res);
1473 }
1474
1475 free_lu_dict(srv_list);
1476 return 0;
1477 }
1478
1479 /*
1480 * Special case for smtp: collect mail_exchangers.
1481 */
1482 gotmx = 0;
1483 node_list = NULL;
1484
1485 if (!strcmp(servname, "smtp"))
1486 {
1487 gai_node_lookupd(nodename, family, numerichost, &node_list);
1488 if ((node_list == NULL) && (numerichost == 0))
1489 {
1490 gai_node_lookupd(nodename, family, 1, &node_list);
1491 }
1492
1493 lu_prioity_sort(&node_list);
1494
1495 for (n = node_list; (n != NULL) && (gotmx == 0); n = n->lu_next)
1496 {
1497 if (n->mx != NULL) gotmx = 1;
1498 }
1499
1500 if ((gotmx == 0) && (node_list != NULL))
1501 {
1502 free_lu_dict(node_list);
1503 node_list = NULL;
1504 }
1505 }
1506
1507 /*
1508 * Look up service, look up node, and combine port/proto with node addresses.
1509 */
1510 srv_list = NULL;
1511 gai_serv_lookupd(servname, proto, 0, &srv_list);
1512 if (srv_list == NULL) gai_serv_lookupd(servname, proto, 1, &srv_list);
1513 if (srv_list == NULL) return 0;
1514
1515 p_list_count = 0;
1516 for (s = srv_list; s != NULL; s = s->lu_next)
1517 {
1518 if (s->port == NULL) continue;
1519 if (s->protocol == NULL) continue;
1520
1521 i = atoi(s->port);
1522 j = IPPROTO_UDP;
1523 if (!strcmp(s->protocol, "tcp")) j = IPPROTO_TCP;
1524
1525 merge_plist(i, j, (struct sockaddr_in ***)&p_list, &p_list_count);
1526 }
1527
1528 free_lu_dict(srv_list);
1529
1530 for (i = 0; i < p_list_count; i++)
1531 {
1532 sa4 = (struct sockaddr_in *)p_list[i];
1533 port = sa4->sin_port;
1534 /* N.B. sin_family is overloaded */
1535 proto = sa4->sin_family;
1536
1537 if (gotmx == 1)
1538 {
1539 for (n = node_list; n != NULL; n = n->lu_next)
1540 {
1541 if (n->mx != NULL) gai_node_pp(n->mx, port, proto, family, setcname, res);
1542 }
1543 }
1544 else
1545 {
1546 gai_node_pp(nodename, port, proto, family, setcname, res);
1547 }
1548
1549 free(p_list[i]);
1550 }
1551
1552 if (node_list != NULL) free_lu_dict(node_list);
1553
1554 if (p_list_count > 0) free(p_list);
1555 return 0;
1556 }
1557
1558 int
1559 getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
1560 {
1561 int status;
1562 struct lu_dict *list;
1563
1564 if (res == NULL) return 0;
1565 *res = NULL;
1566 list = NULL;
1567
1568 /* Check input */
1569 if ((nodename == NULL) && (servname == NULL)) return EAI_NONAME;
1570
1571 /* Check hints */
1572 if (hints != NULL)
1573 {
1574 if (hints->ai_addrlen != 0) return EAI_BADHINTS;
1575 if (hints->ai_canonname != NULL) return EAI_BADHINTS;
1576 if (hints->ai_addr != NULL) return EAI_BADHINTS;
1577 if (hints->ai_next != NULL) return EAI_BADHINTS;
1578
1579 /* Check for supported protocol family */
1580 if (gai_family_type_check(hints->ai_family) != 0) return EAI_FAMILY;
1581
1582 /* Check for supported socket */
1583 if (gai_socket_type_check(hints->ai_socktype) != 0) return EAI_BADHINTS;
1584
1585 /* Check for supported protocol */
1586 if (gai_protocol_type_check(hints->ai_protocol) != 0) return EAI_BADHINTS;
1587
1588 /* Check that socket type is compatible with protocol */
1589 if (gai_socket_protocol_type_check(hints->ai_socktype, hints->ai_protocol) != 0) return EAI_BADHINTS;
1590 }
1591
1592 status = 0;
1593
1594 if (nodename == NULL)
1595 {
1596 /* If node is NULL, find service */
1597 return gai_serv(servname, hints, res);
1598 }
1599
1600 if (servname == NULL)
1601 {
1602 /* If service is NULL, find node */
1603 return gai_node(nodename, hints, res);
1604 }
1605
1606 /* Find node + service */
1607 return gai_nodeserv(nodename, servname, hints, res);
1608 }