| 1 | /* The Inner Net License, Version 2.00 |
|---|
| 2 | |
|---|
| 3 | The author(s) grant permission for redistribution and use in source and |
|---|
| 4 | binary forms, with or without modification, of the software and documentation |
|---|
| 5 | provided that the following conditions are met: |
|---|
| 6 | |
|---|
| 7 | 0. If you receive a version of the software that is specifically labelled |
|---|
| 8 | as not being for redistribution (check the version message and/or README), |
|---|
| 9 | you are not permitted to redistribute that version of the software in any |
|---|
| 10 | way or form. |
|---|
| 11 | 1. All terms of the all other applicable copyrights and licenses must be |
|---|
| 12 | followed. |
|---|
| 13 | 2. Redistributions of source code must retain the authors' copyright |
|---|
| 14 | notice(s), this list of conditions, and the following disclaimer. |
|---|
| 15 | 3. Redistributions in binary form must reproduce the authors' copyright |
|---|
| 16 | notice(s), this list of conditions, and the following disclaimer in the |
|---|
| 17 | documentation and/or other materials provided with the distribution. |
|---|
| 18 | 4. [The copyright holder has authorized the removal of this clause.] |
|---|
| 19 | 5. 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 | |
|---|
| 23 | THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY |
|---|
| 24 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|---|
| 25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|---|
| 26 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY |
|---|
| 27 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|---|
| 28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|---|
| 29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|---|
| 30 | ANY 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 |
|---|
| 32 | SOFTWARE, 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 | |
|---|
| 38 | #include <alloca.h> |
|---|
| 39 | #include <sys/types.h> |
|---|
| 40 | #include <assert.h> |
|---|
| 41 | #include <errno.h> |
|---|
| 42 | #include <ifaddrs.h> |
|---|
| 43 | #include <netdb.h> |
|---|
| 44 | #include <resolv.h> |
|---|
| 45 | #include <stdbool.h> |
|---|
| 46 | #include <stdio.h> |
|---|
| 47 | #include <stdlib.h> |
|---|
| 48 | #include <string.h> |
|---|
| 49 | #include <unistd.h> |
|---|
| 50 | #include <arpa/inet.h> |
|---|
| 51 | #include <sys/socket.h> |
|---|
| 52 | #include <netinet/in.h> |
|---|
| 53 | #include <sys/un.h> |
|---|
| 54 | #include <sys/utsname.h> |
|---|
| 55 | #include <net/if.h> |
|---|
| 56 | #include <nsswitch.h> |
|---|
| 57 | #include <not-cancel.h> |
|---|
| 58 | #include <nscd/nscd-client.h> |
|---|
| 59 | #include <nscd/nscd_proto.h> |
|---|
| 60 | #include <limits.h> |
|---|
| 61 | #include "local.h" |
|---|
| 62 | |
|---|
| 63 | #ifdef HAVE_LIBIDN |
|---|
| 64 | extern int __idna_to_ascii_lz (const char *input, char **output, int flags); |
|---|
| 65 | extern int __idna_to_unicode_lzlz (const char *input, char **output, |
|---|
| 66 | int flags); |
|---|
| 67 | # include <libidn/idna.h> |
|---|
| 68 | #endif |
|---|
| 69 | |
|---|
| 70 | #define GAIH_OKIFUNSPEC 0x0100 |
|---|
| 71 | #define GAIH_EAI ~(GAIH_OKIFUNSPEC) |
|---|
| 72 | |
|---|
| 73 | #ifndef UNIX_PATH_MAX |
|---|
| 74 | #define UNIX_PATH_MAX 108 |
|---|
| 75 | #endif |
|---|
| 76 | |
|---|
| 77 | struct gaih_service |
|---|
| 78 | { |
|---|
| 79 | const char *name; |
|---|
| 80 | int num; |
|---|
| 81 | }; |
|---|
| 82 | |
|---|
| 83 | struct gaih_servtuple |
|---|
| 84 | { |
|---|
| 85 | struct gaih_servtuple *next; |
|---|
| 86 | int socktype; |
|---|
| 87 | int protocol; |
|---|
| 88 | int port; |
|---|
| 89 | }; |
|---|
| 90 | |
|---|
| 91 | static const struct gaih_servtuple nullserv; |
|---|
| 92 | |
|---|
| 93 | struct gaih_addrtuple |
|---|
| 94 | { |
|---|
| 95 | struct gaih_addrtuple *next; |
|---|
| 96 | char *name; |
|---|
| 97 | int family; |
|---|
| 98 | uint32_t addr[4]; |
|---|
| 99 | uint32_t scopeid; |
|---|
| 100 | }; |
|---|
| 101 | |
|---|
| 102 | struct gaih_typeproto |
|---|
| 103 | { |
|---|
| 104 | int socktype; |
|---|
| 105 | int protocol; |
|---|
| 106 | char name[4]; |
|---|
| 107 | int protoflag; |
|---|
| 108 | }; |
|---|
| 109 | |
|---|
| 110 | /* Values for `protoflag'. */ |
|---|
| 111 | #define GAI_PROTO_NOSERVICE 1 |
|---|
| 112 | #define GAI_PROTO_PROTOANY 2 |
|---|
| 113 | |
|---|
| 114 | static const struct gaih_typeproto gaih_inet_typeproto[] = |
|---|
| 115 | { |
|---|
| 116 | { 0, 0, "", 0 }, |
|---|
| 117 | { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 }, |
|---|
| 118 | { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 }, |
|---|
| 119 | { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE }, |
|---|
| 120 | { 0, 0, "", 0 } |
|---|
| 121 | }; |
|---|
| 122 | |
|---|
| 123 | struct gaih |
|---|
| 124 | { |
|---|
| 125 | int family; |
|---|
| 126 | int (*gaih)(const char *name, const struct gaih_service *service, |
|---|
| 127 | const struct addrinfo *req, struct addrinfo **pai); |
|---|
| 128 | }; |
|---|
| 129 | |
|---|
| 130 | static const struct addrinfo default_hints = |
|---|
| 131 | { |
|---|
| 132 | .ai_flags = AI_DEFAULT, |
|---|
| 133 | .ai_family = PF_UNSPEC, |
|---|
| 134 | .ai_socktype = 0, |
|---|
| 135 | .ai_protocol = 0, |
|---|
| 136 | .ai_addrlen = 0, |
|---|
| 137 | .ai_addr = NULL, |
|---|
| 138 | .ai_canonname = NULL, |
|---|
| 139 | .ai_next = NULL |
|---|
| 140 | }; |
|---|
| 141 | |
|---|
| 142 | #define s6_addr32 __u6_addr.__u6_addr32 |
|---|
| 143 | |
|---|
| 144 | #if 0 |
|---|
| 145 | /* Using Unix sockets this way is a security risk. */ |
|---|
| 146 | static int |
|---|
| 147 | gaih_local (const char *name, const struct gaih_service *service, |
|---|
| 148 | const struct addrinfo *req, struct addrinfo **pai) |
|---|
| 149 | { |
|---|
| 150 | struct utsname utsname; |
|---|
| 151 | |
|---|
| 152 | if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST)) |
|---|
| 153 | return GAIH_OKIFUNSPEC | -EAI_NONAME; |
|---|
| 154 | |
|---|
| 155 | if ((name != NULL) || (req->ai_flags & AI_CANONNAME)) |
|---|
| 156 | if (uname (&utsname) < 0) |
|---|
| 157 | return -EAI_SYSTEM; |
|---|
| 158 | |
|---|
| 159 | if (name != NULL) |
|---|
| 160 | { |
|---|
| 161 | if (strcmp(name, "localhost") && |
|---|
| 162 | strcmp(name, "local") && |
|---|
| 163 | strcmp(name, "unix") && |
|---|
| 164 | strcmp(name, utsname.nodename)) |
|---|
| 165 | return GAIH_OKIFUNSPEC | -EAI_NONAME; |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | if (req->ai_protocol || req->ai_socktype) |
|---|
| 169 | { |
|---|
| 170 | const struct gaih_typeproto *tp = gaih_inet_typeproto + 1; |
|---|
| 171 | |
|---|
| 172 | while (tp->name[0] |
|---|
| 173 | && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0 |
|---|
| 174 | || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype) |
|---|
| 175 | || (req->ai_protocol != 0 |
|---|
| 176 | && !(tp->protoflag & GAI_PROTO_PROTOANY) |
|---|
| 177 | && req->ai_protocol != tp->protocol))) |
|---|
| 178 | ++tp; |
|---|
| 179 | |
|---|
| 180 | if (! tp->name[0]) |
|---|
| 181 | { |
|---|
| 182 | if (req->ai_socktype) |
|---|
| 183 | return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE); |
|---|
| 184 | else |
|---|
| 185 | return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
|---|
| 186 | } |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un) |
|---|
| 190 | + ((req->ai_flags & AI_CANONNAME) |
|---|
| 191 | ? (strlen(utsname.nodename) + 1): 0)); |
|---|
| 192 | if (*pai == NULL) |
|---|
| 193 | return -EAI_MEMORY; |
|---|
| 194 | |
|---|
| 195 | (*pai)->ai_next = NULL; |
|---|
| 196 | (*pai)->ai_flags = req->ai_flags; |
|---|
| 197 | (*pai)->ai_family = AF_LOCAL; |
|---|
| 198 | (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM; |
|---|
| 199 | (*pai)->ai_protocol = req->ai_protocol; |
|---|
| 200 | (*pai)->ai_addrlen = sizeof (struct sockaddr_un); |
|---|
| 201 | (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo); |
|---|
| 202 | |
|---|
| 203 | #if SALEN |
|---|
| 204 | ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len = |
|---|
| 205 | sizeof (struct sockaddr_un); |
|---|
| 206 | #endif /* SALEN */ |
|---|
| 207 | |
|---|
| 208 | ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL; |
|---|
| 209 | memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX); |
|---|
| 210 | |
|---|
| 211 | if (service) |
|---|
| 212 | { |
|---|
| 213 | struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr; |
|---|
| 214 | |
|---|
| 215 | if (strchr (service->name, '/') != NULL) |
|---|
| 216 | { |
|---|
| 217 | if (strlen (service->name) >= sizeof (sunp->sun_path)) |
|---|
| 218 | return GAIH_OKIFUNSPEC | -EAI_SERVICE; |
|---|
| 219 | |
|---|
| 220 | strcpy (sunp->sun_path, service->name); |
|---|
| 221 | } |
|---|
| 222 | else |
|---|
| 223 | { |
|---|
| 224 | if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >= |
|---|
| 225 | sizeof (sunp->sun_path)) |
|---|
| 226 | return GAIH_OKIFUNSPEC | -EAI_SERVICE; |
|---|
| 227 | |
|---|
| 228 | __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name); |
|---|
| 229 | } |
|---|
| 230 | } |
|---|
| 231 | else |
|---|
| 232 | { |
|---|
| 233 | /* This is a dangerous use of the interface since there is a time |
|---|
| 234 | window between the test for the file and the actual creation |
|---|
| 235 | (done by the caller) in which a file with the same name could |
|---|
| 236 | be created. */ |
|---|
| 237 | char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path; |
|---|
| 238 | |
|---|
| 239 | if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0), |
|---|
| 240 | 0) != 0 |
|---|
| 241 | || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0) |
|---|
| 242 | return -EAI_SYSTEM; |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | if (req->ai_flags & AI_CANONNAME) |
|---|
| 246 | (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo) |
|---|
| 247 | + sizeof (struct sockaddr_un), |
|---|
| 248 | utsname.nodename); |
|---|
| 249 | else |
|---|
| 250 | (*pai)->ai_canonname = NULL; |
|---|
| 251 | return 0; |
|---|
| 252 | } |
|---|
| 253 | #endif /* 0 */ |
|---|
| 254 | |
|---|
| 255 | static int |
|---|
| 256 | gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, |
|---|
| 257 | const struct addrinfo *req, struct gaih_servtuple *st) |
|---|
| 258 | { |
|---|
| 259 | struct servent *s; |
|---|
| 260 | size_t tmpbuflen = 1024; |
|---|
| 261 | struct servent ts; |
|---|
| 262 | char *tmpbuf; |
|---|
| 263 | int r; |
|---|
| 264 | |
|---|
| 265 | do |
|---|
| 266 | { |
|---|
| 267 | tmpbuf = alloca (tmpbuflen); |
|---|
| 268 | |
|---|
| 269 | r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen, |
|---|
| 270 | &s); |
|---|
| 271 | if (r != 0 || s == NULL) |
|---|
| 272 | { |
|---|
| 273 | if (r == ERANGE) |
|---|
| 274 | tmpbuflen *= 2; |
|---|
| 275 | else |
|---|
| 276 | return GAIH_OKIFUNSPEC | -EAI_SERVICE; |
|---|
| 277 | } |
|---|
| 278 | } |
|---|
| 279 | while (r); |
|---|
| 280 | |
|---|
| 281 | st->next = NULL; |
|---|
| 282 | st->socktype = tp->socktype; |
|---|
| 283 | st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) |
|---|
| 284 | ? req->ai_protocol : tp->protocol); |
|---|
| 285 | st->port = s->s_port; |
|---|
| 286 | |
|---|
| 287 | return 0; |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | #define gethosts(_family, _type) \ |
|---|
| 291 | { \ |
|---|
| 292 | int i; \ |
|---|
| 293 | int herrno; \ |
|---|
| 294 | struct hostent th; \ |
|---|
| 295 | struct hostent *h; \ |
|---|
| 296 | char *localcanon = NULL; \ |
|---|
| 297 | no_data = 0; \ |
|---|
| 298 | while (1) { \ |
|---|
| 299 | rc = 0; \ |
|---|
| 300 | status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen, \ |
|---|
| 301 | &rc, &herrno, NULL, &localcanon)); \ |
|---|
| 302 | if (rc != ERANGE || herrno != NETDB_INTERNAL) \ |
|---|
| 303 | break; \ |
|---|
| 304 | tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \ |
|---|
| 305 | } \ |
|---|
| 306 | if (status == NSS_STATUS_SUCCESS && rc == 0) \ |
|---|
| 307 | h = &th; \ |
|---|
| 308 | else \ |
|---|
| 309 | h = NULL; \ |
|---|
| 310 | if (rc != 0) \ |
|---|
| 311 | { \ |
|---|
| 312 | if (herrno == NETDB_INTERNAL) \ |
|---|
| 313 | { \ |
|---|
| 314 | h_errno = (herrno); \ |
|---|
| 315 | return -EAI_SYSTEM; \ |
|---|
| 316 | } \ |
|---|
| 317 | if (herrno == TRY_AGAIN) \ |
|---|
| 318 | no_data = EAI_AGAIN; \ |
|---|
| 319 | else \ |
|---|
| 320 | no_data = herrno == NO_DATA; \ |
|---|
| 321 | } \ |
|---|
| 322 | else if (h != NULL) \ |
|---|
| 323 | { \ |
|---|
| 324 | for (i = 0; h->h_addr_list[i]; i++) \ |
|---|
| 325 | { \ |
|---|
| 326 | if (*pat == NULL) \ |
|---|
| 327 | { \ |
|---|
| 328 | *pat = alloca (sizeof (struct gaih_addrtuple)); \ |
|---|
| 329 | (*pat)->scopeid = 0; \ |
|---|
| 330 | } \ |
|---|
| 331 | uint32_t *addr = (*pat)->addr; \ |
|---|
| 332 | (*pat)->next = NULL; \ |
|---|
| 333 | if (i == 0) \ |
|---|
| 334 | { \ |
|---|
| 335 | (*pat)->name = alloca (strlen (h->h_name) + 1); \ |
|---|
| 336 | strcpy ((*pat)->name, h->h_name); \ |
|---|
| 337 | } \ |
|---|
| 338 | else \ |
|---|
| 339 | (*pat)->name = NULL; \ |
|---|
| 340 | if (_family == AF_INET && req->ai_family == AF_INET6) \ |
|---|
| 341 | { \ |
|---|
| 342 | (*pat)->family = AF_INET6; \ |
|---|
| 343 | addr[3] = *(uint32_t *) h->h_addr_list[i]; \ |
|---|
| 344 | addr[2] = htonl (0xffff); \ |
|---|
| 345 | addr[1] = 0; \ |
|---|
| 346 | addr[0] = 0; \ |
|---|
| 347 | } \ |
|---|
| 348 | else \ |
|---|
| 349 | { \ |
|---|
| 350 | (*pat)->family = _family; \ |
|---|
| 351 | memcpy (addr, h->h_addr_list[i], sizeof(_type)); \ |
|---|
| 352 | } \ |
|---|
| 353 | pat = &((*pat)->next); \ |
|---|
| 354 | } \ |
|---|
| 355 | \ |
|---|
| 356 | if (localcanon != NULL && canon == NULL) \ |
|---|
| 357 | { \ |
|---|
| 358 | canon = alloca (strlen (localcanon) + 1); \ |
|---|
| 359 | strcpy (canon, localcanon); \ |
|---|
| 360 | } \ |
|---|
| 361 | \ |
|---|
| 362 | if (_family == AF_INET6 && i > 0) \ |
|---|
| 363 | got_ipv6 = true; \ |
|---|
| 364 | } \ |
|---|
| 365 | } |
|---|
| 366 | |
|---|
| 367 | |
|---|
| 368 | typedef enum nss_status (*nss_gethostbyname3_r) |
|---|
| 369 | (const char *name, int af, struct hostent *host, |
|---|
| 370 | char *buffer, size_t buflen, int *errnop, |
|---|
| 371 | int *h_errnop, int32_t *ttlp, char **canonp); |
|---|
| 372 | typedef enum nss_status (*nss_getcanonname_r) |
|---|
| 373 | (const char *name, char *buffer, size_t buflen, char **result, |
|---|
| 374 | int *errnop, int *h_errnop); |
|---|
| 375 | extern service_user *__nss_hosts_database attribute_hidden; |
|---|
| 376 | |
|---|
| 377 | static int |
|---|
| 378 | gaih_inet (const char *name, const struct gaih_service *service, |
|---|
| 379 | const struct addrinfo *req, struct addrinfo **pai) |
|---|
| 380 | { |
|---|
| 381 | const struct gaih_typeproto *tp = gaih_inet_typeproto; |
|---|
| 382 | struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; |
|---|
| 383 | struct gaih_addrtuple *at = NULL; |
|---|
| 384 | int rc; |
|---|
| 385 | bool got_ipv6 = false; |
|---|
| 386 | const char *canon = NULL; |
|---|
| 387 | const char *orig_name = name; |
|---|
| 388 | |
|---|
| 389 | if (req->ai_protocol || req->ai_socktype) |
|---|
| 390 | { |
|---|
| 391 | ++tp; |
|---|
| 392 | |
|---|
| 393 | while (tp->name[0] |
|---|
| 394 | && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype) |
|---|
| 395 | || (req->ai_protocol != 0 |
|---|
| 396 | && !(tp->protoflag & GAI_PROTO_PROTOANY) |
|---|
| 397 | && req->ai_protocol != tp->protocol))) |
|---|
| 398 | ++tp; |
|---|
| 399 | |
|---|
| 400 | if (! tp->name[0]) |
|---|
| 401 | { |
|---|
| 402 | if (req->ai_socktype) |
|---|
| 403 | return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE); |
|---|
| 404 | else |
|---|
| 405 | return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
|---|
| 406 | } |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | if (service != NULL) |
|---|
| 410 | { |
|---|
| 411 | if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) |
|---|
| 412 | return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
|---|
| 413 | |
|---|
| 414 | if (service->num < 0) |
|---|
| 415 | { |
|---|
| 416 | if (tp->name[0]) |
|---|
| 417 | { |
|---|
| 418 | st = (struct gaih_servtuple *) |
|---|
| 419 | alloca (sizeof (struct gaih_servtuple)); |
|---|
| 420 | |
|---|
| 421 | if ((rc = gaih_inet_serv (service->name, tp, req, st))) |
|---|
| 422 | return rc; |
|---|
| 423 | } |
|---|
| 424 | else |
|---|
| 425 | { |
|---|
| 426 | struct gaih_servtuple **pst = &st; |
|---|
| 427 | for (tp++; tp->name[0]; tp++) |
|---|
| 428 | { |
|---|
| 429 | struct gaih_servtuple *newp; |
|---|
| 430 | |
|---|
| 431 | if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) |
|---|
| 432 | continue; |
|---|
| 433 | |
|---|
| 434 | if (req->ai_socktype != 0 |
|---|
| 435 | && req->ai_socktype != tp->socktype) |
|---|
| 436 | continue; |
|---|
| 437 | if (req->ai_protocol != 0 |
|---|
| 438 | && !(tp->protoflag & GAI_PROTO_PROTOANY) |
|---|
| 439 | && req->ai_protocol != tp->protocol) |
|---|
| 440 | continue; |
|---|
| 441 | |
|---|
| 442 | newp = (struct gaih_servtuple *) |
|---|
| 443 | alloca (sizeof (struct gaih_servtuple)); |
|---|
| 444 | |
|---|
| 445 | if ((rc = gaih_inet_serv (service->name, tp, req, newp))) |
|---|
| 446 | { |
|---|
| 447 | if (rc & GAIH_OKIFUNSPEC) |
|---|
| 448 | continue; |
|---|
| 449 | return rc; |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| 452 | *pst = newp; |
|---|
| 453 | pst = &(newp->next); |
|---|
| 454 | } |
|---|
| 455 | if (st == (struct gaih_servtuple *) &nullserv) |
|---|
| 456 | return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
|---|
| 457 | } |
|---|
| 458 | } |
|---|
| 459 | else |
|---|
| 460 | { |
|---|
| 461 | if (req->ai_socktype || req->ai_protocol) |
|---|
| 462 | { |
|---|
| 463 | st = alloca (sizeof (struct gaih_servtuple)); |
|---|
| 464 | st->next = NULL; |
|---|
| 465 | st->socktype = tp->socktype; |
|---|
| 466 | st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) |
|---|
| 467 | ? req->ai_protocol : tp->protocol); |
|---|
| 468 | st->port = htons (service->num); |
|---|
| 469 | } |
|---|
| 470 | else |
|---|
| 471 | { |
|---|
| 472 | /* Neither socket type nor protocol is set. Return all |
|---|
| 473 | socket types we know about. */ |
|---|
| 474 | struct gaih_servtuple **lastp = &st; |
|---|
| 475 | for (tp = gaih_inet_typeproto + 1; tp->name[0]; ++tp) |
|---|
| 476 | if ((tp->protoflag & GAI_PROTO_NOSERVICE) == 0) |
|---|
| 477 | { |
|---|
| 478 | struct gaih_servtuple *newp; |
|---|
| 479 | |
|---|
| 480 | newp = alloca (sizeof (struct gaih_servtuple)); |
|---|
| 481 | newp->next = NULL; |
|---|
| 482 | newp->socktype = tp->socktype; |
|---|
| 483 | newp->protocol = tp->protocol; |
|---|
| 484 | newp->port = htons (service->num); |
|---|
| 485 | |
|---|
| 486 | *lastp = newp; |
|---|
| 487 | lastp = &newp->next; |
|---|
| 488 | } |
|---|
| 489 | } |
|---|
| 490 | } |
|---|
| 491 | } |
|---|
| 492 | else if (req->ai_socktype || req->ai_protocol) |
|---|
| 493 | { |
|---|
| 494 | st = alloca (sizeof (struct gaih_servtuple)); |
|---|
| 495 | st->next = NULL; |
|---|
| 496 | st->socktype = tp->socktype; |
|---|
| 497 | st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) |
|---|
| 498 | ? req->ai_protocol : tp->protocol); |
|---|
| 499 | st->port = 0; |
|---|
| 500 | } |
|---|
| 501 | else |
|---|
| 502 | { |
|---|
| 503 | /* Neither socket type nor protocol is set. Return all socket types |
|---|
| 504 | we know about. */ |
|---|
| 505 | struct gaih_servtuple **lastp = &st; |
|---|
| 506 | for (++tp; tp->name[0]; ++tp) |
|---|
| 507 | { |
|---|
| 508 | struct gaih_servtuple *newp; |
|---|
| 509 | |
|---|
| 510 | newp = alloca (sizeof (struct gaih_servtuple)); |
|---|
| 511 | newp->next = NULL; |
|---|
| 512 | newp->socktype = tp->socktype; |
|---|
| 513 | newp->protocol = tp->protocol; |
|---|
| 514 | newp->port = 0; |
|---|
| 515 | |
|---|
| 516 | *lastp = newp; |
|---|
| 517 | lastp = &newp->next; |
|---|
| 518 | } |
|---|
| 519 | } |
|---|
| 520 | |
|---|
| 521 | if (name != NULL) |
|---|
| 522 | { |
|---|
| 523 | at = alloca (sizeof (struct gaih_addrtuple)); |
|---|
| 524 | |
|---|
| 525 | at->family = AF_UNSPEC; |
|---|
| 526 | at->scopeid = 0; |
|---|
| 527 | at->next = NULL; |
|---|
| 528 | |
|---|
| 529 | #ifdef HAVE_LIBIDN |
|---|
| 530 | if (req->ai_flags & AI_IDN) |
|---|
| 531 | { |
|---|
| 532 | int idn_flags = 0; |
|---|
| 533 | if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED) |
|---|
| 534 | idn_flags |= IDNA_ALLOW_UNASSIGNED; |
|---|
| 535 | if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES) |
|---|
| 536 | idn_flags |= IDNA_USE_STD3_ASCII_RULES; |
|---|
| 537 | |
|---|
| 538 | char *p = NULL; |
|---|
| 539 | rc = __idna_to_ascii_lz (name, &p, idn_flags); |
|---|
| 540 | if (rc != IDNA_SUCCESS) |
|---|
| 541 | { |
|---|
| 542 | if (rc == IDNA_MALLOC_ERROR) |
|---|
| 543 | return -EAI_MEMORY; |
|---|
| 544 | if (rc == IDNA_DLOPEN_ERROR) |
|---|
| 545 | return -EAI_SYSTEM; |
|---|
| 546 | return -EAI_IDN_ENCODE; |
|---|
| 547 | } |
|---|
| 548 | /* In case the output string is the same as the input string |
|---|
| 549 | no new string has been allocated. */ |
|---|
| 550 | if (p != name) |
|---|
| 551 | { |
|---|
| 552 | name = alloca (strlen (p) + 1); |
|---|
| 553 | strcpy (name, p); |
|---|
| 554 | free (p); |
|---|
| 555 | } |
|---|
| 556 | } |
|---|
| 557 | #endif |
|---|
| 558 | |
|---|
| 559 | if (inet_aton (name, (struct in_addr *) at->addr) != 0) |
|---|
| 560 | { |
|---|
| 561 | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) |
|---|
| 562 | at->family = AF_INET; |
|---|
| 563 | else if (req->ai_family == AF_INET6 && req->ai_flags & AI_V4MAPPED) |
|---|
| 564 | { |
|---|
| 565 | at->addr[3] = at->addr[0]; |
|---|
| 566 | at->addr[2] = htonl (0xffff); |
|---|
| 567 | at->addr[1] = 0; |
|---|
| 568 | at->addr[0] = 0; |
|---|
| 569 | at->family = AF_INET6; |
|---|
| 570 | } |
|---|
| 571 | else |
|---|
| 572 | return -EAI_ADDRFAMILY; |
|---|
| 573 | |
|---|
| 574 | dupname: |
|---|
| 575 | if (req->ai_flags & AI_CANONNAME) |
|---|
| 576 | { |
|---|
| 577 | canon = strdup (name); |
|---|
| 578 | if (canon == NULL) |
|---|
| 579 | return -EAI_MEMORY; |
|---|
| 580 | } |
|---|
| 581 | } |
|---|
| 582 | |
|---|
| 583 | if (at->family == AF_UNSPEC) |
|---|
| 584 | { |
|---|
| 585 | char *scope_delim; |
|---|
| 586 | char *namebuf = alloca (strlen (name) + 1); |
|---|
| 587 | strcpy (namebuf, name); |
|---|
| 588 | |
|---|
| 589 | scope_delim = strchr (namebuf, SCOPE_DELIMITER); |
|---|
| 590 | if (scope_delim != NULL) |
|---|
| 591 | *scope_delim = '\0'; |
|---|
| 592 | |
|---|
| 593 | if (inet_pton (AF_INET6, namebuf, at->addr) > 0) |
|---|
| 594 | { |
|---|
| 595 | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) |
|---|
| 596 | at->family = AF_INET6; |
|---|
| 597 | else if (req->ai_family == AF_INET |
|---|
| 598 | && IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)at->addr)) |
|---|
| 599 | { |
|---|
| 600 | at->addr[0] = at->addr[3]; |
|---|
| 601 | at->family = AF_INET; |
|---|
| 602 | } |
|---|
| 603 | else |
|---|
| 604 | return -EAI_ADDRFAMILY; |
|---|
| 605 | |
|---|
| 606 | if (scope_delim != NULL) |
|---|
| 607 | { |
|---|
| 608 | int try_numericscope = 0; |
|---|
| 609 | if (IN6_IS_ADDR_LINKLOCAL ((struct in6_addr *)at->addr) |
|---|
| 610 | || IN6_IS_ADDR_MC_LINKLOCAL ((struct in6_addr *)at->addr)) |
|---|
| 611 | { |
|---|
| 612 | at->scopeid = if_nametoindex (scope_delim + 1); |
|---|
| 613 | if (at->scopeid == 0) |
|---|
| 614 | try_numericscope = 1; |
|---|
| 615 | } |
|---|
| 616 | else |
|---|
| 617 | try_numericscope = 1; |
|---|
| 618 | |
|---|
| 619 | if (try_numericscope != 0) |
|---|
| 620 | { |
|---|
| 621 | char *end; |
|---|
| 622 | assert (sizeof (uint32_t) <= sizeof (unsigned long)); |
|---|
| 623 | at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end, |
|---|
| 624 | 10); |
|---|
| 625 | if (*end != '\0') |
|---|
| 626 | return GAIH_OKIFUNSPEC | -EAI_NONAME; |
|---|
| 627 | } |
|---|
| 628 | } |
|---|
| 629 | |
|---|
| 630 | goto dupname; |
|---|
| 631 | } |
|---|
| 632 | } |
|---|
| 633 | |
|---|
| 634 | if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0) |
|---|
| 635 | { |
|---|
| 636 | struct gaih_addrtuple **pat = &at; |
|---|
| 637 | int no_data = 0; |
|---|
| 638 | int no_inet6_data = 0; |
|---|
| 639 | service_user *nip = NULL; |
|---|
| 640 | enum nss_status inet6_status = NSS_STATUS_UNAVAIL; |
|---|
| 641 | enum nss_status status = NSS_STATUS_UNAVAIL; |
|---|
| 642 | int no_more; |
|---|
| 643 | int old_res_options; |
|---|
| 644 | |
|---|
| 645 | /* If we do not have to look for IPv4 and IPv6 together, use |
|---|
| 646 | the simple, old functions. */ |
|---|
| 647 | if (req->ai_family == AF_INET || req->ai_family == AF_INET6) |
|---|
| 648 | { |
|---|
| 649 | int family = req->ai_family; |
|---|
| 650 | size_t tmpbuflen = 512; |
|---|
| 651 | char *tmpbuf = alloca (tmpbuflen); |
|---|
| 652 | int rc; |
|---|
| 653 | struct hostent th; |
|---|
| 654 | struct hostent *h; |
|---|
| 655 | int herrno; |
|---|
| 656 | |
|---|
| 657 | simple_again: |
|---|
| 658 | while (1) |
|---|
| 659 | { |
|---|
| 660 | rc = __gethostbyname2_r (name, family, &th, tmpbuf, |
|---|
| 661 | tmpbuflen, &h, &herrno); |
|---|
| 662 | if (rc != ERANGE || herrno != NETDB_INTERNAL) |
|---|
| 663 | break; |
|---|
| 664 | tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); |
|---|
| 665 | } |
|---|
| 666 | |
|---|
| 667 | if (rc == 0) |
|---|
| 668 | { |
|---|
| 669 | if (h == NULL) |
|---|
| 670 | { |
|---|
| 671 | if (req->ai_family == AF_INET6 |
|---|
| 672 | && (req->ai_flags & AI_V4MAPPED) |
|---|
| 673 | && family == AF_INET6) |
|---|
| 674 | { |
|---|
| 675 | /* Try again, this time looking for IPv4 |
|---|
| 676 | addresses. */ |
|---|
| 677 | family = AF_INET; |
|---|
| 678 | goto simple_again; |
|---|
| 679 | } |
|---|
| 680 | } |
|---|
| 681 | else |
|---|
| 682 | { |
|---|
| 683 | /* We found data, now convert it into the list. */ |
|---|
| 684 | int i = 0; |
|---|
| 685 | for (i = 0; h->h_addr_list[i]; ++i) |
|---|
| 686 | { |
|---|
| 687 | if (*pat == NULL) |
|---|
| 688 | { |
|---|
| 689 | *pat = alloca (sizeof (struct gaih_addrtuple)); |
|---|
| 690 | (*pat)->scopeid = 0; |
|---|
| 691 | } |
|---|
| 692 | (*pat)->next = NULL; |
|---|
| 693 | (*pat)->family = req->ai_family; |
|---|
| 694 | if (family == req->ai_family) |
|---|
| 695 | memcpy ((*pat)->addr, h->h_addr_list[i], |
|---|
| 696 | h->h_length); |
|---|
| 697 | else |
|---|
| 698 | { |
|---|
| 699 | int32_t *addr = (uint32_t *) (*pat)->addr; |
|---|
| 700 | addr[3] = *(uint32_t *) h->h_addr_list[i]; |
|---|
| 701 | addr[2] = htonl (0xffff); |
|---|
| 702 | addr[1] = 0; |
|---|
| 703 | addr[0] = 0; |
|---|
| 704 | } |
|---|
| 705 | pat = &((*pat)->next); |
|---|
| 706 | } |
|---|
| 707 | } |
|---|
| 708 | } |
|---|
| 709 | else |
|---|
| 710 | { |
|---|
| 711 | if (herrno == NETDB_INTERNAL) |
|---|
| 712 | { |
|---|
| 713 | h_errno = (herrno); |
|---|
| 714 | return -EAI_SYSTEM; |
|---|
| 715 | } |
|---|
| 716 | if (herrno == TRY_AGAIN) |
|---|
| 717 | { |
|---|
| 718 | return -EAI_AGAIN; |
|---|
| 719 | } |
|---|
| 720 | /* We made requests but they turned out no data. |
|---|
| 721 | The name is known, though. */ |
|---|
| 722 | return (GAIH_OKIFUNSPEC | -EAI_NODATA); |
|---|
| 723 | } |
|---|
| 724 | |
|---|
| 725 | goto process_list; |
|---|
| 726 | } |
|---|
| 727 | |
|---|
| 728 | #ifdef USE_NSCD |
|---|
| 729 | if (__nss_not_use_nscd_hosts > 0 |
|---|
| 730 | && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) |
|---|
| 731 | __nss_not_use_nscd_hosts = 0; |
|---|
| 732 | |
|---|
| 733 | if (!__nss_not_use_nscd_hosts) |
|---|
| 734 | { |
|---|
| 735 | /* Try to use nscd. */ |
|---|
| 736 | struct nscd_ai_result *air = NULL; |
|---|
| 737 | int herrno; |
|---|
| 738 | int err = __nscd_getai (name, &air, &herrno); |
|---|
| 739 | if (air != NULL) |
|---|
| 740 | { |
|---|
| 741 | /* Transform into gaih_addrtuple list. */ |
|---|
| 742 | bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; |
|---|
| 743 | char *addrs = air->addrs; |
|---|
| 744 | |
|---|
| 745 | for (int i = 0; i < air->naddrs; ++i) |
|---|
| 746 | { |
|---|
| 747 | socklen_t size = (air->family[i] == AF_INET |
|---|
| 748 | ? INADDRSZ : IN6ADDRSZ); |
|---|
| 749 | if (*pat == NULL) |
|---|
| 750 | { |
|---|
| 751 | *pat = alloca (sizeof (struct gaih_addrtuple)); |
|---|
| 752 | (*pat)->scopeid = 0; |
|---|
| 753 | } |
|---|
| 754 | uint32_t *pataddr = (*pat)->addr; |
|---|
| 755 | (*pat)->next = NULL; |
|---|
| 756 | if (added_canon || air->canon == NULL) |
|---|
| 757 | (*pat)->name = NULL; |
|---|
| 758 | else { |
|---|
| 759 | canon = (*pat)->name = alloca (strlen (air->canon) + 1); |
|---|
| 760 | strcpy (canon, air->canon); |
|---|
| 761 | } |
|---|
| 762 | |
|---|
| 763 | if (air->family[i] == AF_INET |
|---|
| 764 | && req->ai_family == AF_INET6 |
|---|
| 765 | && (req->ai_flags & AI_V4MAPPED)) |
|---|
| 766 | { |
|---|
| 767 | (*pat)->family = AF_INET6; |
|---|
| 768 | pataddr[3] = *(uint32_t *) addrs; |
|---|
| 769 | pataddr[2] = htonl (0xffff); |
|---|
| 770 | pataddr[1] = 0; |
|---|
| 771 | pataddr[0] = 0; |
|---|
| 772 | pat = &((*pat)->next); |
|---|
| 773 | added_canon = true; |
|---|
| 774 | } |
|---|
| 775 | else if (req->ai_family == AF_UNSPEC |
|---|
| 776 | || air->family[i] == req->ai_family) |
|---|
| 777 | { |
|---|
| 778 | (*pat)->family = air->family[i]; |
|---|
| 779 | memcpy (pataddr, addrs, size); |
|---|
| 780 | pat = &((*pat)->next); |
|---|
| 781 | added_canon = true; |
|---|
| 782 | if (air->family[i] == AF_INET6) |
|---|
| 783 | got_ipv6 = true; |
|---|
| 784 | } |
|---|
| 785 | addrs += size; |
|---|
| 786 | } |
|---|
| 787 | |
|---|
| 788 | free (air); |
|---|
| 789 | |
|---|
| 790 | if (at->family == AF_UNSPEC) |
|---|
| 791 | return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
|---|
| 792 | |
|---|
| 793 | goto process_list; |
|---|
| 794 | } |
|---|
| 795 | else if (err != 0 && __nss_not_use_nscd_hosts == 0) |
|---|
| 796 | { |
|---|
| 797 | if (herrno == NETDB_INTERNAL && errno == ENOMEM) |
|---|
| 798 | return -EAI_MEMORY; |
|---|
| 799 | if (herrno == TRY_AGAIN) |
|---|
| 800 | return -EAI_AGAIN; |
|---|
| 801 | return -EAI_SYSTEM; |
|---|
| 802 | } |
|---|
| 803 | } |
|---|
| 804 | #endif |
|---|
| 805 | |
|---|
| 806 | if (__nss_hosts_database != NULL) |
|---|
| 807 | { |
|---|
| 808 | no_more = 0; |
|---|
| 809 | nip = __nss_hosts_database; |
|---|
| 810 | } |
|---|
| 811 | else |
|---|
| 812 | no_more = __nss_database_lookup ("hosts", NULL, |
|---|
| 813 | "dns [!UNAVAIL=return] files", |
|---|
| 814 | &nip); |
|---|
| 815 | |
|---|
| 816 | if (__res_maybe_init (&_res, 0) == -1) |
|---|
| 817 | no_more = 1; |
|---|
| 818 | |
|---|
| 819 | /* If we are looking for both IPv4 and IPv6 address we don't |
|---|
| 820 | want the lookup functions to automatically promote IPv4 |
|---|
| 821 | addresses to IPv6 addresses. Currently this is decided |
|---|
| 822 | by setting the RES_USE_INET6 bit in _res.options. */ |
|---|
| 823 | old_res_options = _res.options; |
|---|
| 824 | _res.options &= ~RES_USE_INET6; |
|---|
| 825 | |
|---|
| 826 | size_t tmpbuflen = 512; |
|---|
| 827 | char *tmpbuf = alloca (tmpbuflen); |
|---|
| 828 | |
|---|
| 829 | while (!no_more) |
|---|
| 830 | { |
|---|
| 831 | nss_gethostbyname3_r fct = NULL; |
|---|
| 832 | if (req->ai_flags & AI_CANONNAME) |
|---|
| 833 | /* No need to use this function if we do not look for |
|---|
| 834 | the canonical name. The function does not exist in |
|---|
| 835 | all NSS modules and therefore the lookup would |
|---|
| 836 | often fail. */ |
|---|
| 837 | fct = __nss_lookup_function (nip, "gethostbyname3_r"); |
|---|
| 838 | if (fct == NULL) |
|---|
| 839 | /* We are cheating here. The gethostbyname2_r function does |
|---|
| 840 | not have the same interface as gethostbyname3_r but the |
|---|
| 841 | extra arguments the latter takes are added at the end. |
|---|
| 842 | So the gethostbyname2_r code will just ignore them. */ |
|---|
| 843 | fct = __nss_lookup_function (nip, "gethostbyname2_r"); |
|---|
| 844 | |
|---|
| 845 | if (fct != NULL) |
|---|
| 846 | { |
|---|
| 847 | if (req->ai_family == AF_INET6 |
|---|
| 848 | || req->ai_family == AF_UNSPEC) |
|---|
| 849 | { |
|---|
| 850 | gethosts (AF_INET6, struct in6_addr); |
|---|
| 851 | no_inet6_data = no_data; |
|---|
| 852 | inet6_status = status; |
|---|
| 853 | } |
|---|
| 854 | if (req->ai_family == AF_INET |
|---|
| 855 | || req->ai_family == AF_UNSPEC |
|---|
| 856 | || (req->ai_family == AF_INET6 |
|---|
| 857 | && (req->ai_flags & AI_V4MAPPED) |
|---|
| 858 | /* Avoid generating the mapped addresses if we |
|---|
| 859 | know we are not going to need them. */ |
|---|
| 860 | && ((req->ai_flags & AI_ALL) || !got_ipv6))) |
|---|
| 861 | { |
|---|
| 862 | gethosts (AF_INET, struct in_addr); |
|---|
| 863 | |
|---|
| 864 | if (req->ai_family == AF_INET) |
|---|
| 865 | { |
|---|
| 866 | no_inet6_data = no_data; |
|---|
| 867 | inet6_status = status; |
|---|
| 868 | } |
|---|
| 869 | } |
|---|
| 870 | |
|---|
| 871 | /* If we found one address for AF_INET or AF_INET6, |
|---|
| 872 | don't continue the search. */ |
|---|
| 873 | if (inet6_status == NSS_STATUS_SUCCESS |
|---|
| 874 | || status == NSS_STATUS_SUCCESS) |
|---|
| 875 | { |
|---|
| 876 | if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL) |
|---|
| 877 | { |
|---|
| 878 | /* If we need the canonical name, get it |
|---|
| 879 | from the same service as the result. */ |
|---|
| 880 | nss_getcanonname_r cfct; |
|---|
| 881 | int herrno; |
|---|
| 882 | |
|---|
| 883 | cfct = __nss_lookup_function (nip, "getcanonname_r"); |
|---|
| 884 | if (cfct != NULL) |
|---|
| 885 | { |
|---|
| 886 | const size_t max_fqdn_len = 256; |
|---|
| 887 | char *buf = alloca (max_fqdn_len); |
|---|
| 888 | char *s; |
|---|
| 889 | |
|---|
| 890 | if (DL_CALL_FCT (cfct, (at->name ?: name, buf, |
|---|
| 891 | max_fqdn_len, &s, &rc, |
|---|
| 892 | &herrno)) |
|---|
| 893 | == NSS_STATUS_SUCCESS) |
|---|
| 894 | canon = s; |
|---|
| 895 | else |
|---|
| 896 | /* Set to name now to avoid using |
|---|
| 897 | gethostbyaddr. */ |
|---|
| 898 | canon = name; |
|---|
| 899 | } |
|---|
| 900 | } |
|---|
| 901 | |
|---|
| 902 | break; |
|---|
| 903 | } |
|---|
| 904 | |
|---|
| 905 | /* We can have different states for AF_INET and |
|---|
| 906 | AF_INET6. Try to find a useful one for both. */ |
|---|
| 907 | if (inet6_status == NSS_STATUS_TRYAGAIN) |
|---|
| 908 | status = NSS_STATUS_TRYAGAIN; |
|---|
| 909 | else if (status == NSS_STATUS_UNAVAIL && |
|---|
| 910 | inet6_status != NSS_STATUS_UNAVAIL) |
|---|
| 911 | status = inet6_status; |
|---|
| 912 | } |
|---|
| 913 | |
|---|
| 914 | if (nss_next_action (nip, status) == NSS_ACTION_RETURN) |
|---|
| 915 | break; |
|---|
| 916 | |
|---|
| 917 | if (nip->next == NULL) |
|---|
| 918 | no_more = -1; |
|---|
| 919 | else |
|---|
| 920 | nip = nip->next; |
|---|
| 921 | } |
|---|
| 922 | |
|---|
| 923 | _res.options = old_res_options; |
|---|
| 924 | |
|---|
| 925 | if (no_data != 0 && no_inet6_data != 0) |
|---|
| 926 | { |
|---|
| 927 | /* If both requests timed out report this. */ |
|---|
| 928 | if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) |
|---|
| 929 | return -EAI_AGAIN; |
|---|
| 930 | |
|---|
| 931 | /* We made requests but they turned out no data. The name |
|---|
| 932 | is known, though. */ |
|---|
| 933 | return (GAIH_OKIFUNSPEC | -EAI_NODATA); |
|---|
| 934 | } |
|---|
| 935 | } |
|---|
| 936 | |
|---|
| 937 | process_list: |
|---|
| 938 | if (at->family == AF_UNSPEC) |
|---|
| 939 | return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
|---|
| 940 | } |
|---|
| 941 | else |
|---|
| 942 | { |
|---|
| 943 | struct gaih_addrtuple *atr; |
|---|
| 944 | atr = at = alloca (sizeof (struct gaih_addrtuple)); |
|---|
| 945 | memset (at, '\0', sizeof (struct gaih_addrtuple)); |
|---|
| 946 | |
|---|
| 947 | if (req->ai_family == AF_UNSPEC) |
|---|
| 948 | { |
|---|
| 949 | at->next = alloca (sizeof (struct gaih_addrtuple)); |
|---|
| 950 | memset (at->next, '\0', sizeof (struct gaih_addrtuple)); |
|---|
| 951 | } |
|---|
| 952 | |
|---|
| 953 | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) |
|---|
| 954 | { |
|---|
| 955 | at->family = AF_INET6; |
|---|
| 956 | if ((req->ai_flags & AI_PASSIVE) == 0) |
|---|
| 957 | memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr)); |
|---|
| 958 | atr = at->next; |
|---|
| 959 | } |
|---|
| 960 | |
|---|
| 961 | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) |
|---|
| 962 | { |
|---|
| 963 | atr->family = AF_INET; |
|---|
| 964 | if ((req->ai_flags & AI_PASSIVE) == 0) |
|---|
| 965 | atr->addr[0] = htonl (INADDR_LOOPBACK); |
|---|
| 966 | } |
|---|
| 967 | } |
|---|
| 968 | |
|---|
| 969 | if (pai == NULL) |
|---|
| 970 | return 0; |
|---|
| 971 | |
|---|
| 972 | { |
|---|
| 973 | struct gaih_servtuple *st2; |
|---|
| 974 | struct gaih_addrtuple *at2 = at; |
|---|
| 975 | size_t socklen; |
|---|
| 976 | sa_family_t family; |
|---|
| 977 | |
|---|
| 978 | /* |
|---|
| 979 | buffer is the size of an unformatted IPv6 address in printable format. |
|---|
| 980 | */ |
|---|
| 981 | while (at2 != NULL) |
|---|
| 982 | { |
|---|
| 983 | /* Only the first entry gets the canonical name. */ |
|---|
| 984 | if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0) |
|---|
| 985 | { |
|---|
| 986 | if (canon == NULL) |
|---|
| 987 | { |
|---|
| 988 | struct hostent *h = NULL; |
|---|
| 989 | int herrno; |
|---|
| 990 | struct hostent th; |
|---|
| 991 | size_t tmpbuflen = 512; |
|---|
| 992 | char *tmpbuf = NULL; |
|---|
| 993 | |
|---|
| 994 | do |
|---|
| 995 | { |
|---|
| 996 | tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2); |
|---|
| 997 | rc = __gethostbyaddr_r (at2->addr, |
|---|
| 998 | ((at2->family == AF_INET6) |
|---|
| 999 | ? sizeof (struct in6_addr) |
|---|
| 1000 | : sizeof (struct in_addr)), |
|---|
| 1001 | at2->family, &th, tmpbuf, |
|---|
| 1002 | tmpbuflen, &h, &herrno); |
|---|
| 1003 | } |
|---|
| 1004 | while (rc == ERANGE && herrno == NETDB_INTERNAL); |
|---|
| 1005 | |
|---|
| 1006 | if (rc != 0 && herrno == NETDB_INTERNAL) |
|---|
| 1007 | { |
|---|
| 1008 | h_errno = (herrno); |
|---|
| 1009 | return -EAI_SYSTEM; |
|---|
| 1010 | } |
|---|
| 1011 | |
|---|
| 1012 | if (h != NULL) |
|---|
| 1013 | canon = h->h_name; |
|---|
| 1014 | else |
|---|
| 1015 | { |
|---|
| 1016 | assert (orig_name != NULL); |
|---|
| 1017 | /* If the canonical name cannot be determined, use |
|---|
| 1018 | the passed in string. */ |
|---|
| 1019 | canon = orig_name; |
|---|
| 1020 | } |
|---|
| 1021 | } |
|---|
| 1022 | |
|---|
| 1023 | #ifdef HAVE_LIBIDN |
|---|
| 1024 | if (req->ai_flags & AI_CANONIDN) |
|---|
| 1025 | { |
|---|
| 1026 | int idn_flags = 0; |
|---|
| 1027 | if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED) |
|---|
| 1028 | idn_flags |= IDNA_ALLOW_UNASSIGNED; |
|---|
| 1029 | if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES) |
|---|
| 1030 | idn_flags |= IDNA_USE_STD3_ASCII_RULES; |
|---|
| 1031 | |
|---|
| 1032 | char *out; |
|---|
| 1033 | int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags); |
|---|
| 1034 | if (rc != IDNA_SUCCESS) |
|---|
| 1035 | { |
|---|
| 1036 | if (rc == IDNA_MALLOC_ERROR) |
|---|
| 1037 | return -EAI_MEMORY; |
|---|
| 1038 | if (rc == IDNA_DLOPEN_ERROR) |
|---|
| 1039 | return -EAI_SYSTEM; |
|---|
| 1040 | return -EAI_IDN_ENCODE; |
|---|
| 1041 | } |
|---|
| 1042 | /* In case the output string is the same as the input |
|---|
| 1043 | string no new string has been allocated. Otherwise |
|---|
| 1044 | make a copy. */ |
|---|
| 1045 | if (out == canon) |
|---|
| 1046 | goto make_copy; |
|---|
| 1047 | } |
|---|
| 1048 | else |
|---|
| 1049 | #endif |
|---|
| 1050 | { |
|---|
| 1051 | #ifdef HAVE_LIBIDN |
|---|
| 1052 | make_copy: |
|---|
| 1053 | #endif |
|---|
| 1054 | canon = strdup (canon); |
|---|
| 1055 | if (canon == NULL) |
|---|
| 1056 | return -EAI_MEMORY; |
|---|
| 1057 | } |
|---|
| 1058 | } |
|---|
| 1059 | |
|---|
| 1060 | if (at2->family == AF_INET6) |
|---|
| 1061 | { |
|---|
| 1062 | family = AF_INET6; |
|---|
| 1063 | socklen = sizeof (struct sockaddr_in6); |
|---|
| 1064 | |
|---|
| 1065 | /* If we looked up IPv4 mapped address discard them here if |
|---|
| 1066 | the caller isn't interested in all address and we have |
|---|
| 1067 | found at least one IPv6 address. */ |
|---|
| 1068 | if (got_ipv6 |
|---|
| 1069 | && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED |
|---|
| 1070 | && IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)at2->addr)) |
|---|
| 1071 | goto ignore; |
|---|
| 1072 | } |
|---|
| 1073 | else |
|---|
| 1074 | { |
|---|
| 1075 | family = AF_INET; |
|---|
| 1076 | socklen = sizeof (struct sockaddr_in); |
|---|
| 1077 | } |
|---|
| 1078 | |
|---|
| 1079 | for (st2 = st; st2 != NULL; st2 = st2->next) |
|---|
| 1080 | { |
|---|
| 1081 | struct addrinfo *ai; |
|---|
| 1082 | ai = *pai = malloc (sizeof (struct addrinfo) + socklen); |
|---|
| 1083 | if (ai == NULL) |
|---|
| 1084 | return -EAI_MEMORY; |
|---|
| 1085 | |
|---|
| 1086 | ai->ai_flags = req->ai_flags; |
|---|
| 1087 | ai->ai_family = family; |
|---|
| 1088 | ai->ai_socktype = st2->socktype; |
|---|
| 1089 | ai->ai_protocol = st2->protocol; |
|---|
| 1090 | ai->ai_addrlen = socklen; |
|---|
| 1091 | ai->ai_addr = (void *) (ai + 1); |
|---|
| 1092 | |
|---|
| 1093 | /* We only add the canonical name once. */ |
|---|
| 1094 | ai->ai_canonname = (char *) canon; |
|---|
| 1095 | canon = NULL; |
|---|
| 1096 | |
|---|
| 1097 | #if SALEN |
|---|
| 1098 | ai->ai_addr->sa_len = socklen; |
|---|
| 1099 | #endif /* SALEN */ |
|---|
| 1100 | ai->ai_addr->sa_family = family; |
|---|
| 1101 | |
|---|
| 1102 | if (family == AF_INET6) |
|---|
| 1103 | { |
|---|
| 1104 | struct sockaddr_in6 *sin6p = |
|---|
| 1105 | (struct sockaddr_in6 *) ai->ai_addr; |
|---|
| 1106 | |
|---|
| 1107 | sin6p->sin6_port = st2->port; |
|---|
| 1108 | sin6p->sin6_flowinfo = 0; |
|---|
| 1109 | memcpy (&sin6p->sin6_addr, |
|---|
| 1110 | at2->addr, sizeof (struct in6_addr)); |
|---|
| 1111 | sin6p->sin6_scope_id = at2->scopeid; |
|---|
| 1112 | } |
|---|
| 1113 | else |
|---|
| 1114 | { |
|---|
| 1115 | struct sockaddr_in *sinp = |
|---|
| 1116 | (struct sockaddr_in *) ai->ai_addr; |
|---|
| 1117 | sinp->sin_port = st2->port; |
|---|
| 1118 | memcpy (&sinp->sin_addr, |
|---|
| 1119 | at2->addr, sizeof (struct in_addr)); |
|---|
| 1120 | memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); |
|---|
| 1121 | } |
|---|
| 1122 | |
|---|
| 1123 | pai = &(ai->ai_next); |
|---|
| 1124 | } |
|---|
| 1125 | *pai = NULL; |
|---|
| 1126 | |
|---|
| 1127 | ignore: |
|---|
| 1128 | at2 = at2->next; |
|---|
| 1129 | } |
|---|
| 1130 | } |
|---|
| 1131 | return 0; |
|---|
| 1132 | } |
|---|
| 1133 | |
|---|
| 1134 | static struct gaih gaih[] = |
|---|
| 1135 | { |
|---|
| 1136 | { PF_INET6, gaih_inet }, |
|---|
| 1137 | { PF_INET, gaih_inet }, |
|---|
| 1138 | #if 0 |
|---|
| 1139 | { PF_LOCAL, gaih_local }, |
|---|
| 1140 | #endif |
|---|
| 1141 | { PF_UNSPEC, NULL } |
|---|
| 1142 | }; |
|---|
| 1143 | |
|---|
| 1144 | struct sort_result |
|---|
| 1145 | { |
|---|
| 1146 | struct addrinfo *dest_addr; |
|---|
| 1147 | struct sockaddr_storage source_addr; |
|---|
| 1148 | uint8_t source_addr_len; |
|---|
| 1149 | bool got_source_addr; |
|---|
| 1150 | }; |
|---|
| 1151 | |
|---|
| 1152 | |
|---|
| 1153 | static int |
|---|
| 1154 | get_scope (const struct sockaddr_storage *ss) |
|---|
| 1155 | { |
|---|
| 1156 | int scope; |
|---|
| 1157 | if (ss->ss_family == PF_INET6) |
|---|
| 1158 | { |
|---|
| 1159 | const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss; |
|---|
| 1160 | |
|---|
| 1161 | if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr)) |
|---|
| 1162 | { |
|---|
| 1163 | if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr)) |
|---|
| 1164 | scope = 2; |
|---|
| 1165 | else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr)) |
|---|
| 1166 | scope = 5; |
|---|
| 1167 | else |
|---|
| 1168 | /* XXX Is this the correct default behavior? */ |
|---|
| 1169 | scope = 14; |
|---|
| 1170 | } |
|---|
| 1171 | else |
|---|
| 1172 | scope = in6->sin6_addr.s6_addr[1] & 0xf; |
|---|
| 1173 | } |
|---|
| 1174 | else if (ss->ss_family == PF_INET) |
|---|
| 1175 | { |
|---|
| 1176 | const struct sockaddr_in *in = (const struct sockaddr_in *) ss; |
|---|
| 1177 | const uint8_t *addr = (const uint8_t *) &in->sin_addr; |
|---|
| 1178 | |
|---|
| 1179 | /* RFC 3484 specifies how to map IPv6 addresses to scopes. |
|---|
| 1180 | 169.254/16 and 127/8 are link-local. */ |
|---|
| 1181 | if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127) |
|---|
| 1182 | scope = 2; |
|---|
| 1183 | else if (addr[0] == 10 || (addr[0] == 172 && addr[1] == 16) |
|---|
| 1184 | || (addr[0] == 192 && addr[1] == 168)) |
|---|
| 1185 | scope = 5; |
|---|
| 1186 | else |
|---|
| 1187 | scope = 14; |
|---|
| 1188 | } |
|---|
| 1189 | else |
|---|
| 1190 | /* XXX What is a good default? */ |
|---|
| 1191 | scope = 15; |
|---|
| 1192 | |
|---|
| 1193 | return scope; |
|---|
| 1194 | } |
|---|
| 1195 | |
|---|
| 1196 | |
|---|
| 1197 | /* XXX The system administrator should be able to install other |
|---|
| 1198 | tables. We need to make this configurable. The problem is that |
|---|
| 1199 | the kernel is also involved since it needs the same table. */ |
|---|
| 1200 | static const struct prefixlist |
|---|
| 1201 | { |
|---|
| 1202 | struct in6_addr prefix; |
|---|
| 1203 | unsigned int bits; |
|---|
| 1204 | int val; |
|---|
| 1205 | } default_labels[] = |
|---|
| 1206 | { |
|---|
| 1207 | /* See RFC 3484 for the details. */ |
|---|
| 1208 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1209 | 0x0000, 0x0000, 0x0000, 0x0001 } } }, |
|---|
| 1210 | 128, 0 }, |
|---|
| 1211 | { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000, |
|---|
| 1212 | 0x0000, 0x0000, 0x0000, 0x0000 } } }, |
|---|
| 1213 | 16, 2 }, |
|---|
| 1214 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1215 | 0x0000, 0x0000, 0x0000, 0x0000 } } }, |
|---|
| 1216 | 96, 3 }, |
|---|
| 1217 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1218 | 0x0000, 0xffff, 0x0000, 0x0000 } } }, |
|---|
| 1219 | 96, 4 }, |
|---|
| 1220 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1221 | 0x0000, 0x0000, 0x0000, 0x0000 } } }, |
|---|
| 1222 | 0, 1 } |
|---|
| 1223 | }; |
|---|
| 1224 | |
|---|
| 1225 | |
|---|
| 1226 | static const struct prefixlist default_precedence[] = |
|---|
| 1227 | { |
|---|
| 1228 | /* See RFC 3484 for the details. */ |
|---|
| 1229 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1230 | 0x0000, 0x0000, 0x0000, 0x0001 } } }, |
|---|
| 1231 | 128, 50 }, |
|---|
| 1232 | { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000, |
|---|
| 1233 | 0x0000, 0x0000, 0x0000, 0x0000 } } }, |
|---|
| 1234 | 16, 30 }, |
|---|
| 1235 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1236 | 0x0000, 0x0000, 0x0000, 0x0000 } } }, |
|---|
| 1237 | 96, 20 }, |
|---|
| 1238 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1239 | 0x0000, 0xffff, 0x0000, 0x0000 } } }, |
|---|
| 1240 | 96, 10 }, |
|---|
| 1241 | { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000, |
|---|
| 1242 | 0x0000, 0x0000, 0x0000, 0x0000 } } }, |
|---|
| 1243 | 0, 40 } |
|---|
| 1244 | }; |
|---|
| 1245 | |
|---|
| 1246 | |
|---|
| 1247 | static int |
|---|
| 1248 | match_prefix (const struct sockaddr_storage *ss, const struct prefixlist *list, |
|---|
| 1249 | int default_val) |
|---|
| 1250 | { |
|---|
| 1251 | int idx; |
|---|
| 1252 | struct sockaddr_in6 in6_mem; |
|---|
| 1253 | const struct sockaddr_in6 *in6; |
|---|
| 1254 | |
|---|
| 1255 | if (ss->ss_family == PF_INET6) |
|---|
| 1256 | in6 = (const struct sockaddr_in6 *) ss; |
|---|
| 1257 | else if (ss->ss_family == PF_INET) |
|---|
| 1258 | { |
|---|
| 1259 | const struct sockaddr_in *in = (const struct sockaddr_in *) ss; |
|---|
| 1260 | |
|---|
| 1261 | /* Convert to IPv6 address. */ |
|---|
| 1262 | in6_mem.sin6_family = PF_INET6; |
|---|
| 1263 | in6_mem.sin6_port = in->sin_port; |
|---|
| 1264 | in6_mem.sin6_flowinfo = 0; |
|---|
| 1265 | if (in->sin_addr.s_addr == htonl (0x7f000001)) |
|---|
| 1266 | in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; |
|---|
| 1267 | else |
|---|
| 1268 | { |
|---|
| 1269 | /* Construct a V4-to-6 mapped address. */ |
|---|
| 1270 | memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr)); |
|---|
| 1271 | in6_mem.sin6_addr.__u6_addr.__u6_addr16[5] = 0xffff; |
|---|
| 1272 | in6_mem.sin6_addr.__u6_addr.__u6_addr32[3] = in->sin_addr.s_addr; |
|---|
| 1273 | in6_mem.sin6_scope_id = 0; |
|---|
| 1274 | } |
|---|
| 1275 | |
|---|
| 1276 | in6 = &in6_mem; |
|---|
| 1277 | } |
|---|
| 1278 | else |
|---|
| 1279 | return default_val; |
|---|
| 1280 | |
|---|
| 1281 | for (idx = 0; ; ++idx) |
|---|
| 1282 | { |
|---|
| 1283 | unsigned int bits = list[idx].bits; |
|---|
| 1284 | uint8_t *mask = list[idx].prefix.s6_addr; |
|---|
| 1285 | uint8_t *val = in6->sin6_addr.s6_addr; |
|---|
| 1286 | |
|---|
| 1287 | while (bits > 8) |
|---|
| 1288 | { |
|---|
| 1289 | if (*mask != *val) |
|---|
| 1290 | break; |
|---|
| 1291 | |
|---|
| 1292 | ++mask; |
|---|
| 1293 | ++val; |
|---|
| 1294 | bits -= 8; |
|---|
| 1295 | } |
|---|
| 1296 | |
|---|
| 1297 | if (bits < 8) |
|---|
| 1298 | { |
|---|
| 1299 | if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits))) |
|---|
| 1300 | /* Match! */ |
|---|
| 1301 | break; |
|---|
| 1302 | } |
|---|
| 1303 | } |
|---|
| 1304 | |
|---|
| 1305 | return list[idx].val; |
|---|
| 1306 | } |
|---|
| 1307 | |
|---|
| 1308 | |
|---|
| 1309 | static int |
|---|
| 1310 | get_label (const struct sockaddr_storage *ss) |
|---|
| 1311 | { |
|---|
| 1312 | /* XXX What is a good default value? */ |
|---|
| 1313 | return match_prefix (ss, default_labels, INT_MAX); |
|---|
| 1314 | } |
|---|
| 1315 | |
|---|
| 1316 | |
|---|
| 1317 | static int |
|---|
| 1318 | get_precedence (const struct sockaddr_storage *ss) |
|---|
| 1319 | { |
|---|
| 1320 | /* XXX What is a good default value? */ |
|---|
| 1321 | return match_prefix (ss, default_precedence, 0); |
|---|
| 1322 | } |
|---|
| 1323 | |
|---|
| 1324 | |
|---|
| 1325 | static int |
|---|
| 1326 | rfc3484_sort (const void *p1, const void *p2) |
|---|
| 1327 | { |
|---|
| 1328 | const struct sort_result *a1 = (const struct sort_result *) p1; |
|---|
| 1329 | const struct sort_result *a2 = (const struct sort_result *) p2; |
|---|
| 1330 | |
|---|
| 1331 | /* Rule 1: Avoid unusable destinations. |
|---|
| 1332 | We have the got_source_addr flag set if the destination is reachable. */ |
|---|
| 1333 | if (a1->got_source_addr && ! a2->got_source_addr) |
|---|
| 1334 | return -1; |
|---|
| 1335 | if (! a1->got_source_addr && a2->got_source_addr) |
|---|
| 1336 | return 1; |
|---|
| 1337 | |
|---|
| 1338 | |
|---|
| 1339 | /* Rule 2: Prefer matching scope. Only interesting if both |
|---|
| 1340 | destination addresses are IPv6. */ |
|---|
| 1341 | int a1_dst_scope |
|---|
| 1342 | = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr); |
|---|
| 1343 | |
|---|
| 1344 | int a2_dst_scope |
|---|
| 1345 | = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr); |
|---|
| 1346 | |
|---|
| 1347 | if (a1->got_source_addr) |
|---|
| 1348 | { |
|---|
| 1349 | int a1_src_scope = get_scope (&a1->source_addr); |
|---|
| 1350 | int a2_src_scope = get_scope (&a2->source_addr); |
|---|
| 1351 | |
|---|
| 1352 | if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope) |
|---|
| 1353 | return -1; |
|---|
| 1354 | if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope) |
|---|
| 1355 | return 1; |
|---|
| 1356 | } |
|---|
| 1357 | |
|---|
| 1358 | |
|---|
| 1359 | /* Rule 3: Avoid deprecated addresses. |
|---|
| 1360 | That's something only the kernel could decide. */ |
|---|
| 1361 | |
|---|
| 1362 | /* Rule 4: Prefer home addresses. |
|---|
| 1363 | Another thing only the kernel can decide. */ |
|---|
| 1364 | |
|---|
| 1365 | /* Rule 5: Prefer matching label. */ |
|---|
| 1366 | if (a1->got_source_addr) |
|---|
| 1367 | { |
|---|
| 1368 | int a1_dst_label |
|---|
| 1369 | = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr); |
|---|
| 1370 | int a1_src_label = get_label (&a1->source_addr); |
|---|
| 1371 | |
|---|
| 1372 | int a2_dst_label |
|---|
| 1373 | = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr); |
|---|
| 1374 | int a2_src_label = get_label (&a2->source_addr); |
|---|
| 1375 | |
|---|
| 1376 | if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label) |
|---|
| 1377 | return -1; |
|---|
| 1378 | if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label) |
|---|
| 1379 | return 1; |
|---|
| 1380 | } |
|---|
| 1381 | |
|---|
| 1382 | |
|---|
| 1383 | /* Rule 6: Prefer higher precedence. */ |
|---|
| 1384 | int a1_prec |
|---|
| 1385 | = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr); |
|---|
| 1386 | int a2_prec |
|---|
| 1387 | = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr); |
|---|
| 1388 | |
|---|
| 1389 | if (a1_prec > a2_prec) |
|---|
| 1390 | return -1; |
|---|
| 1391 | if (a1_prec < a2_prec) |
|---|
| 1392 | return 1; |
|---|
| 1393 | |
|---|
| 1394 | |
|---|
| 1395 | /* Rule 7: Prefer native transport. |
|---|
| 1396 | XXX How to recognize tunnels? */ |
|---|
| 1397 | |
|---|
| 1398 | |
|---|
| 1399 | /* Rule 8: Prefer smaller scope. */ |
|---|
| 1400 | if (a1_dst_scope < a2_dst_scope) |
|---|
| 1401 | return -1; |
|---|
| 1402 | if (a1_dst_scope > a2_dst_scope) |
|---|
| 1403 | return 1; |
|---|
| 1404 | |
|---|
| 1405 | |
|---|
| 1406 | /* Rule 9: Use longest matching prefix. */ |
|---|
| 1407 | if (a1->got_source_addr |
|---|
| 1408 | && a1->dest_addr->ai_family == a2->dest_addr->ai_family) |
|---|
| 1409 | { |
|---|
| 1410 | int bit1 = 0; |
|---|
| 1411 | int bit2 = 0; |
|---|
| 1412 | |
|---|
| 1413 | if (a1->dest_addr->ai_family == PF_INET) |
|---|
| 1414 | { |
|---|
| 1415 | assert (a1->source_addr.ss_family == PF_INET); |
|---|
| 1416 | assert (a2->source_addr.ss_family == PF_INET); |
|---|
| 1417 | |
|---|
| 1418 | struct sockaddr_in *in1_dst; |
|---|
| 1419 | struct sockaddr_in *in1_src; |
|---|
| 1420 | struct sockaddr_in *in2_dst; |
|---|
| 1421 | struct sockaddr_in *in2_src; |
|---|
| 1422 | |
|---|
| 1423 | in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr; |
|---|
| 1424 | in1_src = (struct sockaddr_in *) &a1->source_addr; |
|---|
| 1425 | in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr; |
|---|
| 1426 | in2_src = (struct sockaddr_in *) &a2->source_addr; |
|---|
| 1427 | |
|---|
| 1428 | bit1 = ffs (in1_dst->sin_addr.s_addr ^ in1_src->sin_addr.s_addr); |
|---|
| 1429 | bit2 = ffs (in2_dst->sin_addr.s_addr ^ in2_src->sin_addr.s_addr); |
|---|
| 1430 | } |
|---|
| 1431 | else if (a1->dest_addr->ai_family == PF_INET6) |
|---|
| 1432 | { |
|---|
| 1433 | assert (a1->source_addr.ss_family == PF_INET6); |
|---|
| 1434 | assert (a2->source_addr.ss_family == PF_INET6); |
|---|
| 1435 | |
|---|
| 1436 | struct sockaddr_in6 *in1_dst; |
|---|
| 1437 | struct sockaddr_in6 *in1_src; |
|---|
| 1438 | struct sockaddr_in6 *in2_dst; |
|---|
| 1439 | struct sockaddr_in6 *in2_src; |
|---|
| 1440 | |
|---|
| 1441 | in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr; |
|---|
| 1442 | in1_src = (struct sockaddr_in6 *) &a1->source_addr; |
|---|
| 1443 | in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr; |
|---|
| 1444 | in2_src = (struct sockaddr_in6 *) &a2->source_addr; |
|---|
| 1445 | |
|---|
| 1446 | int i; |
|---|
| 1447 | for (i = 0; i < 4; ++i) |
|---|
| 1448 | if (in1_dst->sin6_addr.s6_addr32[i] |
|---|
| 1449 | != in1_src->sin6_addr.s6_addr32[i] |
|---|
| 1450 | || (in2_dst->sin6_addr.s6_addr32[i] |
|---|
| 1451 | != in2_src->sin6_addr.s6_addr32[i])) |
|---|
| 1452 | break; |
|---|
| 1453 | |
|---|
| 1454 | if (i < 4) |
|---|
| 1455 | { |
|---|
| 1456 | bit1 = ffs (in1_dst->sin6_addr.s6_addr32[i] |
|---|
| 1457 | ^ in1_src->sin6_addr.s6_addr32[i]); |
|---|
| 1458 | bit2 = ffs (in2_dst->sin6_addr.s6_addr32[i] |
|---|
| 1459 | ^ in2_src->sin6_addr.s6_addr32[i]); |
|---|
| 1460 | } |
|---|
| 1461 | } |
|---|
| 1462 | |
|---|
| 1463 | if (bit1 > bit2) |
|---|
| 1464 | return -1; |
|---|
| 1465 | if (bit1 < bit2) |
|---|
| 1466 | return 1; |
|---|
| 1467 | } |
|---|
| 1468 | |
|---|
| 1469 | |
|---|
| 1470 | /* Rule 10: Otherwise, leave the order unchanged. */ |
|---|
| 1471 | return 0; |
|---|
| 1472 | } |
|---|
| 1473 | |
|---|
| 1474 | |
|---|
| 1475 | int |
|---|
| 1476 | getaddrinfo (const char *__restrict name, const char *__restrict service, |
|---|
| 1477 | const struct addrinfo *__restrict hints, |
|---|
| 1478 | struct addrinfo **__restrict pai) |
|---|
| 1479 | { |
|---|
| 1480 | int i = 0, j = 0, last_i = 0; |
|---|
| 1481 | int nresults = 0; |
|---|
| 1482 | struct addrinfo *p = NULL, **end; |
|---|
| 1483 | struct gaih *g = gaih, *pg = NULL; |
|---|
| 1484 | struct gaih_service gaih_service, *pservice; |
|---|
| 1485 | struct addrinfo local_hints; |
|---|
| 1486 | |
|---|
| 1487 | if (name != NULL && name[0] == '*' && name[1] == 0) |
|---|
| 1488 | name = NULL; |
|---|
| 1489 | |
|---|
| 1490 | if (service != NULL && service[0] == '*' && service[1] == 0) |
|---|
| 1491 | service = NULL; |
|---|
| 1492 | |
|---|
| 1493 | if (name == NULL && service == NULL) |
|---|
| 1494 | return EAI_NONAME; |
|---|
| 1495 | |
|---|
| 1496 | if (hints == NULL) |
|---|
| 1497 | hints = &default_hints; |
|---|
| 1498 | |
|---|
| 1499 | if (hints->ai_flags |
|---|
| 1500 | & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED |
|---|
| 1501 | #ifdef HAVE_LIBIDN |
|---|
| 1502 | |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED |
|---|
| 1503 | |AI_IDN_USE_STD3_ASCII_RULES |
|---|
| 1504 | #endif |
|---|
| 1505 | |AI_NUMERICSERV|AI_ALL)) |
|---|
| 1506 | return EAI_BADFLAGS; |
|---|
| 1507 | |
|---|
| 1508 | if ((hints->ai_flags & AI_CANONNAME) && name == NULL) |
|---|
| 1509 | return EAI_BADFLAGS; |
|---|
| 1510 | |
|---|
| 1511 | if (hints->ai_flags & AI_ADDRCONFIG) |
|---|
| 1512 | { |
|---|
| 1513 | /* Determine whether we have IPv4 or IPv6 interfaces or both. |
|---|
| 1514 | We cannot cache the results since new interfaces could be |
|---|
| 1515 | added at any time. */ |
|---|
| 1516 | bool seen_ipv4; |
|---|
| 1517 | bool seen_ipv6; |
|---|
| 1518 | __check_pf (&seen_ipv4, &seen_ipv6); |
|---|
| 1519 | |
|---|
| 1520 | /* Now make a decision on what we return, if anything. */ |
|---|
| 1521 | if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6)) |
|---|
| 1522 | { |
|---|
| 1523 | /* If we haven't seen both IPv4 and IPv6 interfaces we can |
|---|
| 1524 | narrow down the search. */ |
|---|
| 1525 | if (! seen_ipv4 || ! seen_ipv6) |
|---|
| 1526 | { |
|---|
| 1527 | local_hints = *hints; |
|---|
| 1528 | local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6; |
|---|
| 1529 | hints = &local_hints; |
|---|
| 1530 | } |
|---|
| 1531 | } |
|---|
| 1532 | else if ((hints->ai_family == PF_INET && ! seen_ipv4) |
|---|
| 1533 | || (hints->ai_family == PF_INET6 && ! seen_ipv6)) |
|---|
| 1534 | /* We cannot possibly return a valid answer. */ |
|---|
| 1535 | return EAI_NONAME; |
|---|
| 1536 | } |
|---|
| 1537 | |
|---|
| 1538 | if (service && service[0]) |
|---|
| 1539 | { |
|---|
| 1540 | char *c; |
|---|
| 1541 | gaih_service.name = service; |
|---|
| 1542 | gaih_service.num = strtoul (gaih_service.name, &c, 10); |
|---|
| 1543 | if (*c != '\0') |
|---|
| 1544 | { |
|---|
| 1545 | if (hints->ai_flags & AI_NUMERICSERV) |
|---|
| 1546 | return EAI_NONAME; |
|---|
| 1547 | |
|---|
| 1548 | gaih_service.num = -1; |
|---|
| 1549 | } |
|---|
| 1550 | |
|---|
| 1551 | pservice = &gaih_service; |
|---|
| 1552 | } |
|---|
| 1553 | else |
|---|
| 1554 | pservice = NULL; |
|---|
| 1555 | |
|---|
| 1556 | if (pai) |
|---|
| 1557 | end = &p; |
|---|
| 1558 | else |
|---|
| 1559 | end = NULL; |
|---|
| 1560 | |
|---|
| 1561 | while (g->gaih) |
|---|
| 1562 | { |
|---|
| 1563 | if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) |
|---|
| 1564 | { |
|---|
| 1565 | j++; |
|---|
| 1566 | if (pg == NULL || pg->gaih != g->gaih) |
|---|
| 1567 | { |
|---|
| 1568 | pg = g; |
|---|
| 1569 | i = g->gaih (name, pservice, hints, end); |
|---|
| 1570 | if (i != 0) |
|---|
| 1571 | { |
|---|
| 1572 | /* EAI_NODATA is a more specific result as it says that |
|---|
| 1573 | we found a result but it is not usable. */ |
|---|
| 1574 | if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA)) |
|---|
| 1575 | last_i = i; |
|---|
| 1576 | |
|---|
| 1577 | if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC)) |
|---|
| 1578 | { |
|---|
| 1579 | ++g; |
|---|
| 1580 | continue; |
|---|
| 1581 | } |
|---|
| 1582 | |
|---|
| 1583 | freeaddrinfo (p); |
|---|
| 1584 | |
|---|
| 1585 | return -(i & GAIH_EAI); |
|---|
| 1586 | } |
|---|
| 1587 | if (end) |
|---|
| 1588 | while (*end) |
|---|
| 1589 | { |
|---|
| 1590 | end = &((*end)->ai_next); |
|---|
| 1591 | ++nresults; |
|---|
| 1592 | } |
|---|
| 1593 | } |
|---|
| 1594 | } |
|---|
| 1595 | ++g; |
|---|
| 1596 | } |
|---|
| 1597 | |
|---|
| 1598 | if (j == 0) |
|---|
| 1599 | return EAI_FAMILY; |
|---|
| 1600 | |
|---|
| 1601 | if (nresults > 1) |
|---|
| 1602 | { |
|---|
| 1603 | /* Sort results according to RFC 3484. */ |
|---|
| 1604 | struct sort_result results[nresults]; |
|---|
| 1605 | struct addrinfo *q; |
|---|
| 1606 | struct addrinfo *last = NULL; |
|---|
| 1607 | char *canonname = NULL; |
|---|
| 1608 | |
|---|
| 1609 | for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next) |
|---|
| 1610 | { |
|---|
| 1611 | results[i].dest_addr = q; |
|---|
| 1612 | results[i].got_source_addr = false; |
|---|
| 1613 | |
|---|
| 1614 | /* If we just looked up the address for a different |
|---|
| 1615 | protocol, reuse the result. */ |
|---|
| 1616 | if (last != NULL && last->ai_addrlen == q->ai_addrlen |
|---|
| 1617 | && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0) |
|---|
| 1618 | { |
|---|
| 1619 | memcpy (&results[i].source_addr, &results[i - 1].source_addr, |
|---|
| 1620 | results[i - 1].source_addr_len); |
|---|
| 1621 | results[i].source_addr_len = results[i - 1].source_addr_len; |
|---|
| 1622 | results[i].got_source_addr = results[i - 1].got_source_addr; |
|---|
| 1623 | } |
|---|
| 1624 | else |
|---|
| 1625 | { |
|---|
| 1626 | /* We overwrite the type with SOCK_DGRAM since we do not |
|---|
| 1627 | want connect() to connect to the other side. If we |
|---|
| 1628 | cannot determine the source address remember this |
|---|
| 1629 | fact. */ |
|---|
| 1630 | int fd = socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP); |
|---|
| 1631 | socklen_t sl = sizeof (results[i].source_addr); |
|---|
| 1632 | if (fd != -1 |
|---|
| 1633 | && connect (fd, q->ai_addr, q->ai_addrlen) == 0 |
|---|
| 1634 | && getsockname (fd, |
|---|
| 1635 | (struct sockaddr *) &results[i].source_addr, |
|---|
| 1636 | &sl) == 0) |
|---|
| 1637 | { |
|---|
| 1638 | results[i].source_addr_len = sl; |
|---|
| 1639 | results[i].got_source_addr = true; |
|---|
| 1640 | } |
|---|
| 1641 | else |
|---|
| 1642 | /* Just make sure that if we have to process the same |
|---|
| 1643 | address again we do not copy any memory. */ |
|---|
| 1644 | results[i].source_addr_len = 0; |
|---|
| 1645 | |
|---|
| 1646 | if (fd != -1) |
|---|
| 1647 | close_not_cancel_no_status (fd); |
|---|
| 1648 | } |
|---|
| 1649 | |
|---|
| 1650 | /* Remember the canonical name. */ |
|---|
| 1651 | if (q->ai_canonname != NULL) |
|---|
| 1652 | { |
|---|
| 1653 | assert (canonname == NULL); |
|---|
| 1654 | canonname = q->ai_canonname; |
|---|
| 1655 | q->ai_canonname = NULL; |
|---|
| 1656 | } |
|---|
| 1657 | } |
|---|
| 1658 | |
|---|
| 1659 | /* We got all the source addresses we can get, now sort using |
|---|
| 1660 | the information. */ |
|---|
| 1661 | qsort (results, nresults, sizeof (results[0]), rfc3484_sort); |
|---|
| 1662 | |
|---|
| 1663 | /* Queue the results up as they come out of sorting. */ |
|---|
| 1664 | q = p = results[0].dest_addr; |
|---|
| 1665 | for (i = 1; i < nresults; ++i) |
|---|
| 1666 | q = q->ai_next = results[i].dest_addr; |
|---|
| 1667 | q->ai_next = NULL; |
|---|
| 1668 | |
|---|
| 1669 | /* Fill in the canonical name into the new first entry. */ |
|---|
| 1670 | p->ai_canonname = canonname; |
|---|
| 1671 | } |
|---|
| 1672 | |
|---|
| 1673 | if (p) |
|---|
| 1674 | { |
|---|
| 1675 | *pai = p; |
|---|
| 1676 | return 0; |
|---|
| 1677 | } |
|---|
| 1678 | |
|---|
| 1679 | if (pai == NULL && last_i == 0) |
|---|
| 1680 | return 0; |
|---|
| 1681 | |
|---|
| 1682 | return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME; |
|---|
| 1683 | } |
|---|
| 1684 | libc_hidden_def (getaddrinfo) |
|---|
| 1685 | |
|---|
| 1686 | static_link_warning (getaddrinfo) |
|---|
| 1687 | |
|---|
| 1688 | void |
|---|
| 1689 | freeaddrinfo (struct addrinfo *ai) |
|---|
| 1690 | { |
|---|
| 1691 | struct addrinfo *p; |
|---|
| 1692 | |
|---|
| 1693 | while (ai != NULL) |
|---|
| 1694 | { |
|---|
| 1695 | p = ai; |
|---|
| 1696 | ai = ai->ai_next; |
|---|
| 1697 | free (p->ai_canonname); |
|---|
| 1698 | free (p); |
|---|
| 1699 | } |
|---|
| 1700 | } |
|---|
| 1701 | libc_hidden_def (freeaddrinfo) |
|---|