]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/posix/getaddrinfo.c
Internal shadow header for <ifaddrs.h>.
[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.
aeb25823 184. [The copyright holder has authorized the removal of this clause.]
46ec036d
UD
195. Neither the name(s) of the author(s) nor the names of its contributors
20 may be used to endorse or promote products derived from this software
21 without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
24EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
27DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34 If these license terms cause you a real problem, contact the author. */
35
36/* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
37
c0bc5f7b
UD
38#include <assert.h>
39#include <errno.h>
40#include <netdb.h>
9c42c64d 41#include <resolv.h>
c0bc5f7b 42#include <stdio.h>
46ec036d 43#include <stdlib.h>
c0bc5f7b 44#include <string.h>
1fb05e3d 45#include <unistd.h>
c0bc5f7b 46#include <arpa/inet.h>
1fb05e3d 47#include <sys/socket.h>
46ec036d 48#include <netinet/in.h>
c0bc5f7b
UD
49#include <sys/types.h>
50#include <sys/un.h>
51#include <sys/utsname.h>
4fcddf8e 52#include <net/if.h>
218d76e0 53#include <nsswitch.h>
1fb05e3d 54
46ec036d
UD
55#define GAIH_OKIFUNSPEC 0x0100
56#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
57
40a55d20
UD
58#ifndef UNIX_PATH_MAX
59#define UNIX_PATH_MAX 108
60#endif
46ec036d 61
40a55d20
UD
62struct gaih_service
63 {
64 const char *name;
65 int num;
66 };
46ec036d 67
40a55d20
UD
68struct gaih_servtuple
69 {
70 struct gaih_servtuple *next;
71 int socktype;
72 int protocol;
73 int port;
74 };
46ec036d 75
3e2d61a3 76static const struct gaih_servtuple nullserv;
46ec036d 77
40a55d20
UD
78struct gaih_addrtuple
79 {
80 struct gaih_addrtuple *next;
81 int family;
82 char addr[16];
c0bc5f7b 83 uint32_t scopeid;
40a55d20 84 };
46ec036d 85
40a55d20
UD
86struct gaih_typeproto
87 {
88 int socktype;
89 int protocol;
3e2d61a3 90 char name[4];
f3ac48d0 91 int protoflag;
40a55d20 92 };
46ec036d 93
f3ac48d0
UD
94/* Values for `protoflag'. */
95#define GAI_PROTO_NOSERVICE 1
85599e53 96#define GAI_PROTO_PROTOANY 2
f3ac48d0 97
3e2d61a3 98static const struct gaih_typeproto gaih_inet_typeproto[] =
40a55d20 99{
3e2d61a3
UD
100 { 0, 0, "", 0 },
101 { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
102 { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
103 { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
104 { 0, 0, "", 0 }
46ec036d
UD
105};
106
40a55d20
UD
107struct gaih
108 {
109 int family;
110 int (*gaih)(const char *name, const struct gaih_service *service,
111 const struct addrinfo *req, struct addrinfo **pai);
112 };
113
3e2d61a3
UD
114#if PF_UNSPEC == 0
115static const struct addrinfo default_hints;
116#else
117static const struct addrinfo default_hints =
40a55d20 118 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
3e2d61a3 119#endif
40a55d20
UD
120
121
64b7897d
UD
122#if 0
123/* Using Unix sockets this way is a security risk. */
40a55d20
UD
124static int
125gaih_local (const char *name, const struct gaih_service *service,
126 const struct addrinfo *req, struct addrinfo **pai)
1fb05e3d
UD
127{
128 struct utsname utsname;
129
f1a785ac
UD
130 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
131 return GAIH_OKIFUNSPEC | -EAI_NONAME;
132
40a55d20 133 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
db7dc811 134 if (uname (&utsname) < 0)
1fb05e3d 135 return -EAI_SYSTEM;
1fb05e3d 136
40a55d20
UD
137 if (name != NULL)
138 {
139 if (strcmp(name, "localhost") &&
140 strcmp(name, "local") &&
141 strcmp(name, "unix") &&
142 strcmp(name, utsname.nodename))
143 return GAIH_OKIFUNSPEC | -EAI_NONAME;
144 }
145
a0bf6ac7
UD
146 if (req->ai_protocol || req->ai_socktype)
147 {
3e2d61a3 148 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
f3ac48d0 149
4b1fef84 150 while (tp->name[0]
f3ac48d0 151 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
3a47453d
UD
152 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
153 || (req->ai_protocol != 0
85599e53 154 && !(tp->protoflag & GAI_PROTO_PROTOANY)
3a47453d 155 && req->ai_protocol != tp->protocol)))
f3ac48d0 156 ++tp;
a0bf6ac7 157
4b1fef84 158 if (! tp->name[0])
a0bf6ac7
UD
159 {
160 if (req->ai_socktype)
161 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
162 else
163 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
164 }
165 }
166
167 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
40a55d20
UD
168 + ((req->ai_flags & AI_CANONNAME)
169 ? (strlen(utsname.nodename) + 1): 0));
170 if (*pai == NULL)
1fb05e3d
UD
171 return -EAI_MEMORY;
172
173 (*pai)->ai_next = NULL;
174 (*pai)->ai_flags = req->ai_flags;
175 (*pai)->ai_family = AF_LOCAL;
176 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
177 (*pai)->ai_protocol = req->ai_protocol;
a0bf6ac7
UD
178 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
179 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
40a55d20 180
1fb05e3d 181#if SALEN
40a55d20
UD
182 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
183 sizeof (struct sockaddr_un);
1fb05e3d 184#endif /* SALEN */
40a55d20 185
1fb05e3d
UD
186 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
187 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
40a55d20
UD
188
189 if (service)
190 {
191 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
192
193 if (strchr (service->name, '/') != NULL)
194 {
195 if (strlen (service->name) >= sizeof (sunp->sun_path))
196 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
197
198 strcpy (sunp->sun_path, service->name);
199 }
200 else
201 {
202 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
203 sizeof (sunp->sun_path))
204 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
205
206 __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
207 }
208 }
209 else
210 {
e658b54e
UD
211 /* This is a dangerous use of the interface since there is a time
212 window between the test for the file and the actual creation
213 (done by the caller) in which a file with the same name could
214 be created. */
215 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
216
217 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
218 0) != 0
219 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
40a55d20
UD
220 return -EAI_SYSTEM;
221 }
222
1fb05e3d 223 if (req->ai_flags & AI_CANONNAME)
a0bf6ac7
UD
224 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
225 + sizeof (struct sockaddr_un),
226 utsname.nodename);
1fb05e3d
UD
227 else
228 (*pai)->ai_canonname = NULL;
229 return 0;
40a55d20 230}
64b7897d 231#endif /* 0 */
46ec036d 232
40a55d20 233static int
3e2d61a3 234gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
85599e53 235 const struct addrinfo *req, struct gaih_servtuple *st)
46ec036d
UD
236{
237 struct servent *s;
40a55d20 238 size_t tmpbuflen = 1024;
993b3242 239 struct servent ts;
40a55d20
UD
240 char *tmpbuf;
241 int r;
242
243 do
993b3242 244 {
40a55d20 245 tmpbuf = __alloca (tmpbuflen);
40a55d20
UD
246
247 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
248 &s);
a0bf6ac7 249 if (r != 0 || s == NULL)
993b3242 250 {
a0bf6ac7 251 if (r == ERANGE)
40a55d20
UD
252 tmpbuflen *= 2;
253 else
254 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
993b3242
UD
255 }
256 }
40a55d20 257 while (r);
993b3242 258
238ae1eb
UD
259 st->next = NULL;
260 st->socktype = tp->socktype;
85599e53
UD
261 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
262 ? req->ai_protocol : tp->protocol);
238ae1eb 263 st->port = s->s_port;
46ec036d
UD
264
265 return 0;
266}
267
40a55d20
UD
268#define gethosts(_family, _type) \
269 { \
270 int i, herrno; \
271 size_t tmpbuflen; \
272 struct hostent th; \
eb9dc2a2 273 char *tmpbuf = NULL; \
40a55d20 274 tmpbuflen = 512; \
7813b61a 275 no_data = 0; \
40a55d20 276 do { \
218d76e0 277 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
40a55d20
UD
278 rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
279 tmpbuflen, &h, &herrno); \
4fe53b3a 280 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
fb7268b2 281 if (rc != 0) \
40a55d20 282 { \
fb7268b2
UD
283 if (herrno == NETDB_INTERNAL) \
284 { \
285 __set_h_errno (herrno); \
286 return -EAI_SYSTEM; \
287 } \
288 if (herrno == TRY_AGAIN) \
7813b61a
UD
289 no_data = EAI_AGAIN; \
290 else \
291 no_data = herrno == NO_DATA; \
40a55d20 292 } \
7813b61a 293 else if (h != NULL) \
40a55d20
UD
294 { \
295 for (i = 0; h->h_addr_list[i]; i++) \
296 { \
85599e53 297 if (*pat == NULL) { \
218d76e0 298 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
85599e53
UD
299 (*pat)->scopeid = 0; \
300 } \
40a55d20
UD
301 (*pat)->next = NULL; \
302 (*pat)->family = _family; \
303 memcpy ((*pat)->addr, h->h_addr_list[i], \
304 sizeof(_type)); \
305 pat = &((*pat)->next); \
306 } \
307 } \
308 }
309
218d76e0
UD
310#define gethosts2(_family, _type) \
311 { \
312 int i, herrno; \
313 size_t tmpbuflen; \
314 struct hostent th; \
315 char *tmpbuf = NULL; \
316 tmpbuflen = 512; \
317 no_data = 0; \
318 do { \
319 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
320 rc = 0; \
321 status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, \
322 tmpbuflen, &rc, &herrno)); \
323 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
324 if (status == NSS_STATUS_SUCCESS && rc == 0) \
325 h = &th; \
326 else \
327 h = NULL; \
328 if (rc != 0) \
329 { \
330 if (herrno == NETDB_INTERNAL) \
331 { \
332 __set_h_errno (herrno); \
333 return -EAI_SYSTEM; \
334 } \
335 if (herrno == TRY_AGAIN) \
336 no_data = EAI_AGAIN; \
337 else \
338 no_data = herrno == NO_DATA; \
339 } \
340 else if (h != NULL) \
341 { \
342 for (i = 0; h->h_addr_list[i]; i++) \
343 { \
344 if (*pat == NULL) { \
345 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
346 (*pat)->scopeid = 0; \
347 } \
348 (*pat)->next = NULL; \
349 (*pat)->family = _family; \
350 memcpy ((*pat)->addr, h->h_addr_list[i], \
351 sizeof(_type)); \
352 pat = &((*pat)->next); \
353 } \
354 } \
355 }
356
357typedef enum nss_status (*nss_gethostbyname2_r)
358 (const char *name, int af, struct hostent *host,
359 char *buffer, size_t buflen, int *errnop,
360 int *h_errnop);
361extern service_user *__nss_hosts_database attribute_hidden;
362
40a55d20
UD
363static int
364gaih_inet (const char *name, const struct gaih_service *service,
365 const struct addrinfo *req, struct addrinfo **pai)
46ec036d 366{
3e2d61a3
UD
367 const struct gaih_typeproto *tp = gaih_inet_typeproto;
368 struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
1fb05e3d 369 struct gaih_addrtuple *at = NULL;
40a55d20 370 int rc;
46ec036d 371
40a55d20
UD
372 if (req->ai_protocol || req->ai_socktype)
373 {
f3ac48d0
UD
374 ++tp;
375
4b1fef84 376 while (tp->name[0]
3a47453d
UD
377 && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
378 || (req->ai_protocol != 0
85599e53 379 && !(tp->protoflag & GAI_PROTO_PROTOANY)
3a47453d 380 && req->ai_protocol != tp->protocol)))
f3ac48d0
UD
381 ++tp;
382
4b1fef84 383 if (! tp->name[0])
6e4c40ba
UD
384 {
385 if (req->ai_socktype)
386 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
387 else
388 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
389 }
40a55d20
UD
390 }
391
392 if (service != NULL)
393 {
f3ac48d0
UD
394 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
395 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
396
40a55d20
UD
397 if (service->num < 0)
398 {
4b1fef84 399 if (tp->name[0])
40a55d20 400 {
238ae1eb
UD
401 st = (struct gaih_servtuple *)
402 __alloca (sizeof (struct gaih_servtuple));
403
85599e53 404 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
40a55d20
UD
405 return rc;
406 }
407 else
408 {
409 struct gaih_servtuple **pst = &st;
4b1fef84 410 for (tp++; tp->name[0]; tp++)
40a55d20 411 {
3a47453d
UD
412 struct gaih_servtuple *newp;
413
414 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
415 continue;
416
84a4fd33
UD
417 if (req->ai_socktype != 0
418 && req->ai_socktype != tp->socktype)
419 continue;
85599e53
UD
420 if (req->ai_protocol != 0
421 && !(tp->protoflag & GAI_PROTO_PROTOANY)
422 && req->ai_protocol != tp->protocol)
423 continue;
84a4fd33 424
3a47453d 425 newp = (struct gaih_servtuple *)
238ae1eb
UD
426 __alloca (sizeof (struct gaih_servtuple));
427
85599e53 428 if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
40a55d20
UD
429 {
430 if (rc & GAIH_OKIFUNSPEC)
431 continue;
432 return rc;
433 }
238ae1eb
UD
434
435 *pst = newp;
436 pst = &(newp->next);
40a55d20 437 }
3e2d61a3 438 if (st == (struct gaih_servtuple *) &nullserv)
40a55d20
UD
439 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
440 }
46ec036d 441 }
40a55d20
UD
442 else
443 {
a0bf6ac7 444 st = __alloca (sizeof (struct gaih_servtuple));
40a55d20
UD
445 st->next = NULL;
446 st->socktype = tp->socktype;
85599e53
UD
447 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
448 ? req->ai_protocol : tp->protocol);
40a55d20 449 st->port = htons (service->num);
46ec036d 450 }
40a55d20 451 }
84a4fd33
UD
452 else if (req->ai_socktype || req->ai_protocol)
453 {
454 st = __alloca (sizeof (struct gaih_servtuple));
455 st->next = NULL;
85599e53
UD
456 st->socktype = tp->socktype;
457 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
458 ? req->ai_protocol : tp->protocol);
84a4fd33
UD
459 st->port = 0;
460 }
461 else
462 {
463 /* Neither socket type nor protocol is set. Return all socket types
464 we know about. */
465 struct gaih_servtuple **lastp = &st;
4b1fef84 466 for (++tp; tp->name[0]; ++tp)
84a4fd33
UD
467 {
468 struct gaih_servtuple *newp;
469
470 newp = __alloca (sizeof (struct gaih_servtuple));
471 newp->next = NULL;
472 newp->socktype = tp->socktype;
473 newp->protocol = tp->protocol;
474 newp->port = 0;
475
476 *lastp = newp;
477 lastp = &newp->next;
478 }
479 }
40a55d20
UD
480
481 if (name != NULL)
482 {
a0bf6ac7 483 at = __alloca (sizeof (struct gaih_addrtuple));
46ec036d 484
a0bf6ac7 485 at->family = AF_UNSPEC;
c0bc5f7b 486 at->scopeid = 0;
40a55d20 487 at->next = NULL;
46ec036d 488
a0bf6ac7
UD
489 if (inet_pton (AF_INET, name, at->addr) > 0)
490 {
491 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
492 at->family = AF_INET;
493 else
494 return -EAI_ADDRFAMILY;
495 }
46ec036d 496
c0bc5f7b 497 if (at->family == AF_UNSPEC)
a0bf6ac7 498 {
c0bc5f7b
UD
499 char *namebuf = strdupa (name);
500 char *scope_delim;
501
502 scope_delim = strchr (namebuf, SCOPE_DELIMITER);
503 if (scope_delim != NULL)
504 *scope_delim = '\0';
505
506 if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
507 {
c0bc5f7b
UD
508 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
509 at->family = AF_INET6;
510 else
511 return -EAI_ADDRFAMILY;
512
513 if (scope_delim != NULL)
514 {
515 int try_numericscope = 0;
516 if (IN6_IS_ADDR_LINKLOCAL (at->addr)
517 || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
518 {
519 at->scopeid = if_nametoindex (scope_delim + 1);
520 if (at->scopeid == 0)
521 try_numericscope = 1;
522 }
523 else
524 try_numericscope = 1;
525
526 if (try_numericscope != 0)
527 {
528 char *end;
529 assert (sizeof (uint32_t) <= sizeof (unsigned long));
530 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
531 10);
532 if (*end != '\0')
533 return GAIH_OKIFUNSPEC | -EAI_NONAME;
534 }
535 }
536 }
a0bf6ac7 537 }
40a55d20 538
ce75c139 539 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
40a55d20
UD
540 {
541 struct hostent *h;
542 struct gaih_addrtuple **pat = &at;
a0bf6ac7 543 int no_data = 0;
218d76e0 544 int no_inet6_data = 0;
9c42c64d
UD
545 int old_res_options = _res.options;
546
547 /* If we are looking for both IPv4 and IPv6 address we don't
548 want the lookup functions to automatically promote IPv4
549 addresses to IPv6 addresses. Currently this is decided
550 by setting the RES_USE_INET6 bit in _res.options. */
551 if (req->ai_family == AF_UNSPEC)
218d76e0
UD
552 {
553 service_user *nip = NULL;
554 enum nss_status inet6_status, status = NSS_STATUS_UNAVAIL;
555 int no_more;
556 nss_gethostbyname2_r fct;
40a55d20 557
218d76e0
UD
558 if (__nss_hosts_database != NULL)
559 {
560 no_more = 0;
561 nip = __nss_hosts_database;
562 }
563 else
564 no_more = __nss_database_lookup ("hosts", NULL,
565 "dns [!UNAVAIL=return] files", &nip);
40a55d20 566
218d76e0 567 _res.options &= ~RES_USE_INET6;
9c42c64d 568
218d76e0
UD
569 while (!no_more)
570 {
571 fct = __nss_lookup_function (nip, "gethostbyname2_r");
572
64ab2317
UD
573 if (fct != NULL)
574 {
575 gethosts2 (AF_INET6, struct in6_addr);
576 no_inet6_data = no_data;
577 inet6_status = status;
578 gethosts2 (AF_INET, struct in_addr);
579
580 /* If we found one address for AF_INET or AF_INET6,
581 don't continue the search. */
582 if (inet6_status == NSS_STATUS_SUCCESS ||
583 status == NSS_STATUS_SUCCESS)
584 break;
585
586 /* We can have different states for AF_INET
587 and AF_INET6. Try to find a usefull one for
588 both. */
589 if (inet6_status == NSS_STATUS_TRYAGAIN)
590 status = NSS_STATUS_TRYAGAIN;
591 else if (status == NSS_STATUS_UNAVAIL &&
592 inet6_status != NSS_STATUS_UNAVAIL)
593 status = inet6_status;
594 }
218d76e0
UD
595
596 if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
597 break;
598
599 if (nip->next == NULL)
600 no_more = -1;
601 else
602 nip = nip->next;
603 }
604
605 _res.options = old_res_options;
606 }
607 else if (req->ai_family == AF_INET6)
608 {
609 gethosts (AF_INET6, struct in6_addr);
610 no_inet6_data = no_data;
611 }
612 else if (req->ai_family == AF_INET)
40a55d20 613 gethosts (AF_INET, struct in_addr);
a0bf6ac7 614
5ad81f40 615 if (no_data != 0 && no_inet6_data != 0)
7813b61a
UD
616 {
617 /* If both requests timed out report this. */
618 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
619 return -EAI_AGAIN;
620
621 /* We made requests but they turned out no data. The name
622 is known, though. */
623 return (GAIH_OKIFUNSPEC | -EAI_NODATA);
624 }
46ec036d 625 }
40a55d20
UD
626
627 if (at->family == AF_UNSPEC)
628 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
46ec036d 629 }
40a55d20
UD
630 else
631 {
7396d844 632 struct gaih_addrtuple *atr;
a0bf6ac7
UD
633 atr = at = __alloca (sizeof (struct gaih_addrtuple));
634 memset (at, '\0', sizeof (struct gaih_addrtuple));
40a55d20 635
7396d844
UD
636 if (req->ai_family == 0)
637 {
a0bf6ac7
UD
638 at->next = __alloca (sizeof (struct gaih_addrtuple));
639 memset (at->next, '\0', sizeof (struct gaih_addrtuple));
7396d844 640 }
40a55d20 641
7396d844
UD
642 if (req->ai_family == 0 || req->ai_family == AF_INET6)
643 {
644 at->family = AF_INET6;
645 if ((req->ai_flags & AI_PASSIVE) == 0)
a585ba22 646 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
7396d844
UD
647 atr = at->next;
648 }
40a55d20 649
7396d844
UD
650 if (req->ai_family == 0 || req->ai_family == AF_INET)
651 {
652 atr->family = AF_INET;
653 if ((req->ai_flags & AI_PASSIVE) == 0)
654 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
655 }
46ec036d 656 }
1fb05e3d 657
40a55d20
UD
658 if (pai == NULL)
659 return 0;
46ec036d
UD
660
661 {
662 const char *c = NULL;
663 struct gaih_servtuple *st2;
664 struct gaih_addrtuple *at2 = at;
40a55d20 665 size_t socklen, namelen;
85599e53 666 sa_family_t family;
40a55d20 667
f21acc89
UD
668 /*
669 buffer is the size of an unformatted IPv6 address in printable format.
670 */
40a55d20
UD
671 while (at2 != NULL)
672 {
673 if (req->ai_flags & AI_CANONNAME)
674 {
675 struct hostent *h = NULL;
46ec036d 676
40a55d20
UD
677 int herrno;
678 struct hostent th;
679 size_t tmpbuflen = 512;
2d37d6da 680 char *tmpbuf = NULL;
46ec036d 681
40a55d20
UD
682 do
683 {
2d37d6da 684 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
40a55d20
UD
685 rc = __gethostbyaddr_r (at2->addr,
686 ((at2->family == AF_INET6)
687 ? sizeof(struct in6_addr)
688 : sizeof(struct in_addr)),
689 at2->family, &th, tmpbuf, tmpbuflen,
690 &h, &herrno);
691
692 }
2d37d6da 693 while (rc == ERANGE && herrno == NETDB_INTERNAL);
40a55d20 694
a0bf6ac7 695 if (rc != 0 && herrno == NETDB_INTERNAL)
40a55d20
UD
696 {
697 __set_h_errno (herrno);
698 return -EAI_SYSTEM;
699 }
700
160d067b 701 if (h != NULL)
40a55d20 702 c = h->h_name;
160d067b
UD
703 else
704 {
705 /* We have to try to get the canonical in some other
706 way. If we are looking for either AF_INET or
707 AF_INET6 try the other line. */
708 if (req->ai_family == AF_UNSPEC)
709 {
710 struct addrinfo *p = NULL;
711 struct addrinfo **end = &p;
712 struct addrinfo localreq = *req;
713 struct addrinfo *runp;
714
715 localreq.ai_family = AF_INET + AF_INET6 - at2->family;
716 (void) gaih_inet (name, service, &localreq, end);
717
718 runp = p;
719 while (runp != NULL)
720 {
721 if (p->ai_canonname != name)
722 {
723 c = strdupa (p->ai_canonname);
724 break;
725 }
726 runp = runp->ai_next;
727 }
728
729 freeaddrinfo (p);
730 }
731
732 /* If this code is used the missing canonical name is
733 substituted with the name passed in by the user. */
734 if (c == NULL)
735 c = name;
736 }
40a55d20
UD
737
738 if (c == NULL)
739 return GAIH_OKIFUNSPEC | -EAI_NONAME;
740
741 namelen = strlen (c) + 1;
742 }
743 else
744 namelen = 0;
745
746 if (at2->family == AF_INET6)
85599e53
UD
747 {
748 family = AF_INET6;
749 socklen = sizeof (struct sockaddr_in6);
750 }
40a55d20 751 else
85599e53
UD
752 {
753 family = AF_INET;
754 socklen = sizeof (struct sockaddr_in);
755 }
40a55d20
UD
756
757 for (st2 = st; st2 != NULL; st2 = st2->next)
758 {
759 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
760 if (*pai == NULL)
761 return -EAI_MEMORY;
762
763 (*pai)->ai_flags = req->ai_flags;
85599e53 764 (*pai)->ai_family = family;
40a55d20
UD
765 (*pai)->ai_socktype = st2->socktype;
766 (*pai)->ai_protocol = st2->protocol;
767 (*pai)->ai_addrlen = socklen;
768 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
46ec036d 769#if SALEN
c0bc5f7b 770 (*pai)->ai_addr->sa_len = socklen;
46ec036d 771#endif /* SALEN */
85599e53 772 (*pai)->ai_addr->sa_family = family;
5a97622d 773
85599e53 774 if (family == AF_INET6)
40a55d20
UD
775 {
776 struct sockaddr_in6 *sin6p =
777 (struct sockaddr_in6 *) (*pai)->ai_addr;
46ec036d 778
40a55d20
UD
779 sin6p->sin6_flowinfo = 0;
780 memcpy (&sin6p->sin6_addr,
781 at2->addr, sizeof (struct in6_addr));
c0bc5f7b
UD
782 sin6p->sin6_port = st2->port;
783 sin6p->sin6_scope_id = at2->scopeid;
40a55d20
UD
784 }
785 else
786 {
787 struct sockaddr_in *sinp =
788 (struct sockaddr_in *) (*pai)->ai_addr;
789 memcpy (&sinp->sin_addr,
790 at2->addr, sizeof (struct in_addr));
c0bc5f7b 791 sinp->sin_port = st2->port;
40a55d20
UD
792 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
793 }
46ec036d 794
40a55d20
UD
795 if (c)
796 {
797 (*pai)->ai_canonname = ((void *) (*pai) +
798 sizeof (struct addrinfo) + socklen);
799 strcpy ((*pai)->ai_canonname, c);
800 }
801 else
802 (*pai)->ai_canonname = NULL;
46ec036d 803
40a55d20
UD
804 (*pai)->ai_next = NULL;
805 pai = &((*pai)->ai_next);
806 }
46ec036d 807
40a55d20
UD
808 at2 = at2->next;
809 }
46ec036d 810 }
40a55d20 811 return 0;
46ec036d
UD
812}
813
40a55d20
UD
814static struct gaih gaih[] =
815 {
816 { PF_INET6, gaih_inet },
817 { PF_INET, gaih_inet },
64b7897d 818#if 0
40a55d20 819 { PF_LOCAL, gaih_local },
64b7897d 820#endif
40a55d20
UD
821 { PF_UNSPEC, NULL }
822 };
46ec036d 823
40a55d20
UD
824int
825getaddrinfo (const char *name, const char *service,
826 const struct addrinfo *hints, struct addrinfo **pai)
46ec036d 827{
a0bf6ac7 828 int i = 0, j = 0, last_i = 0;
1fb05e3d 829 struct addrinfo *p = NULL, **end;
46ec036d
UD
830 struct gaih *g = gaih, *pg = NULL;
831 struct gaih_service gaih_service, *pservice;
832
40a55d20 833 if (name != NULL && name[0] == '*' && name[1] == 0)
1fb05e3d
UD
834 name = NULL;
835
40a55d20 836 if (service != NULL && service[0] == '*' && service[1] == 0)
1fb05e3d
UD
837 service = NULL;
838
40a55d20 839 if (name == NULL && service == NULL)
46ec036d
UD
840 return EAI_NONAME;
841
40a55d20
UD
842 if (hints == NULL)
843 hints = &default_hints;
46ec036d 844
b09bb958 845 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
46ec036d
UD
846 return EAI_BADFLAGS;
847
40a55d20 848 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
46ec036d
UD
849 return EAI_BADFLAGS;
850
40a55d20
UD
851 if (service && service[0])
852 {
853 char *c;
854 gaih_service.name = service;
855 gaih_service.num = strtoul (gaih_service.name, &c, 10);
856 if (*c)
857 gaih_service.num = -1;
1fb05e3d 858 else
40a55d20
UD
859 /* Can't specify a numerical socket unless a protocol family was
860 given. */
85599e53 861 if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
1fb05e3d 862 return EAI_SERVICE;
40a55d20
UD
863 pservice = &gaih_service;
864 }
865 else
46ec036d
UD
866 pservice = NULL;
867
1fb05e3d
UD
868 if (pai)
869 end = &p;
870 else
871 end = NULL;
872
40a55d20
UD
873 while (g->gaih)
874 {
7396d844 875 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
40a55d20
UD
876 {
877 j++;
7396d844 878 if (pg == NULL || pg->gaih != g->gaih)
40a55d20
UD
879 {
880 pg = g;
7396d844
UD
881 i = g->gaih (name, pservice, hints, end);
882 if (i != 0)
40a55d20 883 {
a0bf6ac7
UD
884 /* EAI_NODATA is a more specific result as it says that
885 we found a result but it is not usable. */
886 if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
887 last_i = i;
888
7396d844 889 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
160d067b
UD
890 {
891 ++g;
892 continue;
893 }
40a55d20 894
160d067b 895 freeaddrinfo (p);
40a55d20 896
7396d844 897 return -(i & GAIH_EAI);
40a55d20
UD
898 }
899 if (end)
900 while(*end) end = &((*end)->ai_next);
901 }
46ec036d 902 }
40a55d20 903 ++g;
46ec036d 904 }
46ec036d 905
40a55d20 906 if (j == 0)
46ec036d
UD
907 return EAI_FAMILY;
908
40a55d20
UD
909 if (p)
910 {
911 *pai = p;
912 return 0;
913 }
46ec036d 914
a0bf6ac7 915 if (pai == NULL && last_i == 0)
1fb05e3d
UD
916 return 0;
917
160d067b 918 freeaddrinfo (p);
46ec036d 919
a0bf6ac7 920 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
46ec036d 921}
9b0b40d3 922libc_hidden_def (getaddrinfo)
46ec036d 923
40a55d20
UD
924void
925freeaddrinfo (struct addrinfo *ai)
46ec036d
UD
926{
927 struct addrinfo *p;
928
40a55d20
UD
929 while (ai != NULL)
930 {
931 p = ai;
932 ai = ai->ai_next;
933 free (p);
934 }
46ec036d 935}
9b0b40d3 936libc_hidden_def (freeaddrinfo)