]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/posix/getaddrinfo.c
Update.
[thirdparty/glibc.git] / sysdeps / posix / getaddrinfo.c
CommitLineData
46ec036d
UD
1/* The Inner Net License, Version 2.00
2
3 The author(s) grant permission for redistribution and use in source and
4binary forms, with or without modification, of the software and documentation
5provided that the following conditions are met:
6
70. 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.
111. All terms of the all other applicable copyrights and licenses must be
12 followed.
132. Redistributions of source code must retain the authors' copyright
14 notice(s), this list of conditions, and the following disclaimer.
153. 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.
184. 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:
22
23 This product includes software developed by <name(s)>, The Inner
24 Net, and other contributors.
25
265. 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.
29
30THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
31EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
34DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
37ANY 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
39SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
41 If these license terms cause you a real problem, contact the author. */
42
43/* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
44
1fb05e3d
UD
45/* getaddrinfo() v1.13 */
46
46ec036d 47#include <sys/types.h>
46ec036d 48#include <stdlib.h>
1fb05e3d
UD
49#include <unistd.h>
50#include <sys/socket.h>
1fb05e3d
UD
51#include <stdio.h>
52#include <string.h>
53#include <sys/utsname.h>
54#include <sys/un.h>
46ec036d 55#include <netinet/in.h>
46ec036d 56#include <netdb.h>
1fb05e3d 57#include <errno.h>
5a97622d 58#include <arpa/inet.h>
1fb05e3d 59
46ec036d
UD
60#define GAIH_OKIFUNSPEC 0x0100
61#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
62
40a55d20
UD
63#ifndef UNIX_PATH_MAX
64#define UNIX_PATH_MAX 108
65#endif
46ec036d 66
40a55d20
UD
67struct gaih_service
68 {
69 const char *name;
70 int num;
71 };
46ec036d 72
40a55d20
UD
73struct gaih_servtuple
74 {
75 struct gaih_servtuple *next;
76 int socktype;
77 int protocol;
78 int port;
79 };
46ec036d 80
40a55d20 81static struct gaih_servtuple nullserv = { NULL, 0, 0, 0 };
46ec036d 82
40a55d20
UD
83struct gaih_addrtuple
84 {
85 struct gaih_addrtuple *next;
86 int family;
87 char addr[16];
88 };
46ec036d 89
40a55d20
UD
90struct gaih_typeproto
91 {
92 int socktype;
93 int protocol;
94 char *name;
95 };
46ec036d 96
40a55d20
UD
97static struct gaih_typeproto gaih_inet_typeproto[] =
98{
99 { 0, 0, NULL },
100 { SOCK_STREAM, IPPROTO_TCP, (char *) "tcp" },
101 { SOCK_DGRAM, IPPROTO_UDP, (char *) "udp" },
102 { 0, 0, NULL }
46ec036d
UD
103};
104
40a55d20
UD
105struct gaih
106 {
107 int family;
108 int (*gaih)(const char *name, const struct gaih_service *service,
109 const struct addrinfo *req, struct addrinfo **pai);
110 };
111
112static struct addrinfo default_hints =
113 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
114
115
116static int
117gaih_local (const char *name, const struct gaih_service *service,
118 const struct addrinfo *req, struct addrinfo **pai)
1fb05e3d
UD
119{
120 struct utsname utsname;
121
40a55d20 122 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
1fb05e3d
UD
123 if (uname(&utsname))
124 return -EAI_SYSTEM;
1fb05e3d 125
40a55d20
UD
126 if (name != NULL)
127 {
128 if (strcmp(name, "localhost") &&
129 strcmp(name, "local") &&
130 strcmp(name, "unix") &&
131 strcmp(name, utsname.nodename))
132 return GAIH_OKIFUNSPEC | -EAI_NONAME;
133 }
134
135 *pai = malloc (sizeof(struct addrinfo) + sizeof(struct sockaddr_un)
136 + ((req->ai_flags & AI_CANONNAME)
137 ? (strlen(utsname.nodename) + 1): 0));
138 if (*pai == NULL)
1fb05e3d
UD
139 return -EAI_MEMORY;
140
141 (*pai)->ai_next = NULL;
142 (*pai)->ai_flags = req->ai_flags;
143 (*pai)->ai_family = AF_LOCAL;
144 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
145 (*pai)->ai_protocol = req->ai_protocol;
146 (*pai)->ai_addrlen = sizeof(struct sockaddr_un);
147 (*pai)->ai_addr = (void *)(*pai) + sizeof(struct addrinfo);
40a55d20 148
1fb05e3d 149#if SALEN
40a55d20
UD
150 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
151 sizeof (struct sockaddr_un);
1fb05e3d 152#endif /* SALEN */
40a55d20 153
1fb05e3d
UD
154 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
155 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
40a55d20
UD
156
157 if (service)
158 {
159 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
160
161 if (strchr (service->name, '/') != NULL)
162 {
163 if (strlen (service->name) >= sizeof (sunp->sun_path))
164 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
165
166 strcpy (sunp->sun_path, service->name);
167 }
168 else
169 {
170 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
171 sizeof (sunp->sun_path))
172 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
173
174 __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
175 }
176 }
177 else
178 {
179 if (tmpnam (((struct sockaddr_un *) (*pai)->ai_addr)->sun_path) == NULL)
180 return -EAI_SYSTEM;
181 }
182
1fb05e3d 183 if (req->ai_flags & AI_CANONNAME)
40a55d20
UD
184 strcpy ((*pai)->ai_canonname = (char *)(*pai) + sizeof(struct addrinfo) +
185 sizeof(struct sockaddr_un), utsname.nodename);
1fb05e3d
UD
186 else
187 (*pai)->ai_canonname = NULL;
188 return 0;
40a55d20 189}
46ec036d 190
40a55d20
UD
191static int
192gaih_inet_serv (const char *servicename, struct gaih_typeproto *tp,
238ae1eb 193 struct gaih_servtuple *st)
46ec036d
UD
194{
195 struct servent *s;
40a55d20 196 size_t tmpbuflen = 1024;
993b3242 197 struct servent ts;
40a55d20
UD
198 char *tmpbuf;
199 int r;
200
201 do
993b3242 202 {
40a55d20
UD
203 tmpbuf = __alloca (tmpbuflen);
204 if (tmpbuf == NULL)
205 return -EAI_MEMORY;
206
207 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
208 &s);
c5f57c58 209 if (r || s == NULL)
993b3242 210 {
40a55d20
UD
211 if (errno == ERANGE)
212 tmpbuflen *= 2;
213 else
214 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
993b3242
UD
215 }
216 }
40a55d20 217 while (r);
993b3242 218
238ae1eb
UD
219 st->next = NULL;
220 st->socktype = tp->socktype;
221 st->protocol = tp->protocol;
222 st->port = s->s_port;
46ec036d
UD
223
224 return 0;
225}
226
40a55d20
UD
227#define gethosts(_family, _type) \
228 { \
229 int i, herrno; \
230 size_t tmpbuflen; \
231 struct hostent th; \
232 char *tmpbuf; \
233 tmpbuflen = 512; \
234 do { \
235 tmpbuflen *= 2; \
236 tmpbuf = __alloca (tmpbuflen); \
237 if (tmpbuf == NULL) \
238 return -EAI_MEMORY; \
239 rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
240 tmpbuflen, &h, &herrno); \
241 } while ((rc != 0) && \
242 (herrno == NETDB_INTERNAL) && (errno == ERANGE)); \
243 if ((rc != 0) && (herrno == NETDB_INTERNAL)) \
244 { \
245 __set_h_errno (herrno); \
246 return -EAI_SYSTEM; \
247 } \
248 if (h != NULL) \
249 { \
250 for (i = 0; h->h_addr_list[i]; i++) \
251 { \
252 if (*pat == NULL) \
253 { \
254 *pat = __alloca (sizeof(struct gaih_addrtuple)); \
255 if (*pat == NULL) \
256 return -EAI_MEMORY; \
257 } \
258 (*pat)->next = NULL; \
259 (*pat)->family = _family; \
260 memcpy ((*pat)->addr, h->h_addr_list[i], \
261 sizeof(_type)); \
262 pat = &((*pat)->next); \
263 } \
264 } \
265 }
266
267static int
268gaih_inet (const char *name, const struct gaih_service *service,
269 const struct addrinfo *req, struct addrinfo **pai)
46ec036d 270{
46ec036d
UD
271 struct gaih_typeproto *tp = gaih_inet_typeproto;
272 struct gaih_servtuple *st = &nullserv;
1fb05e3d 273 struct gaih_addrtuple *at = NULL;
40a55d20 274 int rc;
46ec036d 275
40a55d20
UD
276 if (req->ai_protocol || req->ai_socktype)
277 {
278 for (tp++; tp->name &&
279 ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
280 ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
281 if (tp->name == NULL)
6e4c40ba
UD
282 {
283 if (req->ai_socktype)
284 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
285 else
286 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
287 }
40a55d20
UD
288 }
289
290 if (service != NULL)
291 {
292 if (service->num < 0)
293 {
294 if (tp->name != NULL)
295 {
238ae1eb
UD
296 st = (struct gaih_servtuple *)
297 __alloca (sizeof (struct gaih_servtuple));
298
299 if ((rc = gaih_inet_serv (service->name, tp, st)))
40a55d20
UD
300 return rc;
301 }
302 else
303 {
304 struct gaih_servtuple **pst = &st;
305 for (tp++; tp->name; tp++)
306 {
238ae1eb
UD
307 struct gaih_servtuple *newp = (struct gaih_servtuple *)
308 __alloca (sizeof (struct gaih_servtuple));
309
310 if ((rc = gaih_inet_serv (service->name, tp, newp)))
40a55d20
UD
311 {
312 if (rc & GAIH_OKIFUNSPEC)
313 continue;
314 return rc;
315 }
238ae1eb
UD
316
317 *pst = newp;
318 pst = &(newp->next);
40a55d20
UD
319 }
320 if (st == &nullserv)
321 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
322 }
46ec036d 323 }
40a55d20
UD
324 else
325 {
326 st = __alloca (sizeof(struct gaih_servtuple));
327 if (st == NULL)
328 return -EAI_MEMORY;
329
330 st->next = NULL;
331 st->socktype = tp->socktype;
332 st->protocol = tp->protocol;
333 st->port = htons (service->num);
46ec036d 334 }
40a55d20
UD
335 }
336
337 if (name != NULL)
338 {
339 at = __alloca (sizeof(struct gaih_addrtuple));
340 if (at == NULL)
46ec036d
UD
341 return -EAI_MEMORY;
342
40a55d20
UD
343 at->family = 0;
344 at->next = NULL;
46ec036d 345
40a55d20
UD
346 if (at->family || !req->ai_family || (req->ai_family == AF_INET))
347 if (inet_pton (AF_INET, name, at->addr) > 0)
348 at->family = AF_INET;
46ec036d 349
40a55d20
UD
350 if (!at->family && (!req->ai_family || (req->ai_family == AF_INET6)))
351 if (inet_pton (AF_INET6, name, at->addr) > 0)
352 at->family = AF_INET6;
353
354 if (at->family == AF_UNSPEC)
355 {
356 struct hostent *h;
357 struct gaih_addrtuple **pat = &at;
358
359 if ((req->ai_family == AF_UNSPEC) || (req->ai_family == AF_INET6))
360 gethosts (AF_INET6, struct in6_addr);
361
362 if ((req->ai_family == AF_UNSPEC) || (req->ai_family == AF_INET))
363 gethosts (AF_INET, struct in_addr);
46ec036d 364 }
40a55d20
UD
365
366 if (at->family == AF_UNSPEC)
367 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
368
46ec036d 369 }
40a55d20
UD
370 else
371 {
372 at = __alloca (sizeof(struct gaih_addrtuple));
373 if (at == NULL)
374 return -EAI_MEMORY;
375
376 memset (at, 0, sizeof(struct gaih_addrtuple));
377
378 at->next = __alloca (sizeof(struct gaih_addrtuple));
379 if (at->next == NULL)
380 return -EAI_MEMORY;
381
382 at->family = AF_INET6;
86421aa5
UD
383 if ((req->ai_flags & AI_PASSIVE) == 0)
384 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
40a55d20
UD
385
386 memset (at->next, 0, sizeof(struct gaih_addrtuple));
387 at->next->family = AF_INET;
86421aa5
UD
388 if ((req->ai_flags & AI_PASSIVE) == 0)
389 *(uint32_t *) at->next->addr = htonl (INADDR_LOOPBACK);
46ec036d 390 }
1fb05e3d 391
40a55d20
UD
392 if (pai == NULL)
393 return 0;
46ec036d
UD
394
395 {
396 const char *c = NULL;
397 struct gaih_servtuple *st2;
398 struct gaih_addrtuple *at2 = at;
40a55d20
UD
399 size_t socklen, namelen;
400
f21acc89
UD
401 /*
402 buffer is the size of an unformatted IPv6 address in printable format.
403 */
404 char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
46ec036d 405
40a55d20
UD
406 while (at2 != NULL)
407 {
408 if (req->ai_flags & AI_CANONNAME)
409 {
410 struct hostent *h = NULL;
46ec036d 411
40a55d20
UD
412 int herrno;
413 struct hostent th;
414 size_t tmpbuflen = 512;
415 char *tmpbuf;
46ec036d 416
40a55d20
UD
417 do
418 {
419 tmpbuflen *= 2;
420 tmpbuf = __alloca (tmpbuflen);
46ec036d 421
40a55d20
UD
422 if (tmpbuf == NULL)
423 return -EAI_MEMORY;
424
425 rc = __gethostbyaddr_r (at2->addr,
426 ((at2->family == AF_INET6)
427 ? sizeof(struct in6_addr)
428 : sizeof(struct in_addr)),
429 at2->family, &th, tmpbuf, tmpbuflen,
430 &h, &herrno);
431
432 }
433 while ((rc != 0) && (herrno == NETDB_INTERNAL)
434 && (errno == ERANGE));
435
436 if ((rc != 0) && (herrno == NETDB_INTERNAL))
437 {
438 __set_h_errno (herrno);
439 return -EAI_SYSTEM;
440 }
441
442 if (h == NULL)
443 c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
444 else
445 c = h->h_name;
446
447 if (c == NULL)
448 return GAIH_OKIFUNSPEC | -EAI_NONAME;
449
450 namelen = strlen (c) + 1;
451 }
452 else
453 namelen = 0;
454
455 if (at2->family == AF_INET6)
456 socklen = sizeof (struct sockaddr_in6);
457 else
458 socklen = sizeof (struct sockaddr_in);
459
460 for (st2 = st; st2 != NULL; st2 = st2->next)
461 {
462 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
463 if (*pai == NULL)
464 return -EAI_MEMORY;
465
466 (*pai)->ai_flags = req->ai_flags;
467 (*pai)->ai_family = at2->family;
468 (*pai)->ai_socktype = st2->socktype;
469 (*pai)->ai_protocol = st2->protocol;
470 (*pai)->ai_addrlen = socklen;
471 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
46ec036d 472#if SALEN
40a55d20 473 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_len = i;
46ec036d 474#endif /* SALEN */
40a55d20
UD
475 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_family = at2->family;
476 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_port = st2->port;
5a97622d 477
40a55d20
UD
478 if (at2->family == AF_INET6)
479 {
480 struct sockaddr_in6 *sin6p =
481 (struct sockaddr_in6 *) (*pai)->ai_addr;
46ec036d 482
40a55d20
UD
483 sin6p->sin6_flowinfo = 0;
484 memcpy (&sin6p->sin6_addr,
485 at2->addr, sizeof (struct in6_addr));
486 }
487 else
488 {
489 struct sockaddr_in *sinp =
490 (struct sockaddr_in *) (*pai)->ai_addr;
491 memcpy (&sinp->sin_addr,
492 at2->addr, sizeof (struct in_addr));
493 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
494 }
46ec036d 495
40a55d20
UD
496 if (c)
497 {
498 (*pai)->ai_canonname = ((void *) (*pai) +
499 sizeof (struct addrinfo) + socklen);
500 strcpy ((*pai)->ai_canonname, c);
501 }
502 else
503 (*pai)->ai_canonname = NULL;
46ec036d 504
40a55d20
UD
505 (*pai)->ai_next = NULL;
506 pai = &((*pai)->ai_next);
507 }
46ec036d 508
40a55d20
UD
509 at2 = at2->next;
510 }
46ec036d 511 }
40a55d20 512 return 0;
46ec036d
UD
513}
514
40a55d20
UD
515static struct gaih gaih[] =
516 {
517 { PF_INET6, gaih_inet },
518 { PF_INET, gaih_inet },
519 { PF_LOCAL, gaih_local },
520 { PF_UNSPEC, NULL }
521 };
46ec036d 522
40a55d20
UD
523int
524getaddrinfo (const char *name, const char *service,
525 const struct addrinfo *hints, struct addrinfo **pai)
46ec036d 526{
1fb05e3d
UD
527 int i = 0, j = 0;
528 struct addrinfo *p = NULL, **end;
46ec036d
UD
529 struct gaih *g = gaih, *pg = NULL;
530 struct gaih_service gaih_service, *pservice;
531
40a55d20 532 if (name != NULL && name[0] == '*' && name[1] == 0)
1fb05e3d
UD
533 name = NULL;
534
40a55d20 535 if (service != NULL && service[0] == '*' && service[1] == 0)
1fb05e3d
UD
536 service = NULL;
537
40a55d20 538 if (name == NULL && service == NULL)
46ec036d
UD
539 return EAI_NONAME;
540
40a55d20
UD
541 if (hints == NULL)
542 hints = &default_hints;
46ec036d 543
b09bb958 544 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
46ec036d
UD
545 return EAI_BADFLAGS;
546
40a55d20 547 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
46ec036d
UD
548 return EAI_BADFLAGS;
549
40a55d20
UD
550 if (service && service[0])
551 {
552 char *c;
553 gaih_service.name = service;
554 gaih_service.num = strtoul (gaih_service.name, &c, 10);
555 if (*c)
556 gaih_service.num = -1;
1fb05e3d 557 else
40a55d20
UD
558 /* Can't specify a numerical socket unless a protocol family was
559 given. */
560 if (hints->ai_socktype == 0)
1fb05e3d 561 return EAI_SERVICE;
40a55d20
UD
562 pservice = &gaih_service;
563 }
564 else
46ec036d
UD
565 pservice = NULL;
566
1fb05e3d
UD
567 if (pai)
568 end = &p;
569 else
570 end = NULL;
571
40a55d20
UD
572 while (g->gaih)
573 {
574 if ((hints->ai_family == g->family) || (hints->ai_family == AF_UNSPEC))
575 {
576 j++;
577 if ((pg == NULL) || (pg->gaih != g->gaih))
578 {
579 pg = g;
6796bc80 580 if ((i = g->gaih (name, pservice, hints, end)))
40a55d20
UD
581 {
582 if ((hints->ai_family == AF_UNSPEC) && (i & GAIH_OKIFUNSPEC))
583 continue;
584
585 if (p)
586 freeaddrinfo (p);
587
588 return (i)?-(i & GAIH_EAI):EAI_NONAME;
589 }
590 if (end)
591 while(*end) end = &((*end)->ai_next);
592 }
46ec036d 593 }
40a55d20 594 ++g;
46ec036d 595 }
46ec036d 596
40a55d20 597 if (j == 0)
46ec036d
UD
598 return EAI_FAMILY;
599
40a55d20
UD
600 if (p)
601 {
602 *pai = p;
603 return 0;
604 }
46ec036d 605
40a55d20 606 if (pai == NULL && i == 0)
1fb05e3d
UD
607 return 0;
608
46ec036d 609 if (p)
40a55d20 610 freeaddrinfo (p);
46ec036d 611
40a55d20 612 return i ? -(i & GAIH_EAI) : EAI_NONAME;
46ec036d
UD
613}
614
40a55d20
UD
615void
616freeaddrinfo (struct addrinfo *ai)
46ec036d
UD
617{
618 struct addrinfo *p;
619
40a55d20
UD
620 while (ai != NULL)
621 {
622 p = ai;
623 ai = ai->ai_next;
624 free (p);
625 }
46ec036d 626}