]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/posix/getaddrinfo.c
* nscd/connections.c (dbs): Initialize .prunelock.
[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 38#include <assert.h>
77dd4c3b 39#include <ctype.h>
c0bc5f7b 40#include <errno.h>
925c3c5c 41#include <ifaddrs.h>
c0bc5f7b 42#include <netdb.h>
9c42c64d 43#include <resolv.h>
f24dca48 44#include <stdbool.h>
c0bc5f7b 45#include <stdio.h>
77dd4c3b 46#include <stdio_ext.h>
46ec036d 47#include <stdlib.h>
c0bc5f7b 48#include <string.h>
c0bc5f7b 49#include <arpa/inet.h>
77dd4c3b 50#include <net/if.h>
a334319f 51#include <netinet/in.h>
77dd4c3b
UD
52#include <sys/socket.h>
53#include <sys/stat.h>
c0bc5f7b
UD
54#include <sys/types.h>
55#include <sys/un.h>
56#include <sys/utsname.h>
77dd4c3b 57#include <unistd.h>
218d76e0 58#include <nsswitch.h>
77dd4c3b 59#include <bits/libc-lock.h>
5ddb5bf5 60#include <not-cancel.h>
d19687d6 61#include <nscd/nscd-client.h>
62417d7e 62#include <nscd/nscd_proto.h>
1fb05e3d 63
de079fee 64#ifdef HAVE_LIBIDN
e2fd3cbe 65extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
de079fee
UD
66extern int __idna_to_unicode_lzlz (const char *input, char **output,
67 int flags);
68# include <libidn/idna.h>
69#endif
e2fd3cbe 70
46ec036d
UD
71#define GAIH_OKIFUNSPEC 0x0100
72#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
73
40a55d20 74#ifndef UNIX_PATH_MAX
3af48b5b 75# define UNIX_PATH_MAX 108
40a55d20 76#endif
46ec036d 77
40a55d20
UD
78struct gaih_service
79 {
80 const char *name;
81 int num;
82 };
46ec036d 83
40a55d20
UD
84struct gaih_servtuple
85 {
86 struct gaih_servtuple *next;
87 int socktype;
88 int protocol;
89 int port;
90 };
46ec036d 91
3e2d61a3 92static const struct gaih_servtuple nullserv;
46ec036d 93
40a55d20
UD
94struct gaih_addrtuple
95 {
96 struct gaih_addrtuple *next;
b0d8b23d 97 char *name;
40a55d20 98 int family;
28977c2c 99 uint32_t addr[4];
c0bc5f7b 100 uint32_t scopeid;
40a55d20 101 };
46ec036d 102
40a55d20
UD
103struct gaih_typeproto
104 {
105 int socktype;
106 int protocol;
3e2d61a3 107 char name[4];
f3ac48d0 108 int protoflag;
40a55d20 109 };
46ec036d 110
f3ac48d0
UD
111/* Values for `protoflag'. */
112#define GAI_PROTO_NOSERVICE 1
85599e53 113#define GAI_PROTO_PROTOANY 2
f3ac48d0 114
3e2d61a3 115static const struct gaih_typeproto gaih_inet_typeproto[] =
40a55d20 116{
3e2d61a3
UD
117 { 0, 0, "", 0 },
118 { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
119 { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
120 { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
121 { 0, 0, "", 0 }
46ec036d
UD
122};
123
40a55d20
UD
124struct gaih
125 {
126 int family;
127 int (*gaih)(const char *name, const struct gaih_service *service,
01abbc0f
UD
128 const struct addrinfo *req, struct addrinfo **pai,
129 unsigned int *naddrs);
40a55d20
UD
130 };
131
3e2d61a3 132static const struct addrinfo default_hints =
925c3c5c
UD
133 {
134 .ai_flags = AI_DEFAULT,
135 .ai_family = PF_UNSPEC,
136 .ai_socktype = 0,
137 .ai_protocol = 0,
138 .ai_addrlen = 0,
139 .ai_addr = NULL,
140 .ai_canonname = NULL,
141 .ai_next = NULL
142 };
40a55d20
UD
143
144
64b7897d
UD
145#if 0
146/* Using Unix sockets this way is a security risk. */
40a55d20
UD
147static int
148gaih_local (const char *name, const struct gaih_service *service,
149 const struct addrinfo *req, struct addrinfo **pai)
1fb05e3d
UD
150{
151 struct utsname utsname;
152
f1a785ac
UD
153 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
154 return GAIH_OKIFUNSPEC | -EAI_NONAME;
155
40a55d20 156 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
db7dc811 157 if (uname (&utsname) < 0)
1fb05e3d 158 return -EAI_SYSTEM;
1fb05e3d 159
40a55d20
UD
160 if (name != NULL)
161 {
162 if (strcmp(name, "localhost") &&
163 strcmp(name, "local") &&
164 strcmp(name, "unix") &&
165 strcmp(name, utsname.nodename))
166 return GAIH_OKIFUNSPEC | -EAI_NONAME;
167 }
168
a0bf6ac7
UD
169 if (req->ai_protocol || req->ai_socktype)
170 {
3e2d61a3 171 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
f3ac48d0 172
4b1fef84 173 while (tp->name[0]
f3ac48d0 174 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
3a47453d
UD
175 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
176 || (req->ai_protocol != 0
85599e53 177 && !(tp->protoflag & GAI_PROTO_PROTOANY)
3a47453d 178 && req->ai_protocol != tp->protocol)))
f3ac48d0 179 ++tp;
a0bf6ac7 180
4b1fef84 181 if (! tp->name[0])
a0bf6ac7
UD
182 {
183 if (req->ai_socktype)
3af48b5b 184 return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
a0bf6ac7 185 else
3af48b5b 186 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
a0bf6ac7
UD
187 }
188 }
189
190 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
40a55d20
UD
191 + ((req->ai_flags & AI_CANONNAME)
192 ? (strlen(utsname.nodename) + 1): 0));
193 if (*pai == NULL)
1fb05e3d
UD
194 return -EAI_MEMORY;
195
196 (*pai)->ai_next = NULL;
197 (*pai)->ai_flags = req->ai_flags;
198 (*pai)->ai_family = AF_LOCAL;
199 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
200 (*pai)->ai_protocol = req->ai_protocol;
a0bf6ac7
UD
201 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
202 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
40a55d20 203
beb32642 204#ifdef _HAVE_SA_LEN
40a55d20
UD
205 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
206 sizeof (struct sockaddr_un);
beb32642 207#endif /* _HAVE_SA_LEN */
40a55d20 208
1fb05e3d
UD
209 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
210 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
40a55d20
UD
211
212 if (service)
213 {
214 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
215
216 if (strchr (service->name, '/') != NULL)
217 {
218 if (strlen (service->name) >= sizeof (sunp->sun_path))
219 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
220
221 strcpy (sunp->sun_path, service->name);
222 }
223 else
224 {
225 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
226 sizeof (sunp->sun_path))
227 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
228
229 __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
230 }
231 }
232 else
233 {
e658b54e
UD
234 /* This is a dangerous use of the interface since there is a time
235 window between the test for the file and the actual creation
236 (done by the caller) in which a file with the same name could
237 be created. */
238 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
239
240 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
241 0) != 0
242 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
40a55d20
UD
243 return -EAI_SYSTEM;
244 }
245
1fb05e3d 246 if (req->ai_flags & AI_CANONNAME)
a0bf6ac7
UD
247 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
248 + sizeof (struct sockaddr_un),
249 utsname.nodename);
1fb05e3d
UD
250 else
251 (*pai)->ai_canonname = NULL;
252 return 0;
40a55d20 253}
64b7897d 254#endif /* 0 */
46ec036d 255
3af48b5b 256
40a55d20 257static int
3e2d61a3 258gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
3af48b5b 259 const struct addrinfo *req, struct gaih_servtuple *st)
46ec036d
UD
260{
261 struct servent *s;
40a55d20 262 size_t tmpbuflen = 1024;
993b3242 263 struct servent ts;
40a55d20
UD
264 char *tmpbuf;
265 int r;
266
267 do
993b3242 268 {
40a55d20 269 tmpbuf = __alloca (tmpbuflen);
40a55d20
UD
270
271 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
272 &s);
a0bf6ac7 273 if (r != 0 || s == NULL)
993b3242 274 {
a0bf6ac7 275 if (r == ERANGE)
40a55d20
UD
276 tmpbuflen *= 2;
277 else
278 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
993b3242
UD
279 }
280 }
40a55d20 281 while (r);
993b3242 282
238ae1eb
UD
283 st->next = NULL;
284 st->socktype = tp->socktype;
85599e53
UD
285 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
286 ? req->ai_protocol : tp->protocol);
238ae1eb 287 st->port = s->s_port;
46ec036d
UD
288
289 return 0;
290}
291
925c3c5c 292#define gethosts(_family, _type) \
925c3c5c 293 { \
2d26a717
UD
294 int i; \
295 int herrno; \
925c3c5c 296 struct hostent th; \
a4fdd4b8 297 struct hostent *h; \
f04b1e1f 298 char *localcanon = NULL; \
925c3c5c 299 no_data = 0; \
2d26a717 300 while (1) { \
925c3c5c 301 rc = 0; \
7a11603d
UD
302 status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen, \
303 &rc, &herrno, NULL, &localcanon)); \
2d26a717
UD
304 if (rc != ERANGE || herrno != NETDB_INTERNAL) \
305 break; \
306 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
307 } \
925c3c5c
UD
308 if (status == NSS_STATUS_SUCCESS && rc == 0) \
309 h = &th; \
310 else \
311 h = NULL; \
312 if (rc != 0) \
313 { \
314 if (herrno == NETDB_INTERNAL) \
315 { \
316 __set_h_errno (herrno); \
317 return -EAI_SYSTEM; \
318 } \
319 if (herrno == TRY_AGAIN) \
320 no_data = EAI_AGAIN; \
321 else \
322 no_data = herrno == NO_DATA; \
323 } \
324 else if (h != NULL) \
325 { \
326 for (i = 0; h->h_addr_list[i]; i++) \
327 { \
28977c2c
UD
328 if (*pat == NULL) \
329 { \
330 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
331 (*pat)->scopeid = 0; \
332 } \
333 uint32_t *addr = (*pat)->addr; \
925c3c5c 334 (*pat)->next = NULL; \
b0d8b23d 335 (*pat)->name = i == 0 ? strdupa (h->h_name) : NULL; \
28977c2c
UD
336 if (_family == AF_INET && req->ai_family == AF_INET6) \
337 { \
338 (*pat)->family = AF_INET6; \
339 addr[3] = *(uint32_t *) h->h_addr_list[i]; \
340 addr[2] = htonl (0xffff); \
341 addr[1] = 0; \
342 addr[0] = 0; \
343 } \
344 else \
345 { \
346 (*pat)->family = _family; \
347 memcpy (addr, h->h_addr_list[i], sizeof(_type)); \
348 } \
925c3c5c
UD
349 pat = &((*pat)->next); \
350 } \
28977c2c 351 \
7a11603d 352 if (localcanon != NULL && canon == NULL) \
f04b1e1f
UD
353 canon = strdupa (localcanon); \
354 \
28977c2c
UD
355 if (_family == AF_INET6 && i > 0) \
356 got_ipv6 = true; \
925c3c5c 357 } \
218d76e0
UD
358 }
359
28977c2c 360
f04b1e1f 361typedef enum nss_status (*nss_gethostbyname3_r)
218d76e0
UD
362 (const char *name, int af, struct hostent *host,
363 char *buffer, size_t buflen, int *errnop,
f04b1e1f 364 int *h_errnop, int32_t *ttlp, char **canonp);
28977c2c
UD
365typedef enum nss_status (*nss_getcanonname_r)
366 (const char *name, char *buffer, size_t buflen, char **result,
367 int *errnop, int *h_errnop);
218d76e0
UD
368extern service_user *__nss_hosts_database attribute_hidden;
369
3af48b5b 370
40a55d20
UD
371static int
372gaih_inet (const char *name, const struct gaih_service *service,
01abbc0f
UD
373 const struct addrinfo *req, struct addrinfo **pai,
374 unsigned int *naddrs)
46ec036d 375{
3e2d61a3
UD
376 const struct gaih_typeproto *tp = gaih_inet_typeproto;
377 struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
1fb05e3d 378 struct gaih_addrtuple *at = NULL;
40a55d20 379 int rc;
925c3c5c 380 bool got_ipv6 = false;
28977c2c 381 const char *canon = NULL;
b6c0f679 382 const char *orig_name = name;
46ec036d 383
40a55d20
UD
384 if (req->ai_protocol || req->ai_socktype)
385 {
f3ac48d0
UD
386 ++tp;
387
4b1fef84 388 while (tp->name[0]
3a47453d
UD
389 && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
390 || (req->ai_protocol != 0
85599e53 391 && !(tp->protoflag & GAI_PROTO_PROTOANY)
3a47453d 392 && req->ai_protocol != tp->protocol)))
f3ac48d0
UD
393 ++tp;
394
4b1fef84 395 if (! tp->name[0])
6e4c40ba
UD
396 {
397 if (req->ai_socktype)
3af48b5b 398 return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
6e4c40ba 399 else
3af48b5b 400 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
6e4c40ba 401 }
40a55d20
UD
402 }
403
29546dd9 404 int port = 0;
40a55d20
UD
405 if (service != NULL)
406 {
f3ac48d0 407 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
3af48b5b 408 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
f3ac48d0 409
40a55d20
UD
410 if (service->num < 0)
411 {
4b1fef84 412 if (tp->name[0])
40a55d20 413 {
238ae1eb
UD
414 st = (struct gaih_servtuple *)
415 __alloca (sizeof (struct gaih_servtuple));
416
85599e53 417 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
40a55d20
UD
418 return rc;
419 }
420 else
421 {
422 struct gaih_servtuple **pst = &st;
4b1fef84 423 for (tp++; tp->name[0]; tp++)
40a55d20 424 {
3a47453d
UD
425 struct gaih_servtuple *newp;
426
427 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
428 continue;
429
84a4fd33
UD
430 if (req->ai_socktype != 0
431 && req->ai_socktype != tp->socktype)
432 continue;
85599e53
UD
433 if (req->ai_protocol != 0
434 && !(tp->protoflag & GAI_PROTO_PROTOANY)
435 && req->ai_protocol != tp->protocol)
436 continue;
84a4fd33 437
3a47453d 438 newp = (struct gaih_servtuple *)
238ae1eb
UD
439 __alloca (sizeof (struct gaih_servtuple));
440
85599e53 441 if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
40a55d20
UD
442 {
443 if (rc & GAIH_OKIFUNSPEC)
444 continue;
445 return rc;
446 }
238ae1eb
UD
447
448 *pst = newp;
449 pst = &(newp->next);
40a55d20 450 }
3e2d61a3 451 if (st == (struct gaih_servtuple *) &nullserv)
3af48b5b 452 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
40a55d20 453 }
46ec036d 454 }
40a55d20
UD
455 else
456 {
29546dd9
UD
457 port = htons (service->num);
458 goto got_port;
46ec036d 459 }
40a55d20 460 }
84a4fd33
UD
461 else
462 {
29546dd9
UD
463 got_port:
464
465 if (req->ai_socktype || req->ai_protocol)
466 {
467 st = __alloca (sizeof (struct gaih_servtuple));
468 st->next = NULL;
469 st->socktype = tp->socktype;
470 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
471 ? req->ai_protocol : tp->protocol);
472 st->port = port;
473 }
474 else
84a4fd33 475 {
29546dd9
UD
476 /* Neither socket type nor protocol is set. Return all socket types
477 we know about. */
478 struct gaih_servtuple **lastp = &st;
479 for (++tp; tp->name[0]; ++tp)
480 {
481 struct gaih_servtuple *newp;
84a4fd33 482
29546dd9
UD
483 newp = __alloca (sizeof (struct gaih_servtuple));
484 newp->next = NULL;
485 newp->socktype = tp->socktype;
486 newp->protocol = tp->protocol;
487 newp->port = port;
84a4fd33 488
29546dd9
UD
489 *lastp = newp;
490 lastp = &newp->next;
491 }
84a4fd33
UD
492 }
493 }
40a55d20
UD
494
495 if (name != NULL)
496 {
a0bf6ac7 497 at = __alloca (sizeof (struct gaih_addrtuple));
46ec036d 498
a0bf6ac7 499 at->family = AF_UNSPEC;
c0bc5f7b 500 at->scopeid = 0;
40a55d20 501 at->next = NULL;
46ec036d 502
e2fd3cbe
UD
503#ifdef HAVE_LIBIDN
504 if (req->ai_flags & AI_IDN)
505 {
3a0e90bd
UD
506 int idn_flags = 0;
507 if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
508 idn_flags |= IDNA_ALLOW_UNASSIGNED;
509 if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
510 idn_flags |= IDNA_USE_STD3_ASCII_RULES;
511
e2fd3cbe 512 char *p = NULL;
3a0e90bd 513 rc = __idna_to_ascii_lz (name, &p, idn_flags);
e2fd3cbe 514 if (rc != IDNA_SUCCESS)
de079fee
UD
515 {
516 if (rc == IDNA_MALLOC_ERROR)
517 return -EAI_MEMORY;
518 if (rc == IDNA_DLOPEN_ERROR)
519 return -EAI_SYSTEM;
520 return -EAI_IDN_ENCODE;
521 }
522 /* In case the output string is the same as the input string
523 no new string has been allocated. */
524 if (p != name)
525 {
526 name = strdupa (p);
527 free (p);
528 }
e2fd3cbe
UD
529 }
530#endif
531
c1d98085 532 if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
a0bf6ac7
UD
533 {
534 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
535 at->family = AF_INET;
846d1362 536 else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
925c3c5c 537 {
28977c2c
UD
538 at->addr[3] = at->addr[0];
539 at->addr[2] = htonl (0xffff);
540 at->addr[1] = 0;
541 at->addr[0] = 0;
c1d98085 542 at->family = AF_INET6;
925c3c5c 543 }
a0bf6ac7
UD
544 else
545 return -EAI_ADDRFAMILY;
4376935c 546
4376935c 547 if (req->ai_flags & AI_CANONNAME)
5b353be3 548 canon = name;
a0bf6ac7 549 }
5b353be3 550 else if (at->family == AF_UNSPEC)
a0bf6ac7 551 {
abab6859
UD
552 char *namebuf = (char *) name;
553 char *scope_delim = strchr (name, SCOPE_DELIMITER);
c0bc5f7b 554
abab6859
UD
555 if (__builtin_expect (scope_delim != NULL, 0))
556 {
557 namebuf = alloca (scope_delim - name + 1);
558 *((char *) __mempcpy (namebuf, name, scope_delim - name)) = '\0';
559 }
c0bc5f7b
UD
560
561 if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
562 {
c0bc5f7b
UD
563 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
564 at->family = AF_INET6;
87bb6b6c
UD
565 else if (req->ai_family == AF_INET
566 && IN6_IS_ADDR_V4MAPPED (at->addr))
567 {
28977c2c 568 at->addr[0] = at->addr[3];
87bb6b6c
UD
569 at->family = AF_INET;
570 }
c0bc5f7b
UD
571 else
572 return -EAI_ADDRFAMILY;
573
574 if (scope_delim != NULL)
575 {
576 int try_numericscope = 0;
577 if (IN6_IS_ADDR_LINKLOCAL (at->addr)
578 || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
579 {
580 at->scopeid = if_nametoindex (scope_delim + 1);
581 if (at->scopeid == 0)
582 try_numericscope = 1;
583 }
584 else
585 try_numericscope = 1;
586
587 if (try_numericscope != 0)
588 {
589 char *end;
590 assert (sizeof (uint32_t) <= sizeof (unsigned long));
591 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
592 10);
593 if (*end != '\0')
594 return GAIH_OKIFUNSPEC | -EAI_NONAME;
595 }
596 }
4376935c 597
5b353be3
UD
598 if (req->ai_flags & AI_CANONNAME)
599 canon = name;
c0bc5f7b 600 }
a0bf6ac7 601 }
40a55d20 602
ce75c139 603 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
40a55d20 604 {
40a55d20 605 struct gaih_addrtuple **pat = &at;
a0bf6ac7 606 int no_data = 0;
218d76e0 607 int no_inet6_data = 0;
28977c2c
UD
608 service_user *nip = NULL;
609 enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
610 enum nss_status status = NSS_STATUS_UNAVAIL;
611 int no_more;
28977c2c 612 int old_res_options;
d19687d6
UD
613
614 /* If we do not have to look for IPv4 and IPv6 together, use
615 the simple, old functions. */
abab6859
UD
616 if (req->ai_family == AF_INET
617 || (req->ai_family == AF_INET6
618 && ((req->ai_flags & AI_V4MAPPED) == 0
619 || (req->ai_flags & AI_ALL) == 0)))
d19687d6
UD
620 {
621 int family = req->ai_family;
622 size_t tmpbuflen = 512;
623 char *tmpbuf = alloca (tmpbuflen);
624 int rc;
625 struct hostent th;
626 struct hostent *h;
627 int herrno;
628
629 simple_again:
630 while (1)
631 {
632 rc = __gethostbyname2_r (name, family, &th, tmpbuf,
633 tmpbuflen, &h, &herrno);
634 if (rc != ERANGE || herrno != NETDB_INTERNAL)
635 break;
636 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
637 }
638
639 if (rc == 0)
640 {
641 if (h == NULL)
642 {
643 if (req->ai_family == AF_INET6
644 && (req->ai_flags & AI_V4MAPPED)
645 && family == AF_INET6)
646 {
647 /* Try again, this time looking for IPv4
648 addresses. */
649 family = AF_INET;
650 goto simple_again;
651 }
652 }
653 else
654 {
655 /* We found data, now convert it into the list. */
656 for (int i = 0; h->h_addr_list[i]; ++i)
657 {
658 if (*pat == NULL)
659 {
660 *pat = __alloca (sizeof (struct gaih_addrtuple));
661 (*pat)->scopeid = 0;
662 }
663 (*pat)->next = NULL;
664 (*pat)->family = req->ai_family;
665 if (family == req->ai_family)
666 memcpy ((*pat)->addr, h->h_addr_list[i],
667 h->h_length);
668 else
669 {
9cfe5381 670 uint32_t *addr = (uint32_t *) (*pat)->addr;
d19687d6
UD
671 addr[3] = *(uint32_t *) h->h_addr_list[i];
672 addr[2] = htonl (0xffff);
673 addr[1] = 0;
674 addr[0] = 0;
675 }
676 pat = &((*pat)->next);
677 }
678 }
679 }
680 else
681 {
682 if (herrno == NETDB_INTERNAL)
683 {
684 __set_h_errno (herrno);
685 return -EAI_SYSTEM;
686 }
687 if (herrno == TRY_AGAIN)
688 {
689 return -EAI_AGAIN;
690 }
691 /* We made requests but they turned out no data.
692 The name is known, though. */
3af48b5b 693 return GAIH_OKIFUNSPEC | -EAI_NODATA;
d19687d6
UD
694 }
695
696 goto process_list;
697 }
698
699#ifdef USE_NSCD
62417d7e
UD
700 if (__nss_not_use_nscd_hosts > 0
701 && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
702 __nss_not_use_nscd_hosts = 0;
d19687d6 703
62417d7e
UD
704 if (!__nss_not_use_nscd_hosts)
705 {
706 /* Try to use nscd. */
707 struct nscd_ai_result *air = NULL;
708 int herrno;
709 int err = __nscd_getai (name, &air, &herrno);
710 if (air != NULL)
d19687d6 711 {
62417d7e
UD
712 /* Transform into gaih_addrtuple list. */
713 bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
714 char *addrs = air->addrs;
d19687d6 715
62417d7e 716 for (int i = 0; i < air->naddrs; ++i)
d19687d6 717 {
62417d7e
UD
718 socklen_t size = (air->family[i] == AF_INET
719 ? INADDRSZ : IN6ADDRSZ);
720 if (*pat == NULL)
721 {
722 *pat = __alloca (sizeof (struct gaih_addrtuple));
723 (*pat)->scopeid = 0;
724 }
725 uint32_t *pataddr = (*pat)->addr;
726 (*pat)->next = NULL;
727 if (added_canon || air->canon == NULL)
728 (*pat)->name = NULL;
729 else
730 canon = (*pat)->name = strdupa (air->canon);
731
732 if (air->family[i] == AF_INET
733 && req->ai_family == AF_INET6
734 && (req->ai_flags & AI_V4MAPPED))
735 {
736 (*pat)->family = AF_INET6;
737 pataddr[3] = *(uint32_t *) addrs;
738 pataddr[2] = htonl (0xffff);
739 pataddr[1] = 0;
740 pataddr[0] = 0;
741 pat = &((*pat)->next);
742 added_canon = true;
743 }
744 else if (req->ai_family == AF_UNSPEC
745 || air->family[i] == req->ai_family)
746 {
747 (*pat)->family = air->family[i];
748 memcpy (pataddr, addrs, size);
749 pat = &((*pat)->next);
750 added_canon = true;
751 if (air->family[i] == AF_INET6)
752 got_ipv6 = true;
753 }
754 addrs += size;
d19687d6 755 }
d19687d6 756
6535f55f
UD
757 free (air);
758
62417d7e 759 if (at->family == AF_UNSPEC)
3af48b5b 760 return GAIH_OKIFUNSPEC | -EAI_NONAME;
d19687d6 761
62417d7e
UD
762 goto process_list;
763 }
764 else if (err != 0 && __nss_not_use_nscd_hosts == 0)
765 {
766 if (herrno == NETDB_INTERNAL && errno == ENOMEM)
767 return -EAI_MEMORY;
768 if (herrno == TRY_AGAIN)
769 return -EAI_AGAIN;
770 return -EAI_SYSTEM;
771 }
d19687d6
UD
772 }
773#endif
28977c2c
UD
774
775 if (__nss_hosts_database != NULL)
776 {
777 no_more = 0;
778 nip = __nss_hosts_database;
779 }
780 else
781 no_more = __nss_database_lookup ("hosts", NULL,
782 "dns [!UNAVAIL=return] files",
783 &nip);
784
785 if (__res_maybe_init (&_res, 0) == -1)
786 no_more = 1;
787
9c42c64d
UD
788 /* If we are looking for both IPv4 and IPv6 address we don't
789 want the lookup functions to automatically promote IPv4
790 addresses to IPv6 addresses. Currently this is decided
791 by setting the RES_USE_INET6 bit in _res.options. */
28977c2c
UD
792 old_res_options = _res.options;
793 _res.options &= ~RES_USE_INET6;
40a55d20 794
d19687d6
UD
795 size_t tmpbuflen = 512;
796 char *tmpbuf = alloca (tmpbuflen);
797
28977c2c
UD
798 while (!no_more)
799 {
d7243362
UD
800 nss_gethostbyname3_r fct = NULL;
801 if (req->ai_flags & AI_CANONNAME)
802 /* No need to use this function if we do not look for
803 the canonical name. The function does not exist in
804 all NSS modules and therefore the lookup would
805 often fail. */
806 fct = __nss_lookup_function (nip, "gethostbyname3_r");
f04b1e1f
UD
807 if (fct == NULL)
808 /* We are cheating here. The gethostbyname2_r function does
809 not have the same interface as gethostbyname3_r but the
810 extra arguments the latter takes are added at the end.
811 So the gethostbyname2_r code will just ignore them. */
812 fct = __nss_lookup_function (nip, "gethostbyname2_r");
9c42c64d 813
28977c2c 814 if (fct != NULL)
218d76e0 815 {
28977c2c
UD
816 if (req->ai_family == AF_INET6
817 || req->ai_family == AF_UNSPEC)
64ab2317 818 {
28977c2c 819 gethosts (AF_INET6, struct in6_addr);
64ab2317
UD
820 no_inet6_data = no_data;
821 inet6_status = status;
64ab2317 822 }
28977c2c
UD
823 if (req->ai_family == AF_INET
824 || req->ai_family == AF_UNSPEC
825 || (req->ai_family == AF_INET6
2d26a717
UD
826 && (req->ai_flags & AI_V4MAPPED)
827 /* Avoid generating the mapped addresses if we
828 know we are not going to need them. */
829 && ((req->ai_flags & AI_ALL) || !got_ipv6)))
28977c2c
UD
830 {
831 gethosts (AF_INET, struct in_addr);
218d76e0 832
28977c2c
UD
833 if (req->ai_family == AF_INET)
834 {
835 no_inet6_data = no_data;
836 inet6_status = status;
837 }
838 }
218d76e0 839
28977c2c
UD
840 /* If we found one address for AF_INET or AF_INET6,
841 don't continue the search. */
842 if (inet6_status == NSS_STATUS_SUCCESS
843 || status == NSS_STATUS_SUCCESS)
844 {
f04b1e1f 845 if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
28977c2c 846 {
afd7b703
UD
847 /* If we need the canonical name, get it
848 from the same service as the result. */
849 nss_getcanonname_r cfct;
850 int herrno;
851
852 cfct = __nss_lookup_function (nip, "getcanonname_r");
853 if (cfct != NULL)
854 {
855 const size_t max_fqdn_len = 256;
856 char *buf = alloca (max_fqdn_len);
857 char *s;
858
b0d8b23d 859 if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
afd7b703
UD
860 max_fqdn_len, &s, &rc,
861 &herrno))
862 == NSS_STATUS_SUCCESS)
863 canon = s;
864 else
865 /* Set to name now to avoid using
866 gethostbyaddr. */
867 canon = name;
868 }
28977c2c
UD
869 }
870
871 break;
872 }
873
874 /* We can have different states for AF_INET and
875 AF_INET6. Try to find a useful one for both. */
876 if (inet6_status == NSS_STATUS_TRYAGAIN)
877 status = NSS_STATUS_TRYAGAIN;
abab6859
UD
878 else if (status == NSS_STATUS_UNAVAIL
879 && inet6_status != NSS_STATUS_UNAVAIL)
28977c2c 880 status = inet6_status;
218d76e0
UD
881 }
882
28977c2c
UD
883 if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
884 break;
885
886 if (nip->next == NULL)
887 no_more = -1;
888 else
889 nip = nip->next;
2ce1a10f 890 }
a0bf6ac7 891
28977c2c
UD
892 _res.options = old_res_options;
893
5ad81f40 894 if (no_data != 0 && no_inet6_data != 0)
7813b61a
UD
895 {
896 /* If both requests timed out report this. */
897 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
898 return -EAI_AGAIN;
899
900 /* We made requests but they turned out no data. The name
901 is known, though. */
3af48b5b 902 return GAIH_OKIFUNSPEC | -EAI_NODATA;
7813b61a 903 }
46ec036d 904 }
40a55d20 905
d19687d6 906 process_list:
40a55d20 907 if (at->family == AF_UNSPEC)
3af48b5b 908 return GAIH_OKIFUNSPEC | -EAI_NONAME;
46ec036d 909 }
40a55d20
UD
910 else
911 {
7396d844 912 struct gaih_addrtuple *atr;
a0bf6ac7
UD
913 atr = at = __alloca (sizeof (struct gaih_addrtuple));
914 memset (at, '\0', sizeof (struct gaih_addrtuple));
40a55d20 915
28977c2c 916 if (req->ai_family == AF_UNSPEC)
7396d844 917 {
a0bf6ac7
UD
918 at->next = __alloca (sizeof (struct gaih_addrtuple));
919 memset (at->next, '\0', sizeof (struct gaih_addrtuple));
7396d844 920 }
40a55d20 921
28977c2c 922 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
7396d844
UD
923 {
924 at->family = AF_INET6;
925 if ((req->ai_flags & AI_PASSIVE) == 0)
a585ba22 926 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
7396d844
UD
927 atr = at->next;
928 }
40a55d20 929
28977c2c 930 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
7396d844
UD
931 {
932 atr->family = AF_INET;
933 if ((req->ai_flags & AI_PASSIVE) == 0)
28977c2c 934 atr->addr[0] = htonl (INADDR_LOOPBACK);
7396d844 935 }
46ec036d 936 }
1fb05e3d 937
40a55d20
UD
938 if (pai == NULL)
939 return 0;
46ec036d
UD
940
941 {
46ec036d
UD
942 struct gaih_servtuple *st2;
943 struct gaih_addrtuple *at2 = at;
28977c2c 944 size_t socklen;
85599e53 945 sa_family_t family;
40a55d20 946
f21acc89
UD
947 /*
948 buffer is the size of an unformatted IPv6 address in printable format.
949 */
40a55d20
UD
950 while (at2 != NULL)
951 {
163288fe
UD
952 /* Only the first entry gets the canonical name. */
953 if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
40a55d20 954 {
28977c2c 955 if (canon == NULL)
40a55d20 956 {
28977c2c
UD
957 struct hostent *h = NULL;
958 int herrno;
959 struct hostent th;
960 size_t tmpbuflen = 512;
961 char *tmpbuf = NULL;
40a55d20 962
28977c2c
UD
963 do
964 {
965 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
966 rc = __gethostbyaddr_r (at2->addr,
967 ((at2->family == AF_INET6)
968 ? sizeof (struct in6_addr)
969 : sizeof (struct in_addr)),
970 at2->family, &th, tmpbuf,
971 tmpbuflen, &h, &herrno);
972 }
973 while (rc == ERANGE && herrno == NETDB_INTERNAL);
40a55d20 974
28977c2c 975 if (rc != 0 && herrno == NETDB_INTERNAL)
160d067b 976 {
28977c2c
UD
977 __set_h_errno (herrno);
978 return -EAI_SYSTEM;
160d067b
UD
979 }
980
28977c2c
UD
981 if (h != NULL)
982 canon = h->h_name;
983 else
984 {
b6c0f679 985 assert (orig_name != NULL);
28977c2c
UD
986 /* If the canonical name cannot be determined, use
987 the passed in string. */
b6c0f679 988 canon = orig_name;
28977c2c 989 }
160d067b 990 }
40a55d20 991
de079fee
UD
992#ifdef HAVE_LIBIDN
993 if (req->ai_flags & AI_CANONIDN)
994 {
3a0e90bd
UD
995 int idn_flags = 0;
996 if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
997 idn_flags |= IDNA_ALLOW_UNASSIGNED;
998 if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
999 idn_flags |= IDNA_USE_STD3_ASCII_RULES;
1000
de079fee 1001 char *out;
28977c2c 1002 int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
de079fee
UD
1003 if (rc != IDNA_SUCCESS)
1004 {
1005 if (rc == IDNA_MALLOC_ERROR)
1006 return -EAI_MEMORY;
1007 if (rc == IDNA_DLOPEN_ERROR)
1008 return -EAI_SYSTEM;
1009 return -EAI_IDN_ENCODE;
1010 }
1011 /* In case the output string is the same as the input
b9343764
UD
1012 string no new string has been allocated. Otherwise
1013 make a copy. */
1014 if (out == canon)
1015 goto make_copy;
de079fee 1016 }
b9343764 1017 else
de079fee 1018#endif
b9343764
UD
1019 {
1020#ifdef HAVE_LIBIDN
1021 make_copy:
1022#endif
1023 canon = strdup (canon);
1024 if (canon == NULL)
1025 return -EAI_MEMORY;
1026 }
40a55d20 1027 }
40a55d20 1028
abab6859
UD
1029 family = at2->family;
1030 if (family == AF_INET6)
85599e53 1031 {
85599e53 1032 socklen = sizeof (struct sockaddr_in6);
925c3c5c
UD
1033
1034 /* If we looked up IPv4 mapped address discard them here if
1035 the caller isn't interested in all address and we have
1036 found at least one IPv6 address. */
28977c2c 1037 if (got_ipv6
925c3c5c
UD
1038 && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
1039 && IN6_IS_ADDR_V4MAPPED (at2->addr))
1040 goto ignore;
85599e53 1041 }
40a55d20 1042 else
abab6859 1043 socklen = sizeof (struct sockaddr_in);
40a55d20
UD
1044
1045 for (st2 = st; st2 != NULL; st2 = st2->next)
1046 {
a24c5ac4
UD
1047 struct addrinfo *ai;
1048 ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
1049 if (ai == NULL)
e7c8359e
UD
1050 {
1051 free ((char *) canon);
1052 return -EAI_MEMORY;
1053 }
40a55d20 1054
a24c5ac4
UD
1055 ai->ai_flags = req->ai_flags;
1056 ai->ai_family = family;
1057 ai->ai_socktype = st2->socktype;
1058 ai->ai_protocol = st2->protocol;
1059 ai->ai_addrlen = socklen;
1060 ai->ai_addr = (void *) (ai + 1);
b9343764
UD
1061
1062 /* We only add the canonical name once. */
a24c5ac4 1063 ai->ai_canonname = (char *) canon;
b9343764
UD
1064 canon = NULL;
1065
beb32642 1066#ifdef _HAVE_SA_LEN
a24c5ac4 1067 ai->ai_addr->sa_len = socklen;
beb32642 1068#endif /* _HAVE_SA_LEN */
a24c5ac4 1069 ai->ai_addr->sa_family = family;
5a97622d 1070
e7c8359e
UD
1071 /* In case of an allocation error the list must be NULL
1072 terminated. */
1073 ai->ai_next = NULL;
1074
85599e53 1075 if (family == AF_INET6)
40a55d20
UD
1076 {
1077 struct sockaddr_in6 *sin6p =
a24c5ac4 1078 (struct sockaddr_in6 *) ai->ai_addr;
46ec036d 1079
28977c2c 1080 sin6p->sin6_port = st2->port;
40a55d20
UD
1081 sin6p->sin6_flowinfo = 0;
1082 memcpy (&sin6p->sin6_addr,
1083 at2->addr, sizeof (struct in6_addr));
c0bc5f7b 1084 sin6p->sin6_scope_id = at2->scopeid;
40a55d20
UD
1085 }
1086 else
1087 {
1088 struct sockaddr_in *sinp =
a24c5ac4 1089 (struct sockaddr_in *) ai->ai_addr;
28977c2c 1090 sinp->sin_port = st2->port;
40a55d20
UD
1091 memcpy (&sinp->sin_addr,
1092 at2->addr, sizeof (struct in_addr));
1093 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
1094 }
46ec036d 1095
a24c5ac4 1096 pai = &(ai->ai_next);
40a55d20 1097 }
46ec036d 1098
01abbc0f
UD
1099 ++*naddrs;
1100
925c3c5c 1101 ignore:
40a55d20
UD
1102 at2 = at2->next;
1103 }
46ec036d 1104 }
40a55d20 1105 return 0;
46ec036d
UD
1106}
1107
786dcb62 1108#if 0
192c0fa5 1109static const struct gaih gaih[] =
40a55d20
UD
1110 {
1111 { PF_INET6, gaih_inet },
1112 { PF_INET, gaih_inet },
64b7897d 1113#if 0
40a55d20 1114 { PF_LOCAL, gaih_local },
64b7897d 1115#endif
40a55d20
UD
1116 { PF_UNSPEC, NULL }
1117 };
786dcb62 1118#endif
46ec036d 1119
5ddb5bf5
UD
1120struct sort_result
1121{
1122 struct addrinfo *dest_addr;
1123 struct sockaddr_storage source_addr;
d19687d6 1124 uint8_t source_addr_len;
5ddb5bf5 1125 bool got_source_addr;
3af48b5b 1126 uint8_t source_addr_flags;
5ddb5bf5
UD
1127};
1128
1129
1130static int
1131get_scope (const struct sockaddr_storage *ss)
1132{
1133 int scope;
1134 if (ss->ss_family == PF_INET6)
1135 {
1136 const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss;
1137
1138 if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
1139 {
1140 if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr))
1141 scope = 2;
1142 else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr))
1143 scope = 5;
1144 else
1145 /* XXX Is this the correct default behavior? */
1146 scope = 14;
1147 }
1148 else
1149 scope = in6->sin6_addr.s6_addr[1] & 0xf;
1150 }
1151 else if (ss->ss_family == PF_INET)
1152 {
1153 const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
1154 const uint8_t *addr = (const uint8_t *) &in->sin_addr;
1155
1156 /* RFC 3484 specifies how to map IPv6 addresses to scopes.
1157 169.254/16 and 127/8 are link-local. */
1158 if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127)
1159 scope = 2;
a334319f 1160 else if (addr[0] == 10 || (addr[0] == 172 && addr[1] == 16)
5ddb5bf5
UD
1161 || (addr[0] == 192 && addr[1] == 168))
1162 scope = 5;
1163 else
1164 scope = 14;
1165 }
1166 else
1167 /* XXX What is a good default? */
1168 scope = 15;
1169
1170 return scope;
1171}
1172
1173
77dd4c3b 1174struct prefixentry
5ddb5bf5
UD
1175{
1176 struct in6_addr prefix;
1177 unsigned int bits;
1178 int val;
77dd4c3b
UD
1179};
1180
1181
1182/* The label table. */
1183static const struct prefixentry *labels;
1184
1185/* Default labels. */
1186static const struct prefixentry default_labels[] =
5ddb5bf5
UD
1187 {
1188 /* See RFC 3484 for the details. */
77dd4c3b
UD
1189 { { .in6_u
1190 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
5ddb5bf5 1192 128, 0 },
77dd4c3b
UD
1193 { { .in6_u
1194 = { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1195 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5 1196 16, 2 },
77dd4c3b
UD
1197 { { .in6_u
1198 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5 1200 96, 3 },
77dd4c3b
UD
1201 { { .in6_u
1202 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1203 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5 1204 96, 4 },
6e2a7825
UD
1205 /* The next two entries differ from RFC 3484. We need to treat
1206 IPv6 site-local addresses special because they are never NATed,
1207 unlike site-locale IPv4 addresses. If this would not happen, on
1208 machines which have only IPv4 and IPv6 site-local addresses, the
1209 sorting would prefer the IPv6 site-local addresses, causing
1210 unnecessary delays when trying to connect to a global IPv6 address
1211 through a site-local IPv6 address. */
1212 { { .in6_u
1213 = { .u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
1215 10, 5 },
1216 { { .in6_u
1217 = { .u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
1219 7, 6 },
77dd4c3b
UD
1220 { { .in6_u
1221 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5
UD
1223 0, 1 }
1224 };
1225
1226
77dd4c3b
UD
1227/* The precedence table. */
1228static const struct prefixentry *precedence;
1229
1230/* The default precedences. */
1231static const struct prefixentry default_precedence[] =
5ddb5bf5
UD
1232 {
1233 /* See RFC 3484 for the details. */
77dd4c3b
UD
1234 { { .in6_u
1235 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
5ddb5bf5 1237 128, 50 },
77dd4c3b
UD
1238 { { .in6_u
1239 = { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5 1241 16, 30 },
77dd4c3b
UD
1242 { { .in6_u
1243 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5 1245 96, 20 },
77dd4c3b
UD
1246 { { .in6_u
1247 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1248 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
6e2a7825 1249 96, 10 },
77dd4c3b
UD
1250 { { .in6_u
1251 = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
5ddb5bf5
UD
1253 0, 40 }
1254 };
1255
1256
1257static int
77dd4c3b
UD
1258match_prefix (const struct sockaddr_storage *ss,
1259 const struct prefixentry *list, int default_val)
5ddb5bf5
UD
1260{
1261 int idx;
1262 struct sockaddr_in6 in6_mem;
1263 const struct sockaddr_in6 *in6;
1264
1265 if (ss->ss_family == PF_INET6)
1266 in6 = (const struct sockaddr_in6 *) ss;
1267 else if (ss->ss_family == PF_INET)
1268 {
1269 const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
1270
1271 /* Convert to IPv6 address. */
1272 in6_mem.sin6_family = PF_INET6;
1273 in6_mem.sin6_port = in->sin_port;
1274 in6_mem.sin6_flowinfo = 0;
1275 if (in->sin_addr.s_addr == htonl (0x7f000001))
1276 in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
1277 else
1278 {
1279 /* Construct a V4-to-6 mapped address. */
1280 memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
1281 in6_mem.sin6_addr.s6_addr16[5] = 0xffff;
1282 in6_mem.sin6_addr.s6_addr32[3] = in->sin_addr.s_addr;
1283 in6_mem.sin6_scope_id = 0;
1284 }
1285
1286 in6 = &in6_mem;
1287 }
1288 else
1289 return default_val;
1290
1291 for (idx = 0; ; ++idx)
1292 {
1293 unsigned int bits = list[idx].bits;
a334319f
UD
1294 uint8_t *mask = list[idx].prefix.s6_addr;
1295 uint8_t *val = in6->sin6_addr.s6_addr;
5ddb5bf5 1296
207cce4c 1297 while (bits >= 8)
5ddb5bf5
UD
1298 {
1299 if (*mask != *val)
1300 break;
1301
1302 ++mask;
1303 ++val;
1304 bits -= 8;
1305 }
1306
1307 if (bits < 8)
1308 {
d9a3ae7f 1309 if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits)))
5ddb5bf5
UD
1310 /* Match! */
1311 break;
1312 }
1313 }
1314
1315 return list[idx].val;
1316}
1317
1318
1319static int
1320get_label (const struct sockaddr_storage *ss)
1321{
1322 /* XXX What is a good default value? */
77dd4c3b 1323 return match_prefix (ss, labels, INT_MAX);
5ddb5bf5
UD
1324}
1325
1326
1327static int
1328get_precedence (const struct sockaddr_storage *ss)
1329{
1330 /* XXX What is a good default value? */
77dd4c3b 1331 return match_prefix (ss, precedence, 0);
5ddb5bf5
UD
1332}
1333
1334
c6bad06a
UD
1335/* Find last bit set in a word. */
1336static int
1337fls (uint32_t a)
1338{
1339 uint32_t mask;
1340 int n = 0;
1341 for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n)
1342 if ((a & mask) != 0)
1343 break;
1344 return n;
1345}
1346
1347
5ddb5bf5
UD
1348static int
1349rfc3484_sort (const void *p1, const void *p2)
1350{
1351 const struct sort_result *a1 = (const struct sort_result *) p1;
1352 const struct sort_result *a2 = (const struct sort_result *) p2;
1353
1354 /* Rule 1: Avoid unusable destinations.
1355 We have the got_source_addr flag set if the destination is reachable. */
1356 if (a1->got_source_addr && ! a2->got_source_addr)
1357 return -1;
1358 if (! a1->got_source_addr && a2->got_source_addr)
1359 return 1;
1360
1361
1362 /* Rule 2: Prefer matching scope. Only interesting if both
1363 destination addresses are IPv6. */
1364 int a1_dst_scope
1365 = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
1366
1367 int a2_dst_scope
1368 = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
1369
1370 if (a1->got_source_addr)
1371 {
1372 int a1_src_scope = get_scope (&a1->source_addr);
1373 int a2_src_scope = get_scope (&a2->source_addr);
1374
1375 if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope)
1376 return -1;
1377 if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope)
1378 return 1;
1379 }
1380
1381
3af48b5b
UD
1382 /* Rule 3: Avoid deprecated addresses. */
1383 if (a1->got_source_addr)
1384 {
1385 if (!(a1->source_addr_flags & in6ai_deprecated)
1386 && (a2->source_addr_flags & in6ai_deprecated))
1387 return -1;
1388 if ((a1->source_addr_flags & in6ai_deprecated)
1389 && !(a2->source_addr_flags & in6ai_deprecated))
1390 return 1;
1391 }
5ddb5bf5 1392
a334319f
UD
1393 /* Rule 4: Prefer home addresses.
1394 Another thing only the kernel can decide. */
5ddb5bf5
UD
1395
1396 /* Rule 5: Prefer matching label. */
1397 if (a1->got_source_addr)
1398 {
1399 int a1_dst_label
1400 = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
1401 int a1_src_label = get_label (&a1->source_addr);
1402
1403 int a2_dst_label
1404 = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
1405 int a2_src_label = get_label (&a2->source_addr);
1406
1407 if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
1408 return -1;
1409 if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label)
1410 return 1;
1411 }
1412
1413
1414 /* Rule 6: Prefer higher precedence. */
1415 int a1_prec
1416 = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
1417 int a2_prec
1418 = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
1419
1420 if (a1_prec > a2_prec)
1421 return -1;
1422 if (a1_prec < a2_prec)
1423 return 1;
1424
1425
3af48b5b
UD
1426 /* Rule 7: Prefer native transport. */
1427 if (a1->got_source_addr)
1428 {
1429 if (!(a1->source_addr_flags & in6ai_temporary)
1430 && (a1->source_addr_flags & in6ai_temporary))
1431 return -1;
1432 if ((a1->source_addr_flags & in6ai_temporary)
1433 && !(a1->source_addr_flags & in6ai_temporary))
1434 return -1;
1435
1436 /* XXX Do we need to check anything beside temporary addresses? */
1437 }
5ddb5bf5
UD
1438
1439
1440 /* Rule 8: Prefer smaller scope. */
1441 if (a1_dst_scope < a2_dst_scope)
1442 return -1;
1443 if (a1_dst_scope > a2_dst_scope)
1444 return 1;
1445
1446
1447 /* Rule 9: Use longest matching prefix. */
1448 if (a1->got_source_addr
1449 && a1->dest_addr->ai_family == a2->dest_addr->ai_family)
1450 {
1451 int bit1 = 0;
1452 int bit2 = 0;
1453
1454 if (a1->dest_addr->ai_family == PF_INET)
1455 {
1456 assert (a1->source_addr.ss_family == PF_INET);
1457 assert (a2->source_addr.ss_family == PF_INET);
1458
1459 struct sockaddr_in *in1_dst;
1460 struct sockaddr_in *in1_src;
1461 struct sockaddr_in *in2_dst;
1462 struct sockaddr_in *in2_src;
1463
1464 in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
1465 in1_src = (struct sockaddr_in *) &a1->source_addr;
1466 in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
1467 in2_src = (struct sockaddr_in *) &a2->source_addr;
1468
c6bad06a
UD
1469 bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
1470 ^ in1_src->sin_addr.s_addr));
1471 bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
1472 ^ in2_src->sin_addr.s_addr));
5ddb5bf5
UD
1473 }
1474 else if (a1->dest_addr->ai_family == PF_INET6)
1475 {
1476 assert (a1->source_addr.ss_family == PF_INET6);
1477 assert (a2->source_addr.ss_family == PF_INET6);
1478
1479 struct sockaddr_in6 *in1_dst;
1480 struct sockaddr_in6 *in1_src;
1481 struct sockaddr_in6 *in2_dst;
1482 struct sockaddr_in6 *in2_src;
1483
1484 in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr;
1485 in1_src = (struct sockaddr_in6 *) &a1->source_addr;
1486 in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr;
1487 in2_src = (struct sockaddr_in6 *) &a2->source_addr;
1488
1489 int i;
1490 for (i = 0; i < 4; ++i)
1491 if (in1_dst->sin6_addr.s6_addr32[i]
1492 != in1_src->sin6_addr.s6_addr32[i]
1493 || (in2_dst->sin6_addr.s6_addr32[i]
1494 != in2_src->sin6_addr.s6_addr32[i]))
1495 break;
1496
1497 if (i < 4)
1498 {
c6bad06a
UD
1499 bit1 = fls (ntohl (in1_dst->sin6_addr.s6_addr32[i]
1500 ^ in1_src->sin6_addr.s6_addr32[i]));
1501 bit2 = fls (ntohl (in2_dst->sin6_addr.s6_addr32[i]
1502 ^ in2_src->sin6_addr.s6_addr32[i]));
5ddb5bf5
UD
1503 }
1504 }
1505
1506 if (bit1 > bit2)
1507 return -1;
1508 if (bit1 < bit2)
1509 return 1;
1510 }
1511
1512
1513 /* Rule 10: Otherwise, leave the order unchanged. */
1514 return 0;
1515}
1516
1517
3af48b5b
UD
1518static int
1519in6aicmp (const void *p1, const void *p2)
1520{
1521 struct in6addrinfo *a1 = (struct in6addrinfo *) p1;
1522 struct in6addrinfo *a2 = (struct in6addrinfo *) p2;
1523
1524 return memcmp (a1->addr, a2->addr, sizeof (a1->addr));
1525}
1526
1527
77dd4c3b
UD
1528/* Name of the config file for RFC 3484 sorting (for now). */
1529#define GAICONF_FNAME "/etc/gai.conf"
1530
1531
1532/* Nozero if we are supposed to reload the config file automatically
1533 whenever it changed. */
1534static int gaiconf_reload_flag;
1535
1536/* Last modification time. */
1537static struct timespec gaiconf_mtime;
1538
1539
1540libc_freeres_fn(fini)
1541{
1542 if (labels != default_labels)
1543 {
1544 const struct prefixentry *old = labels;
1545 labels = default_labels;
1546 free ((void *) old);
1547 }
1548
1549 if (precedence != default_precedence)
1550 {
1551 const struct prefixentry *old = precedence;
1552 precedence = default_precedence;
1553 free ((void *) old);
1554 }
1555}
1556
1557
1558struct prefixlist
1559{
1560 struct prefixentry entry;
1561 struct prefixlist *next;
1562};
1563
1564
1565static void
1566free_prefixlist (struct prefixlist *list)
1567{
1568 while (list != NULL)
1569 {
1570 struct prefixlist *oldp = list;
1571 list = list->next;
1572 free (oldp);
1573 }
1574}
1575
1576
1577static int
1578prefixcmp (const void *p1, const void *p2)
1579{
1580 const struct prefixentry *e1 = (const struct prefixentry *) p1;
1581 const struct prefixentry *e2 = (const struct prefixentry *) p2;
1582
1583 if (e1->bits < e2->bits)
1584 return 1;
1585 if (e1->bits == e2->bits)
1586 return 0;
1587 return -1;
1588}
1589
1590
1591static void
1592gaiconf_init (void)
1593{
1594 struct prefixlist *labellist = NULL;
1595 size_t nlabellist = 0;
1596 bool labellist_nullbits = false;
1597 struct prefixlist *precedencelist = NULL;
1598 size_t nprecedencelist = 0;
1599 bool precedencelist_nullbits = false;
1600
1601 FILE *fp = fopen (GAICONF_FNAME, "rc");
1602 if (fp != NULL)
1603 {
1604 struct stat64 st;
1605 if (__fxstat64 (_STAT_VER, fileno (fp), &st) != 0)
1606 {
1607 fclose (fp);
1608 goto no_file;
1609 }
1610
1611 char *line = NULL;
1612 size_t linelen = 0;
1613
1614 __fsetlocking (fp, FSETLOCKING_BYCALLER);
1615
1616 while (!feof_unlocked (fp))
1617 {
1618 ssize_t n = __getline (&line, &linelen, fp);
1619 if (n <= 0)
1620 break;
1621
1622 /* Handle comments. No escaping possible so this is easy. */
1623 char *cp = strchr (line, '#');
1624 if (cp != NULL)
1625 *cp = '\0';
1626
1627 cp = line;
1628 while (isspace (*cp))
1629 ++cp;
1630
1631 char *cmd = cp;
1632 while (*cp != '\0' && !isspace (*cp))
1633 ++cp;
1634 size_t cmdlen = cp - cmd;
1635
1636 if (*cp != '\0')
1637 *cp++ = '\0';
1638 while (isspace (*cp))
1639 ++cp;
1640
1641 char *val1 = cp;
1642 while (*cp != '\0' && !isspace (*cp))
1643 ++cp;
1644 size_t val1len = cp - cmd;
1645
1646 /* We always need at least two values. */
1647 if (val1len == 0)
1648 continue;
1649
1650 if (*cp != '\0')
1651 *cp++ = '\0';
1652 while (isspace (*cp))
1653 ++cp;
1654
1655 char *val2 = cp;
1656 while (*cp != '\0' && !isspace (*cp))
1657 ++cp;
1658
1659 /* Ignore the rest of the line. */
1660 *cp = '\0';
1661
1662 struct prefixlist **listp;
1663 size_t *lenp;
1664 bool *nullbitsp;
1665 switch (cmdlen)
1666 {
1667 case 5:
1668 if (strcmp (cmd, "label") == 0)
1669 {
1670 struct in6_addr prefix;
1671 unsigned long int bits = 128;
1672 unsigned long int val;
1673 char *endp;
1674
1675 listp = &labellist;
1676 lenp = &nlabellist;
1677 nullbitsp = &labellist_nullbits;
1678
1679 new_elem:
1680 __set_errno (0);
1681 cp = strchr (val1, '/');
1682 if (cp != NULL)
1683 *cp++ = '\0';
1684 if (inet_pton (AF_INET6, val1, &prefix)
1685 && (cp == NULL
1686 || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
1687 || errno != ERANGE)
1688 && *endp == '\0'
1689 && bits <= INT_MAX
1690 && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX
1691 || errno != ERANGE)
1692 && *endp == '\0'
1693 && val <= INT_MAX)
1694 {
1695 struct prefixlist *newp = malloc (sizeof (*newp));
1696 if (newp == NULL)
1697 {
1698 free (line);
1699 fclose (fp);
1700 goto no_file;
1701 }
1702
1703 memcpy (&newp->entry.prefix, &prefix, sizeof (prefix));
1704 newp->entry.bits = bits;
1705 newp->entry.val = val;
1706 newp->next = *listp;
1707 *listp = newp;
1708 ++*lenp;
1709 *nullbitsp |= bits == 0;
1710 }
1711 }
1712 break;
1713
1714 case 6:
1715 if (strcmp (cmd, "reload") == 0)
1716 gaiconf_reload_flag = strcmp (val1, "yes") == 0;
1717 break;
1718
1719 case 10:
1720 if (strcmp (cmd, "precedence") == 0)
1721 {
1722 listp = &precedencelist;
1723 lenp = &nprecedencelist;
1724 nullbitsp = &precedencelist_nullbits;
1725 goto new_elem;
1726 }
1727 break;
1728 }
1729 }
1730
1731 free (line);
1732
1733 fclose (fp);
1734
1735 /* Create the array for the labels. */
1736 struct prefixentry *new_labels;
1737 if (nlabellist > 0)
1738 {
1739 if (!labellist_nullbits)
1740 ++nlabellist;
1741 new_labels = malloc (nlabellist * sizeof (*new_labels));
1742 if (new_labels == NULL)
1743 goto no_file;
1744
1745 int i = nlabellist;
1746 if (!labellist_nullbits)
1747 {
1748 --i;
1749 memset (&new_labels[i].prefix, '\0', sizeof (struct in6_addr));
1750 new_labels[i].bits = 0;
1751 new_labels[i].val = 1;
1752 }
1753
1754 struct prefixlist *l = labellist;
1755 while (i-- > 0)
1756 {
1757 new_labels[i] = l->entry;
1758 l = l->next;
1759 }
1760 free_prefixlist (labellist);
1761
1762 /* Sort the entries so that the most specific ones are at
1763 the beginning. */
1764 qsort (new_labels, nlabellist, sizeof (*new_labels), prefixcmp);
1765 }
1766 else
1767 new_labels = (struct prefixentry *) default_labels;
1768
1769 struct prefixentry *new_precedence;
1770 if (nprecedencelist > 0)
1771 {
1772 if (!precedencelist_nullbits)
1773 ++nprecedencelist;
1774 new_precedence = malloc (nprecedencelist * sizeof (*new_precedence));
1775 if (new_precedence == NULL)
1776 {
1777 if (new_labels != default_labels)
1778 free (new_labels);
1779 goto no_file;
1780 }
1781
1782 int i = nprecedencelist;
1783 if (!precedencelist_nullbits)
1784 {
1785 --i;
1786 memset (&new_precedence[i].prefix, '\0',
1787 sizeof (struct in6_addr));
1788 new_precedence[i].bits = 0;
1789 new_precedence[i].val = 40;
1790 }
1791
1792 struct prefixlist *l = precedencelist;
1793 while (i-- > 0)
1794 {
1795 new_precedence[i] = l->entry;
1796 l = l->next;
1797 }
1798 free_prefixlist (precedencelist);
1799
1800 /* Sort the entries so that the most specific ones are at
1801 the beginning. */
1802 qsort (new_precedence, nprecedencelist, sizeof (*new_labels),
1803 prefixcmp);
1804 }
1805 else
1806 new_precedence = (struct prefixentry *) default_precedence;
1807
1808 /* Now we are ready to replace the values. */
1809 const struct prefixentry *old = labels;
1810 labels = new_labels;
1811 if (old != default_labels)
1812 free ((void *) old);
1813
1814 old = precedence;
1815 precedence = new_precedence;
1816 if (old != default_precedence)
1817 free ((void *) old);
1818
1819 gaiconf_mtime = st.st_mtim;
1820 }
1821 else
1822 {
1823 no_file:
1824 free_prefixlist (labellist);
1825 free_prefixlist (precedencelist);
1826
1827 /* If we previously read the file but it is gone now, free the
1828 old data and use the builtin one. Leave the reload flag
1829 alone. */
1830 fini ();
1831 }
1832}
1833
1834
1835static void
1836gaiconf_reload (void)
1837{
1838 struct stat64 st;
1839 if (__xstat64 (_STAT_VER, GAICONF_FNAME, &st) != 0
1840 || memcmp (&st.st_mtim, &gaiconf_mtime, sizeof (gaiconf_mtime)) != 0)
1841 gaiconf_init ();
1842}
1843
1844
40a55d20
UD
1845int
1846getaddrinfo (const char *name, const char *service,
1847 const struct addrinfo *hints, struct addrinfo **pai)
46ec036d 1848{
786dcb62 1849 int i = 0, last_i = 0;
5ddb5bf5 1850 int nresults = 0;
786dcb62 1851 struct addrinfo *p = NULL;
46ec036d 1852 struct gaih_service gaih_service, *pservice;
925c3c5c 1853 struct addrinfo local_hints;
46ec036d 1854
40a55d20 1855 if (name != NULL && name[0] == '*' && name[1] == 0)
1fb05e3d
UD
1856 name = NULL;
1857
40a55d20 1858 if (service != NULL && service[0] == '*' && service[1] == 0)
1fb05e3d
UD
1859 service = NULL;
1860
40a55d20 1861 if (name == NULL && service == NULL)
46ec036d
UD
1862 return EAI_NONAME;
1863
40a55d20
UD
1864 if (hints == NULL)
1865 hints = &default_hints;
46ec036d 1866
925c3c5c
UD
1867 if (hints->ai_flags
1868 & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
e2fd3cbe 1869#ifdef HAVE_LIBIDN
3a0e90bd
UD
1870 |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED
1871 |AI_IDN_USE_STD3_ASCII_RULES
e2fd3cbe 1872#endif
c1d98085 1873 |AI_NUMERICSERV|AI_ALL))
46ec036d
UD
1874 return EAI_BADFLAGS;
1875
06877a37 1876 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
46ec036d
UD
1877 return EAI_BADFLAGS;
1878
cf6ada44 1879 struct in6addrinfo *in6ai = NULL;
3af48b5b
UD
1880 size_t in6ailen;
1881 bool seen_ipv4 = false;
1882 bool seen_ipv6 = false;
1883 /* We might need information about what kind of interfaces are available.
1884 But even if AI_ADDRCONFIG is not used, if the user requested IPv6
1885 addresses we have to know whether an address is deprecated or
1886 temporary. */
1887 if ((hints->ai_flags & AI_ADDRCONFIG) || hints->ai_family == PF_UNSPEC
1888 || hints->ai_family == PF_INET6)
1889 /* Determine whether we have IPv4 or IPv6 interfaces or both. We
1890 cannot cache the results since new interfaces could be added at
1891 any time. */
1892 __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
1893
925c3c5c
UD
1894 if (hints->ai_flags & AI_ADDRCONFIG)
1895 {
925c3c5c 1896 /* Now make a decision on what we return, if anything. */
c1d98085 1897 if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6))
925c3c5c
UD
1898 {
1899 /* If we haven't seen both IPv4 and IPv6 interfaces we can
1900 narrow down the search. */
1901 if (! seen_ipv4 || ! seen_ipv6)
1902 {
1903 local_hints = *hints;
1904 local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6;
1905 hints = &local_hints;
1906 }
1907 }
1908 else if ((hints->ai_family == PF_INET && ! seen_ipv4)
1909 || (hints->ai_family == PF_INET6 && ! seen_ipv6))
3af48b5b
UD
1910 {
1911 /* We cannot possibly return a valid answer. */
1912 free (in6ai);
1913 return EAI_NONAME;
1914 }
925c3c5c
UD
1915 }
1916
40a55d20
UD
1917 if (service && service[0])
1918 {
1919 char *c;
1920 gaih_service.name = service;
1921 gaih_service.num = strtoul (gaih_service.name, &c, 10);
c1d98085
UD
1922 if (*c != '\0')
1923 {
1924 if (hints->ai_flags & AI_NUMERICSERV)
3af48b5b
UD
1925 {
1926 free (in6ai);
1927 return EAI_NONAME;
1928 }
c1d98085
UD
1929
1930 gaih_service.num = -1;
1931 }
ca225a41 1932
40a55d20
UD
1933 pservice = &gaih_service;
1934 }
1935 else
46ec036d
UD
1936 pservice = NULL;
1937
786dcb62 1938 struct addrinfo **end;
1fb05e3d
UD
1939 if (pai)
1940 end = &p;
1941 else
1942 end = NULL;
1943
01abbc0f 1944 unsigned int naddrs = 0;
786dcb62
UD
1945#if 0
1946 /* If we would support more protocols than just IPv4 and IPv6 we
1947 would iterate over a table with appropriate callback functions.
1948 Since we currently only handle IPv4 and IPv6 this is not
1949 necessary. */
1950 const struct gaih *g = gaih;
1951 const struct gaih *pg = NULL;
1952 int j = 0;
40a55d20
UD
1953 while (g->gaih)
1954 {
7396d844 1955 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
40a55d20
UD
1956 {
1957 j++;
7396d844 1958 if (pg == NULL || pg->gaih != g->gaih)
40a55d20
UD
1959 {
1960 pg = g;
01abbc0f 1961 i = g->gaih (name, pservice, hints, end, &naddrs);
7396d844 1962 if (i != 0)
40a55d20 1963 {
a0bf6ac7
UD
1964 /* EAI_NODATA is a more specific result as it says that
1965 we found a result but it is not usable. */
1966 if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
1967 last_i = i;
1968
7396d844 1969 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
160d067b
UD
1970 {
1971 ++g;
1972 continue;
1973 }
40a55d20 1974
160d067b 1975 freeaddrinfo (p);
3af48b5b 1976 free (in6ai);
40a55d20 1977
7396d844 1978 return -(i & GAIH_EAI);
40a55d20
UD
1979 }
1980 if (end)
5ddb5bf5
UD
1981 while (*end)
1982 {
1983 end = &((*end)->ai_next);
1984 ++nresults;
1985 }
40a55d20 1986 }
46ec036d 1987 }
40a55d20 1988 ++g;
46ec036d 1989 }
46ec036d 1990
40a55d20 1991 if (j == 0)
3af48b5b
UD
1992 {
1993 free (in6ai);
1994 return EAI_FAMILY;
1995 }
786dcb62
UD
1996#else
1997 if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET
1998 || hints->ai_family == AF_INET6)
1999 {
2000 last_i = gaih_inet (name, pservice, hints, end, &naddrs);
2001 if (last_i != 0)
2002 {
2003 freeaddrinfo (p);
2004 free (in6ai);
2005
105a492d 2006 return -(last_i & GAIH_EAI);
786dcb62
UD
2007 }
2008 if (end)
2009 while (*end)
2010 {
2011 end = &((*end)->ai_next);
2012 ++nresults;
2013 }
2014 }
2015 else
2016 {
2017 free (in6ai);
2018 return EAI_FAMILY;
2019 }
2020#endif
46ec036d 2021
01abbc0f 2022 if (naddrs > 1)
5ddb5bf5 2023 {
77dd4c3b
UD
2024 /* Read the config file. */
2025 __libc_once_define (static, once);
2026 __typeof (once) old_once = once;
2027 __libc_once (once, gaiconf_init);
2028 if (old_once && gaiconf_reload_flag)
2029 gaiconf_reload ();
2030
5ddb5bf5
UD
2031 /* Sort results according to RFC 3484. */
2032 struct sort_result results[nresults];
2033 struct addrinfo *q;
d19687d6 2034 struct addrinfo *last = NULL;
c7fa647a 2035 char *canonname = NULL;
5ddb5bf5 2036
3af48b5b
UD
2037 /* If we have information about deprecated and temporary address
2038 sort the array now. */
2039 if (in6ai != NULL)
2040 qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp);
2041
d19687d6 2042 for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
5ddb5bf5
UD
2043 {
2044 results[i].dest_addr = q;
2045 results[i].got_source_addr = false;
2046
007a3ddc 2047 /* If we just looked up the address for a different
d19687d6
UD
2048 protocol, reuse the result. */
2049 if (last != NULL && last->ai_addrlen == q->ai_addrlen
2050 && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0)
5ddb5bf5 2051 {
d19687d6
UD
2052 memcpy (&results[i].source_addr, &results[i - 1].source_addr,
2053 results[i - 1].source_addr_len);
2054 results[i].source_addr_len = results[i - 1].source_addr_len;
2055 results[i].got_source_addr = results[i - 1].got_source_addr;
3af48b5b 2056 results[i].source_addr_flags = results[i - 1].source_addr_flags;
d19687d6
UD
2057 }
2058 else
2059 {
3af48b5b
UD
2060 results[i].source_addr_flags = 0;
2061
007a3ddc
UD
2062 /* We overwrite the type with SOCK_DGRAM since we do not
2063 want connect() to connect to the other side. If we
2064 cannot determine the source address remember this
2065 fact. */
d19687d6 2066 int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
007a3ddc
UD
2067 socklen_t sl = sizeof (results[i].source_addr);
2068 if (fd != -1
2069 && __connect (fd, q->ai_addr, q->ai_addrlen) == 0
2070 && __getsockname (fd,
2071 (struct sockaddr *) &results[i].source_addr,
2072 &sl) == 0)
d19687d6 2073 {
007a3ddc
UD
2074 results[i].source_addr_len = sl;
2075 results[i].got_source_addr = true;
3af48b5b
UD
2076
2077 if (q->ai_family == PF_INET6 && in6ai != NULL)
2078 {
2079 /* See whether the address is the list of deprecated
2080 or temporary addresses. */
2081 struct in6addrinfo tmp;
2082 memcpy (tmp.addr, q->ai_addr, IN6ADDRSZ);
2083
2084 struct in6addrinfo *found
2085 = bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
2086 in6aicmp);
2087 if (found != NULL)
2088 results[i].source_addr_flags = found->flags;
2089 }
d19687d6 2090 }
007a3ddc
UD
2091 else
2092 /* Just make sure that if we have to process the same
2093 address again we do not copy any memory. */
2094 results[i].source_addr_len = 0;
2095
2096 if (fd != -1)
2097 close_not_cancel_no_status (fd);
5ddb5bf5 2098 }
c7fa647a
UD
2099
2100 /* Remember the canonical name. */
2101 if (q->ai_canonname != NULL)
2102 {
2103 assert (canonname == NULL);
2104 canonname = q->ai_canonname;
2105 q->ai_canonname = NULL;
2106 }
5ddb5bf5
UD
2107 }
2108
2109 /* We got all the source addresses we can get, now sort using
2110 the information. */
2111 qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
2112
2113 /* Queue the results up as they come out of sorting. */
2114 q = p = results[0].dest_addr;
2115 for (i = 1; i < nresults; ++i)
2116 q = q->ai_next = results[i].dest_addr;
2117 q->ai_next = NULL;
c7fa647a
UD
2118
2119 /* Fill in the canonical name into the new first entry. */
2120 p->ai_canonname = canonname;
5ddb5bf5
UD
2121 }
2122
3af48b5b
UD
2123 free (in6ai);
2124
40a55d20
UD
2125 if (p)
2126 {
2127 *pai = p;
2128 return 0;
2129 }
46ec036d 2130
a0bf6ac7 2131 if (pai == NULL && last_i == 0)
1fb05e3d
UD
2132 return 0;
2133
a0bf6ac7 2134 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
46ec036d 2135}
9b0b40d3 2136libc_hidden_def (getaddrinfo)
46ec036d 2137
2f7f7bc6
UD
2138static_link_warning (getaddrinfo)
2139
40a55d20
UD
2140void
2141freeaddrinfo (struct addrinfo *ai)
46ec036d
UD
2142{
2143 struct addrinfo *p;
2144
40a55d20
UD
2145 while (ai != NULL)
2146 {
2147 p = ai;
2148 ai = ai->ai_next;
b9343764 2149 free (p->ai_canonname);
40a55d20
UD
2150 free (p);
2151 }
46ec036d 2152}
9b0b40d3 2153libc_hidden_def (freeaddrinfo)