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