]> sourceware.org Git - glibc.git/blame - sysdeps/posix/getaddrinfo.c
Update.
[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); \
4fe53b3a 241 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
40a55d20
UD
242 if ((rc != 0) && (herrno == NETDB_INTERNAL)) \
243 { \
244 __set_h_errno (herrno); \
245 return -EAI_SYSTEM; \
246 } \
247 if (h != NULL) \
248 { \
249 for (i = 0; h->h_addr_list[i]; i++) \
250 { \
251 if (*pat == NULL) \
252 { \
253 *pat = __alloca (sizeof(struct gaih_addrtuple)); \
254 if (*pat == NULL) \
255 return -EAI_MEMORY; \
256 } \
257 (*pat)->next = NULL; \
258 (*pat)->family = _family; \
259 memcpy ((*pat)->addr, h->h_addr_list[i], \
260 sizeof(_type)); \
261 pat = &((*pat)->next); \
262 } \
263 } \
264 }
265
266static int
267gaih_inet (const char *name, const struct gaih_service *service,
268 const struct addrinfo *req, struct addrinfo **pai)
46ec036d 269{
46ec036d
UD
270 struct gaih_typeproto *tp = gaih_inet_typeproto;
271 struct gaih_servtuple *st = &nullserv;
1fb05e3d 272 struct gaih_addrtuple *at = NULL;
40a55d20 273 int rc;
46ec036d 274
40a55d20
UD
275 if (req->ai_protocol || req->ai_socktype)
276 {
277 for (tp++; tp->name &&
278 ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
279 ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
280 if (tp->name == NULL)
6e4c40ba
UD
281 {
282 if (req->ai_socktype)
283 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
284 else
285 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
286 }
40a55d20
UD
287 }
288
289 if (service != NULL)
290 {
291 if (service->num < 0)
292 {
293 if (tp->name != NULL)
294 {
238ae1eb
UD
295 st = (struct gaih_servtuple *)
296 __alloca (sizeof (struct gaih_servtuple));
297
298 if ((rc = gaih_inet_serv (service->name, tp, st)))
40a55d20
UD
299 return rc;
300 }
301 else
302 {
303 struct gaih_servtuple **pst = &st;
304 for (tp++; tp->name; tp++)
305 {
238ae1eb
UD
306 struct gaih_servtuple *newp = (struct gaih_servtuple *)
307 __alloca (sizeof (struct gaih_servtuple));
308
309 if ((rc = gaih_inet_serv (service->name, tp, newp)))
40a55d20
UD
310 {
311 if (rc & GAIH_OKIFUNSPEC)
312 continue;
313 return rc;
314 }
238ae1eb
UD
315
316 *pst = newp;
317 pst = &(newp->next);
40a55d20
UD
318 }
319 if (st == &nullserv)
320 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
321 }
46ec036d 322 }
40a55d20
UD
323 else
324 {
325 st = __alloca (sizeof(struct gaih_servtuple));
326 if (st == NULL)
327 return -EAI_MEMORY;
328
329 st->next = NULL;
330 st->socktype = tp->socktype;
331 st->protocol = tp->protocol;
332 st->port = htons (service->num);
46ec036d 333 }
40a55d20
UD
334 }
335
336 if (name != NULL)
337 {
338 at = __alloca (sizeof(struct gaih_addrtuple));
46ec036d 339
40a55d20
UD
340 at->family = 0;
341 at->next = NULL;
46ec036d 342
7396d844 343 if (req->ai_family == 0 || req->ai_family == AF_INET)
40a55d20
UD
344 if (inet_pton (AF_INET, name, at->addr) > 0)
345 at->family = AF_INET;
46ec036d 346
7396d844 347 if (!at->family && (req->ai_family == 0 || req->ai_family == AF_INET6))
40a55d20
UD
348 if (inet_pton (AF_INET6, name, at->addr) > 0)
349 at->family = AF_INET6;
350
351 if (at->family == AF_UNSPEC)
352 {
353 struct hostent *h;
354 struct gaih_addrtuple **pat = &at;
355
7396d844 356 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
40a55d20
UD
357 gethosts (AF_INET6, struct in6_addr);
358
7396d844 359 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
40a55d20 360 gethosts (AF_INET, struct in_addr);
46ec036d 361 }
40a55d20
UD
362
363 if (at->family == AF_UNSPEC)
364 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
365
46ec036d 366 }
40a55d20
UD
367 else
368 {
7396d844
UD
369 struct gaih_addrtuple *atr;
370 atr = at = __alloca (sizeof(struct gaih_addrtuple));
40a55d20
UD
371 memset (at, 0, sizeof(struct gaih_addrtuple));
372
7396d844
UD
373 if (req->ai_family == 0)
374 {
375 at->next = __alloca (sizeof(struct gaih_addrtuple));
376 memset (at->next, 0, sizeof(struct gaih_addrtuple));
377 }
40a55d20 378
7396d844
UD
379 if (req->ai_family == 0 || req->ai_family == AF_INET6)
380 {
381 at->family = AF_INET6;
382 if ((req->ai_flags & AI_PASSIVE) == 0)
383 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
384 atr = at->next;
385 }
40a55d20 386
7396d844
UD
387 if (req->ai_family == 0 || req->ai_family == AF_INET)
388 {
389 atr->family = AF_INET;
390 if ((req->ai_flags & AI_PASSIVE) == 0)
391 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
392 }
46ec036d 393 }
1fb05e3d 394
40a55d20
UD
395 if (pai == NULL)
396 return 0;
46ec036d
UD
397
398 {
399 const char *c = NULL;
400 struct gaih_servtuple *st2;
401 struct gaih_addrtuple *at2 = at;
40a55d20
UD
402 size_t socklen, namelen;
403
f21acc89
UD
404 /*
405 buffer is the size of an unformatted IPv6 address in printable format.
406 */
407 char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
46ec036d 408
40a55d20
UD
409 while (at2 != NULL)
410 {
411 if (req->ai_flags & AI_CANONNAME)
412 {
413 struct hostent *h = NULL;
46ec036d 414
40a55d20
UD
415 int herrno;
416 struct hostent th;
417 size_t tmpbuflen = 512;
418 char *tmpbuf;
46ec036d 419
40a55d20
UD
420 do
421 {
422 tmpbuflen *= 2;
423 tmpbuf = __alloca (tmpbuflen);
46ec036d 424
40a55d20
UD
425 if (tmpbuf == NULL)
426 return -EAI_MEMORY;
427
428 rc = __gethostbyaddr_r (at2->addr,
429 ((at2->family == AF_INET6)
430 ? sizeof(struct in6_addr)
431 : sizeof(struct in_addr)),
432 at2->family, &th, tmpbuf, tmpbuflen,
433 &h, &herrno);
434
435 }
436 while ((rc != 0) && (herrno == NETDB_INTERNAL)
437 && (errno == ERANGE));
438
439 if ((rc != 0) && (herrno == NETDB_INTERNAL))
440 {
441 __set_h_errno (herrno);
442 return -EAI_SYSTEM;
443 }
444
445 if (h == NULL)
446 c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
447 else
448 c = h->h_name;
449
450 if (c == NULL)
451 return GAIH_OKIFUNSPEC | -EAI_NONAME;
452
453 namelen = strlen (c) + 1;
454 }
455 else
456 namelen = 0;
457
458 if (at2->family == AF_INET6)
459 socklen = sizeof (struct sockaddr_in6);
460 else
461 socklen = sizeof (struct sockaddr_in);
462
463 for (st2 = st; st2 != NULL; st2 = st2->next)
464 {
465 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
466 if (*pai == NULL)
467 return -EAI_MEMORY;
468
469 (*pai)->ai_flags = req->ai_flags;
470 (*pai)->ai_family = at2->family;
471 (*pai)->ai_socktype = st2->socktype;
472 (*pai)->ai_protocol = st2->protocol;
473 (*pai)->ai_addrlen = socklen;
474 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
46ec036d 475#if SALEN
40a55d20 476 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_len = i;
46ec036d 477#endif /* SALEN */
40a55d20
UD
478 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_family = at2->family;
479 ((struct sockaddr_in *) (*pai)->ai_addr)->sin_port = st2->port;
5a97622d 480
40a55d20
UD
481 if (at2->family == AF_INET6)
482 {
483 struct sockaddr_in6 *sin6p =
484 (struct sockaddr_in6 *) (*pai)->ai_addr;
46ec036d 485
40a55d20
UD
486 sin6p->sin6_flowinfo = 0;
487 memcpy (&sin6p->sin6_addr,
488 at2->addr, sizeof (struct in6_addr));
489 }
490 else
491 {
492 struct sockaddr_in *sinp =
493 (struct sockaddr_in *) (*pai)->ai_addr;
494 memcpy (&sinp->sin_addr,
495 at2->addr, sizeof (struct in_addr));
496 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
497 }
46ec036d 498
40a55d20
UD
499 if (c)
500 {
501 (*pai)->ai_canonname = ((void *) (*pai) +
502 sizeof (struct addrinfo) + socklen);
503 strcpy ((*pai)->ai_canonname, c);
504 }
505 else
506 (*pai)->ai_canonname = NULL;
46ec036d 507
40a55d20
UD
508 (*pai)->ai_next = NULL;
509 pai = &((*pai)->ai_next);
510 }
46ec036d 511
40a55d20
UD
512 at2 = at2->next;
513 }
46ec036d 514 }
40a55d20 515 return 0;
46ec036d
UD
516}
517
40a55d20
UD
518static struct gaih gaih[] =
519 {
520 { PF_INET6, gaih_inet },
521 { PF_INET, gaih_inet },
522 { PF_LOCAL, gaih_local },
523 { PF_UNSPEC, NULL }
524 };
46ec036d 525
40a55d20
UD
526int
527getaddrinfo (const char *name, const char *service,
528 const struct addrinfo *hints, struct addrinfo **pai)
46ec036d 529{
1fb05e3d
UD
530 int i = 0, j = 0;
531 struct addrinfo *p = NULL, **end;
46ec036d
UD
532 struct gaih *g = gaih, *pg = NULL;
533 struct gaih_service gaih_service, *pservice;
534
40a55d20 535 if (name != NULL && name[0] == '*' && name[1] == 0)
1fb05e3d
UD
536 name = NULL;
537
40a55d20 538 if (service != NULL && service[0] == '*' && service[1] == 0)
1fb05e3d
UD
539 service = NULL;
540
40a55d20 541 if (name == NULL && service == NULL)
46ec036d
UD
542 return EAI_NONAME;
543
40a55d20
UD
544 if (hints == NULL)
545 hints = &default_hints;
46ec036d 546
b09bb958 547 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
46ec036d
UD
548 return EAI_BADFLAGS;
549
40a55d20 550 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
46ec036d
UD
551 return EAI_BADFLAGS;
552
40a55d20
UD
553 if (service && service[0])
554 {
555 char *c;
556 gaih_service.name = service;
557 gaih_service.num = strtoul (gaih_service.name, &c, 10);
558 if (*c)
559 gaih_service.num = -1;
1fb05e3d 560 else
40a55d20
UD
561 /* Can't specify a numerical socket unless a protocol family was
562 given. */
563 if (hints->ai_socktype == 0)
1fb05e3d 564 return EAI_SERVICE;
40a55d20
UD
565 pservice = &gaih_service;
566 }
567 else
46ec036d
UD
568 pservice = NULL;
569
1fb05e3d
UD
570 if (pai)
571 end = &p;
572 else
573 end = NULL;
574
40a55d20
UD
575 while (g->gaih)
576 {
7396d844 577 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
40a55d20
UD
578 {
579 j++;
7396d844 580 if (pg == NULL || pg->gaih != g->gaih)
40a55d20
UD
581 {
582 pg = g;
7396d844
UD
583 i = g->gaih (name, pservice, hints, end);
584 if (i != 0)
40a55d20 585 {
7396d844 586 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
40a55d20
UD
587 continue;
588
589 if (p)
590 freeaddrinfo (p);
591
7396d844 592 return -(i & GAIH_EAI);
40a55d20
UD
593 }
594 if (end)
595 while(*end) end = &((*end)->ai_next);
596 }
46ec036d 597 }
40a55d20 598 ++g;
46ec036d 599 }
46ec036d 600
40a55d20 601 if (j == 0)
46ec036d
UD
602 return EAI_FAMILY;
603
40a55d20
UD
604 if (p)
605 {
606 *pai = p;
607 return 0;
608 }
46ec036d 609
40a55d20 610 if (pai == NULL && i == 0)
1fb05e3d
UD
611 return 0;
612
46ec036d 613 if (p)
40a55d20 614 freeaddrinfo (p);
46ec036d 615
40a55d20 616 return i ? -(i & GAIH_EAI) : EAI_NONAME;
46ec036d
UD
617}
618
40a55d20
UD
619void
620freeaddrinfo (struct addrinfo *ai)
46ec036d
UD
621{
622 struct addrinfo *p;
623
40a55d20
UD
624 while (ai != NULL)
625 {
626 p = ai;
627 ai = ai->ai_next;
628 free (p);
629 }
46ec036d 630}
This page took 0.161187 seconds and 5 git commands to generate.