Update.
[glibc.git] / sysdeps / posix / getaddrinfo.c
blobfb9709fee77023e18076fe1e56c3e376cc34591e
1 /* The Inner Net License, Version 2.00
3 The author(s) grant permission for redistribution and use in source and
4 binary forms, with or without modification, of the software and documentation
5 provided that the following conditions are met:
7 0. If you receive a version of the software that is specifically labelled
8 as not being for redistribution (check the version message and/or README),
9 you are not permitted to redistribute that version of the software in any
10 way or form.
11 1. All terms of the all other applicable copyrights and licenses must be
12 followed.
13 2. Redistributions of source code must retain the authors' copyright
14 notice(s), this list of conditions, and the following disclaimer.
15 3. Redistributions in binary form must reproduce the authors' copyright
16 notice(s), this list of conditions, and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 4. All advertising materials mentioning features or use of this software
19 must display the following acknowledgement with the name(s) of the
20 authors as specified in the copyright notice(s) substituted where
21 indicated:
23 This product includes software developed by <name(s)>, The Inner
24 Net, and other contributors.
26 5. Neither the name(s) of the author(s) nor the names of its contributors
27 may be used to endorse or promote products derived from this software
28 without specific prior written permission.
30 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
34 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
37 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 If these license terms cause you a real problem, contact the author. */
43 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
45 /* getaddrinfo() v1.13 */
47 /* To do what POSIX says, even when it's broken: */
48 #define BROKEN_LIKE_POSIX 1
49 #define LOCAL 1
50 #define INET6 1
51 #define HOSTTABLE 0
52 #define RESOLVER 1
54 #include <sys/types.h>
55 #include <stdlib.h>
56 #include <unistd.h>
57 #include <sys/socket.h>
58 #if LOCAL
59 #include <stdio.h>
60 #include <string.h>
61 #include <sys/utsname.h>
62 #include <sys/un.h>
63 #endif /* LOCAL */
64 #include <netinet/in.h>
65 #include <netdb.h>
66 #include <errno.h>
67 #include <arpa/inet.h>
69 #ifndef AF_LOCAL
70 #define AF_LOCAL AF_UNIX
71 #endif /* AF_LOCAL */
72 #ifndef PF_LOCAL
73 #define PF_LOCAL PF_UNIX
74 #endif /* PF_LOCAL */
75 #ifndef UNIX_PATH_MAX
76 #define UNIX_PATH_MAX 108
77 #endif /* UNIX_PATH_MAX */
79 #define GAIH_OKIFUNSPEC 0x0100
80 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
82 #if HOSTTABLE
83 struct hostent *_hostname2addr_hosts(const char *name, int);
84 struct hostent *_addr2hostname_hosts(const char *name, int, int);
85 #endif /* HOSTTABLE */
87 static struct addrinfo nullreq =
88 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
90 struct gaih_service {
91 char *name;
92 int num;
95 struct gaih_servtuple {
96 struct gaih_servtuple *next;
97 int socktype;
98 int protocol;
99 int port;
102 static struct gaih_servtuple nullserv = {
103 NULL, 0, 0, 0
106 struct gaih_addrtuple {
107 struct gaih_addrtuple *next;
108 int family;
109 char addr[16];
112 struct gaih_typeproto {
113 int socktype;
114 int protocol;
115 char *name;
118 #if LOCAL
119 static int gaih_local(const char *name, const struct gaih_service *service,
120 const struct addrinfo *req, struct addrinfo **pai)
122 struct utsname utsname;
124 if (name || (req->ai_flags & AI_CANONNAME))
125 if (uname(&utsname))
126 return -EAI_SYSTEM;
127 if (name) {
128 if (strcmp(name, "localhost") && strcmp(name, "local") && strcmp(name, "unix") && strcmp(name, utsname.nodename))
129 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
132 if (!(*pai = malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_un) + ((req->ai_flags & AI_CANONNAME) ? (strlen(utsname.nodename) + 1): 0))))
133 return -EAI_MEMORY;
135 (*pai)->ai_next = NULL;
136 (*pai)->ai_flags = req->ai_flags;
137 (*pai)->ai_family = AF_LOCAL;
138 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
139 (*pai)->ai_protocol = req->ai_protocol;
140 (*pai)->ai_addrlen = sizeof(struct sockaddr_un);
141 (*pai)->ai_addr = (void *)(*pai) + sizeof(struct addrinfo);
142 #if SALEN
143 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_len = sizeof(struct sockaddr_un);
144 #endif /* SALEN */
145 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
146 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
147 if (service) {
148 char *c;
149 if (c = strchr(service->name, '/')) {
150 if (strlen(service->name) >= sizeof(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path))
151 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
152 strcpy(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, service->name);
153 } else {
154 if (strlen(P_tmpdir "/") + 1 + strlen(service->name) >= sizeof(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path))
155 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
156 strcpy(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, P_tmpdir "/");
157 strcat(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, service->name);
159 } else {
160 if (!tmpnam(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path))
161 return -EAI_SYSTEM;
163 if (req->ai_flags & AI_CANONNAME)
164 strcpy((*pai)->ai_canonname = (char *)(*pai) + sizeof(struct addrinfo) + sizeof(struct sockaddr_un), utsname.nodename);
165 else
166 (*pai)->ai_canonname = NULL;
167 return 0;
169 #endif /* LOCAL */
171 static struct gaih_typeproto gaih_inet_typeproto[] =
173 { 0, 0, NULL },
174 { SOCK_STREAM, IPPROTO_TCP, (char *) "tcp" },
175 { SOCK_DGRAM, IPPROTO_UDP, (char *) "udp" },
176 { 0, 0, NULL }
179 static int gaih_inet_serv(char *servicename, struct gaih_typeproto *tp, struct gaih_servtuple **st)
181 struct servent *s;
182 int tmpbuflen = 1024;
183 struct servent ts;
184 char *tmpbuf = __alloca (tmpbuflen);
185 while (__getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen, &s))
187 if (errno == ERANGE)
189 tmpbuflen *= 2;
190 tmpbuf = __alloca (tmpbuflen);
192 else
194 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
198 if (!(*st = malloc (sizeof (struct gaih_servtuple))))
199 return -EAI_MEMORY;
201 (*st)->next = NULL;
202 (*st)->socktype = tp->socktype;
203 (*st)->protocol = tp->protocol;
204 (*st)->port = s->s_port;
206 return 0;
209 static int gaih_inet(const char *name, const struct gaih_service *service,
210 const struct addrinfo *req, struct addrinfo **pai)
212 struct gaih_typeproto *tp = gaih_inet_typeproto;
213 struct gaih_servtuple *st = &nullserv;
214 struct gaih_addrtuple *at = NULL;
215 int i;
217 if (req->ai_protocol || req->ai_socktype) {
218 for (tp++; tp->name &&
219 ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
220 ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
221 if (!tp->name)
222 if (req->ai_socktype)
223 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
224 else
225 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
228 if (service) {
229 if (service->num < 0) {
230 if (tp->name) {
231 if (i = gaih_inet_serv(service->name, tp, &st))
232 return i;
233 } else {
234 struct gaih_servtuple **pst = &st;
235 for (tp++; tp->name; tp++) {
236 if (i = gaih_inet_serv(service->name, tp, pst)) {
237 if (i & GAIH_OKIFUNSPEC)
238 continue;
239 goto ret;
241 pst = &((*pst)->next);
243 if (st == &nullserv) {
244 i = (GAIH_OKIFUNSPEC | -EAI_SERVICE);
245 goto ret;
248 } else {
249 if (!(st = malloc(sizeof(struct gaih_servtuple))))
250 return -EAI_MEMORY;
252 st->next = NULL;
253 st->socktype = tp->socktype;
254 st->protocol = tp->protocol;
255 st->port = htons(service->num);
259 if (name) {
260 if (!(at = malloc(sizeof(struct gaih_addrtuple)))) {
261 i = -EAI_MEMORY;
262 goto ret;
265 at->family = 0;
266 at->next = NULL;
268 if (!at->family || !req->ai_family || (req->ai_family == AF_INET))
269 if (inet_pton(AF_INET, name, at->addr) > 0)
270 at->family = AF_INET;
272 #if INET6
273 if (!at->family && (!req->ai_family || (req->ai_family == AF_INET6)))
274 if (inet_pton(AF_INET6, name, at->addr) > 0)
275 at->family = AF_INET6;
276 #endif /* INET6 */
278 #if HOSTTABLE
279 if (!at->family) {
280 struct hostent *h;
281 struct gaih_addrtuple **pat = &at;
283 #if INET6
284 if (!req->ai_family || (req->ai_family == AF_INET6))
285 if (h = _hostname2addr_hosts(name, AF_INET6)) {
286 for (i = 0; h->h_addr_list[i]; i++) {
287 if (!*pat) {
288 if (!(*pat = malloc(sizeof(struct gaih_addrtuple)))) {
289 i = -EAI_MEMORY;
290 goto ret;
293 (*pat)->next = NULL;
294 (*pat)->family = AF_INET6;
295 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(struct in6_addr));
296 pat = &((*pat)->next);
299 #endif /* INET6 */
301 if (!req->ai_family || (req->ai_family == AF_INET))
302 if (h = _hostname2addr_hosts(name, AF_INET)) {
303 for (i = 0; h->h_addr_list[i]; i++) {
304 if (!*pat) {
305 if (!(*pat = malloc(sizeof(struct gaih_addrtuple)))) {
306 i = -EAI_MEMORY;
307 goto ret;
310 (*pat)->next = NULL;
311 (*pat)->family = AF_INET;
312 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(struct in_addr));
313 pat = &((*pat)->next);
317 #endif /* HOSTTABLE */
319 #if RESOLVER
320 if (!at->family) {
321 struct hostent *h;
322 struct gaih_addrtuple **pat = &at;
324 #if INET6
325 if (!req->ai_family || (req->ai_family == AF_INET6)) {
326 int herrno;
327 int tmpbuflen = 1024;
328 struct hostent th;
329 char *tmpbuf = __alloca(tmpbuflen);
330 while (__gethostbyname2_r(name, AF_INET6, &th, tmpbuf, tmpbuflen,
331 &h, &herrno)) {
332 if (herrno == NETDB_INTERNAL) {
333 if (errno == ERANGE) {
334 /* Need more buffer */
335 tmpbuflen *= 2;
336 tmpbuf = __alloca(tmpbuflen);
337 } else {
338 /* Bail out */
339 __set_h_errno(herrno);
340 i = -EAI_SYSTEM;
341 goto ret;
343 } else {
344 break;
347 if (h) {
348 for (i = 0; h->h_addr_list[i]; i++) {
349 if (!*pat) {
350 if (!(*pat = malloc(sizeof(struct gaih_addrtuple)))) {
351 i = -EAI_MEMORY;
352 goto ret;
355 (*pat)->next = NULL;
356 (*pat)->family = AF_INET6;
357 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(struct in6_addr));
358 pat = &((*pat)->next);
362 #endif /* INET6 */
364 if (!req->ai_family || (req->ai_family == AF_INET)) {
365 int herrno;
366 struct hostent th;
367 int tmpbuflen = 1024;
368 char *tmpbuf = __alloca(tmpbuflen);
369 while (__gethostbyname2_r(name, AF_INET, &th, tmpbuf, tmpbuflen,
370 &h, &herrno)) {
371 if (herrno == NETDB_INTERNAL) {
372 if (errno == ERANGE) {
373 /* Need more buffer */
374 tmpbuflen *= 2;
375 tmpbuf = __alloca(tmpbuflen);
376 } else {
377 /* Bail out */
378 __set_h_errno(herrno);
379 i = -EAI_SYSTEM;
380 goto ret;
382 } else {
383 break;
386 if (h) {
387 for (i = 0; h->h_addr_list[i]; i++) {
388 if (!*pat) {
389 if (!(*pat = malloc(sizeof(struct gaih_addrtuple)))) {
390 i = -EAI_MEMORY;
391 goto ret;
394 (*pat)->next = NULL;
395 (*pat)->family = AF_INET;
396 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(struct in_addr));
397 pat = &((*pat)->next);
402 #endif /* RESOLVER */
404 if (!at->family)
405 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
406 } else {
407 if (!(at = malloc(sizeof(struct gaih_addrtuple)))) {
408 i = -EAI_MEMORY;
409 goto ret;
412 memset(at, 0, sizeof(struct gaih_addrtuple));
414 #if INET6
415 if (!(at->next = malloc(sizeof(struct gaih_addrtuple)))) {
416 i = -EAI_MEMORY;
417 goto ret;
420 at->family = AF_INET6;
422 memset(at->next, 0, sizeof(struct gaih_addrtuple));
423 at->next->family = AF_INET;
424 #else /* INET6 */
425 at->family = AF_INET;
426 #endif /* INET6 */
429 if (!pai) {
430 i = 0;
431 goto ret;
435 const char *c = NULL;
436 struct gaih_servtuple *st2;
437 struct gaih_addrtuple *at2 = at;
438 int j;
440 buffer is the size of an unformatted IPv6 address in printable format.
442 char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
444 while(at2) {
445 if (req->ai_flags & AI_CANONNAME) {
446 struct hostent *h = NULL;
448 #if RESOLVER
449 int herrno;
450 struct hostent th;
451 int tmpbuflen = 1024;
452 char *tmpbuf = __alloca(tmpbuflen);
453 while (__gethostbyaddr_r(at2->addr,
454 #if INET6
455 (at2->family == AF_INET6) ? sizeof(struct in6_addr) :
456 #endif /* INET6 */
457 sizeof(struct in_addr), at2->family,
458 &th, tmpbuf, tmpbuflen, &h, &herrno)) {
459 if (herrno == NETDB_INTERNAL) {
460 if (errno == ERANGE) {
461 /* Need more buffer */
462 tmpbuflen *= 2;
463 tmpbuf = __alloca(tmpbuflen);
464 } else {
465 /* Bail out */
466 __set_h_errno(herrno);
467 i = -EAI_SYSTEM;
468 goto ret;
470 } else {
471 break;
474 #endif /* RESOLVER */
475 #if HOSTTABLE
476 if (!h)
477 h = _addr2hostname_hosts(at2->addr,
478 #if INET6
479 (at2->family == AF_INET6) ? sizeof(struct in6_addr) :
480 #endif /* INET6 */
481 sizeof(struct in_addr), at2->family);
482 #endif /* HOSTTABLE */
484 if (!h)
485 c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer));
486 else
487 c = h->h_name;
489 if (!c) {
490 i = (GAIH_OKIFUNSPEC | -EAI_NONAME);
491 goto ret;
494 j = strlen(c) + 1;
495 } else
496 j = 0;
498 #if INET6
499 if (at2->family == AF_INET6)
500 i = sizeof(struct sockaddr_in6);
501 else
502 #endif /* INET6 */
503 i = sizeof(struct sockaddr_in);
505 st2 = st;
506 while(st2) {
507 if (!(*pai = malloc(sizeof(struct addrinfo) + i + j))) {
508 i = -EAI_MEMORY;
509 goto ret;
511 (*pai)->ai_flags = req->ai_flags;
512 (*pai)->ai_family = at2->family;
513 (*pai)->ai_socktype = st2->socktype;
514 (*pai)->ai_protocol = st2->protocol;
515 (*pai)->ai_addrlen = i;
516 (*pai)->ai_addr = (void *)(*pai) + sizeof(struct addrinfo);
517 #if SALEN
518 ((struct sockaddr_in *)(*pai)->ai_addr)->sin_len = i;
519 #endif /* SALEN */
520 ((struct sockaddr_in *)(*pai)->ai_addr)->sin_family = at2->family;
521 ((struct sockaddr_in *)(*pai)->ai_addr)->sin_port = st2->port;
523 #if INET6
524 if (at2->family == AF_INET6) {
525 ((struct sockaddr_in6 *)(*pai)->ai_addr)->sin6_flowinfo = 0;
526 memcpy(&((struct sockaddr_in6 *)(*pai)->ai_addr)->sin6_addr, at2->addr, sizeof(struct in6_addr));
527 } else
528 #endif /* INET6 */
530 memcpy(&((struct sockaddr_in *)(*pai)->ai_addr)->sin_addr, at2->addr, sizeof(struct in_addr));
531 memset(((struct sockaddr_in *)(*pai)->ai_addr)->sin_zero, 0, sizeof(((struct sockaddr_in *)(*pai)->ai_addr)->sin_zero));
534 if (c) {
535 (*pai)->ai_canonname = (void *)(*pai) + sizeof(struct addrinfo) + i;
536 strcpy((*pai)->ai_canonname, c);
537 } else
538 (*pai)->ai_canonname = NULL;
539 (*pai)->ai_next = NULL;
541 pai = &((*pai)->ai_next);
543 st2 = st2->next;
545 at2 = at2->next;
549 i = 0;
551 ret:
552 if (st != &nullserv) {
553 struct gaih_servtuple *st2 = st;
554 while(st) {
555 st2 = st->next;
556 free(st);
557 st = st2;
560 if (at) {
561 struct gaih_addrtuple *at2 = at;
562 while(at) {
563 at2 = at->next;
564 free(at);
565 at = at2;
568 return i;
571 struct gaih {
572 int family;
573 int (*gaih)(const char *name, const struct gaih_service *service,
574 const struct addrinfo *req, struct addrinfo **pai);
577 static struct gaih gaih[] = {
578 #if INET6
579 { PF_INET6, gaih_inet },
580 #endif /* INET6 */
581 { PF_INET, gaih_inet },
582 #if LOCAL
583 { PF_LOCAL, gaih_local },
584 #endif /* LOCAL */
585 { PF_UNSPEC, NULL }
588 int getaddrinfo(const char *name, const char *service,
589 const struct addrinfo *req, struct addrinfo **pai)
591 int i = 0, j = 0;
592 struct addrinfo *p = NULL, **end;
593 struct gaih *g = gaih, *pg = NULL;
594 struct gaih_service gaih_service, *pservice;
596 if (name && (name[0] == '*') && !name[1])
597 name = NULL;
599 if (service && (service[0] == '*') && !service[1])
600 service = NULL;
602 #if BROKEN_LIKE_POSIX
603 if (!name && !service)
604 return EAI_NONAME;
605 #endif /* BROKEN_LIKE_POSIX */
607 if (!req)
608 req = &nullreq;
610 if (req->ai_flags & ~3)
611 return EAI_BADFLAGS;
613 if ((req->ai_flags & AI_CANONNAME) && !name)
614 return EAI_BADFLAGS;
616 if (service && *service) {
617 char *c;
618 gaih_service.num = strtoul(gaih_service.name = (void *)service, &c, 10);
619 if (*c) {
620 gaih_service.num = -1;
622 #if BROKEN_LIKE_POSIX
623 else
624 if (!req->ai_socktype)
625 return EAI_SERVICE;
626 #endif /* BROKEN_LIKE_POSIX */
627 pservice = &gaih_service;
628 } else
629 pservice = NULL;
631 if (pai)
632 end = &p;
633 else
634 end = NULL;
636 while(g->gaih) {
637 if ((req->ai_family == g->family) || !req->ai_family) {
638 j++;
639 if (!((pg && (pg->gaih == g->gaih)))) {
640 pg = g;
641 if (i = g->gaih(name, pservice, req, end)) {
642 if (!req->ai_family && (i & GAIH_OKIFUNSPEC))
643 continue;
644 goto gaih_err;
646 if (end)
647 while(*end) end = &((*end)->ai_next);
650 g++;
653 if (!j)
654 return EAI_FAMILY;
656 if (p) {
657 *pai = p;
658 return 0;
661 if (!pai && !i)
662 return 0;
664 gaih_err:
665 if (p)
666 freeaddrinfo(p);
668 if (i)
669 return -(i & GAIH_EAI);
671 return EAI_NONAME;
674 void freeaddrinfo(struct addrinfo *ai)
676 struct addrinfo *p;
678 while(ai) {
679 p = ai;
680 ai = ai->ai_next;
681 free((void *)p);