Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* $NetBSD: getaddrinfo.c,v 1.82 2006/03/25 12:09:40 rpaulo Exp $ */ |
michael@0 | 2 | /* $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $ */ |
michael@0 | 3 | |
michael@0 | 4 | /* |
michael@0 | 5 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
michael@0 | 6 | * All rights reserved. |
michael@0 | 7 | * |
michael@0 | 8 | * Redistribution and use in source and binary forms, with or without |
michael@0 | 9 | * modification, are permitted provided that the following conditions |
michael@0 | 10 | * are met: |
michael@0 | 11 | * 1. Redistributions of source code must retain the above copyright |
michael@0 | 12 | * notice, this list of conditions and the following disclaimer. |
michael@0 | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
michael@0 | 14 | * notice, this list of conditions and the following disclaimer in the |
michael@0 | 15 | * documentation and/or other materials provided with the distribution. |
michael@0 | 16 | * 3. Neither the name of the project nor the names of its contributors |
michael@0 | 17 | * may be used to endorse or promote products derived from this software |
michael@0 | 18 | * without specific prior written permission. |
michael@0 | 19 | * |
michael@0 | 20 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
michael@0 | 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
michael@0 | 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
michael@0 | 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
michael@0 | 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
michael@0 | 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
michael@0 | 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
michael@0 | 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
michael@0 | 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
michael@0 | 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
michael@0 | 30 | * SUCH DAMAGE. |
michael@0 | 31 | */ |
michael@0 | 32 | |
michael@0 | 33 | /* |
michael@0 | 34 | * This version of getaddrinfo.c is derived from Android 2.3 "Gingerbread", |
michael@0 | 35 | * which contains uncredited changes by Android/Google developers. It has |
michael@0 | 36 | * been modified in 2011 for use in the Android build of Mozilla Firefox by |
michael@0 | 37 | * Mozilla contributors (including Michael Edwards <m.k.edwards@gmail.com>, |
michael@0 | 38 | * and Steve Workman <sjhworkman@gmail.com>). |
michael@0 | 39 | * These changes are offered under the same license as the original NetBSD |
michael@0 | 40 | * file, whose copyright and license are unchanged above. |
michael@0 | 41 | */ |
michael@0 | 42 | #define ANDROID_CHANGES 1 |
michael@0 | 43 | #define INET6 1 |
michael@0 | 44 | |
michael@0 | 45 | /* |
michael@0 | 46 | * Issues to be discussed: |
michael@0 | 47 | * - Return values. There are nonstandard return values defined and used |
michael@0 | 48 | * in the source code. This is because RFC2553 is silent about which error |
michael@0 | 49 | * code must be returned for which situation. |
michael@0 | 50 | * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2 |
michael@0 | 51 | * says to use inet_aton() to convert IPv4 numeric to binary (alows |
michael@0 | 52 | * classful form as a result). |
michael@0 | 53 | * current code - disallow classful form for IPv4 (due to use of inet_pton). |
michael@0 | 54 | * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is |
michael@0 | 55 | * invalid. |
michael@0 | 56 | * current code - SEGV on freeaddrinfo(NULL) |
michael@0 | 57 | * Note: |
michael@0 | 58 | * - We use getipnodebyname() just for thread-safeness. There's no intent |
michael@0 | 59 | * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to |
michael@0 | 60 | * getipnodebyname(). |
michael@0 | 61 | * - MOZILLA: Thread safeness for pre-Honeycomb Android versions implemented by |
michael@0 | 62 | * way of open/gets/close and mmap rather than fopen/fgets/fclose. Affects |
michael@0 | 63 | * _files_getaddrinfo for hosts file. Note: Honeycomb and later versions use |
michael@0 | 64 | * a thread-safe stdio, so for those versions normal Bionic libc getaddrinfo |
michael@0 | 65 | * is used. |
michael@0 | 66 | * - The code filters out AFs that are not supported by the kernel, |
michael@0 | 67 | * when globbing NULL hostname (to loopback, or wildcard). Is it the right |
michael@0 | 68 | * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG |
michael@0 | 69 | * in ai_flags? |
michael@0 | 70 | * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. |
michael@0 | 71 | * (1) what should we do against numeric hostname (2) what should we do |
michael@0 | 72 | * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? |
michael@0 | 73 | * non-loopback address configured? global address configured? |
michael@0 | 74 | * - To avoid search order issue, we have a big amount of code duplicate |
michael@0 | 75 | * from gethnamaddr.c and some other places. The issues that there's no |
michael@0 | 76 | * lower layer function to lookup "IPv4 or IPv6" record. Calling |
michael@0 | 77 | * gethostbyname2 from getaddrinfo will end up in wrong search order, as |
michael@0 | 78 | * follows: |
michael@0 | 79 | * - The code makes use of following calls when asked to resolver with |
michael@0 | 80 | * ai_family = PF_UNSPEC: |
michael@0 | 81 | * getipnodebyname(host, AF_INET6); |
michael@0 | 82 | * getipnodebyname(host, AF_INET); |
michael@0 | 83 | * This will result in the following queries if the node is configure to |
michael@0 | 84 | * prefer /etc/hosts than DNS: |
michael@0 | 85 | * lookup /etc/hosts for IPv6 address |
michael@0 | 86 | * lookup DNS for IPv6 address |
michael@0 | 87 | * lookup /etc/hosts for IPv4 address |
michael@0 | 88 | * lookup DNS for IPv4 address |
michael@0 | 89 | * which may not meet people's requirement. |
michael@0 | 90 | * The right thing to happen is to have underlying layer which does |
michael@0 | 91 | * PF_UNSPEC lookup (lookup both) and return chain of addrinfos. |
michael@0 | 92 | * This would result in a bit of code duplicate with _dns_ghbyname() and |
michael@0 | 93 | * friends. |
michael@0 | 94 | */ |
michael@0 | 95 | |
michael@0 | 96 | #include <fcntl.h> |
michael@0 | 97 | #include <sys/cdefs.h> |
michael@0 | 98 | #include <sys/types.h> |
michael@0 | 99 | #include <sys/stat.h> |
michael@0 | 100 | #include <sys/param.h> |
michael@0 | 101 | #include <sys/socket.h> |
michael@0 | 102 | #include <sys/un.h> |
michael@0 | 103 | #include <sys/mman.h> |
michael@0 | 104 | #include <net/if.h> |
michael@0 | 105 | #include <netinet/in.h> |
michael@0 | 106 | #include <arpa/inet.h> |
michael@0 | 107 | #include "arpa_nameser.h" |
michael@0 | 108 | #include <assert.h> |
michael@0 | 109 | #include <ctype.h> |
michael@0 | 110 | #include <errno.h> |
michael@0 | 111 | #include <netdb.h> |
michael@0 | 112 | #include "resolv_private.h" |
michael@0 | 113 | #include <stddef.h> |
michael@0 | 114 | #include <stdlib.h> |
michael@0 | 115 | #include <string.h> |
michael@0 | 116 | #include <unistd.h> |
michael@0 | 117 | |
michael@0 | 118 | #include <syslog.h> |
michael@0 | 119 | #include <stdarg.h> |
michael@0 | 120 | #include "nsswitch.h" |
michael@0 | 121 | |
michael@0 | 122 | #ifdef MOZ_GETADDRINFO_LOG_VERBOSE |
michael@0 | 123 | #include <android/log.h> |
michael@0 | 124 | #endif |
michael@0 | 125 | |
michael@0 | 126 | #ifdef ANDROID_CHANGES |
michael@0 | 127 | #include <sys/system_properties.h> |
michael@0 | 128 | #endif /* ANDROID_CHANGES */ |
michael@0 | 129 | |
michael@0 | 130 | typedef struct _pseudo_FILE { |
michael@0 | 131 | int fd; |
michael@0 | 132 | off_t maplen; |
michael@0 | 133 | void* mapping; |
michael@0 | 134 | off_t offset; |
michael@0 | 135 | } _pseudo_FILE; |
michael@0 | 136 | |
michael@0 | 137 | #define _PSEUDO_FILE_INITIALIZER { -1, 0, MAP_FAILED, 0 } |
michael@0 | 138 | |
michael@0 | 139 | static void |
michael@0 | 140 | _pseudo_fclose(_pseudo_FILE * __restrict__ fp) |
michael@0 | 141 | { |
michael@0 | 142 | assert(fp); |
michael@0 | 143 | fp->offset = 0; |
michael@0 | 144 | if (fp->mapping != MAP_FAILED) { |
michael@0 | 145 | (void) munmap(fp->mapping, fp->maplen); |
michael@0 | 146 | fp->mapping = MAP_FAILED; |
michael@0 | 147 | } |
michael@0 | 148 | fp->maplen = 0; |
michael@0 | 149 | if (fp->fd != -1) { |
michael@0 | 150 | (void) close(fp->fd); |
michael@0 | 151 | fp->fd = -1; |
michael@0 | 152 | } |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | static _pseudo_FILE * |
michael@0 | 156 | _pseudo_fopen_r(_pseudo_FILE * __restrict__ fp, const char* fname) |
michael@0 | 157 | { |
michael@0 | 158 | struct stat statbuf; |
michael@0 | 159 | assert(fp); |
michael@0 | 160 | fp->fd = open(fname, O_RDONLY); |
michael@0 | 161 | if (fp->fd < 0) { |
michael@0 | 162 | fp->fd = -1; |
michael@0 | 163 | return NULL; |
michael@0 | 164 | } |
michael@0 | 165 | if ((0 != fstat(fp->fd, &statbuf)) || (statbuf.st_size <= 0)) { |
michael@0 | 166 | close(fp->fd); |
michael@0 | 167 | fp->fd = -1; |
michael@0 | 168 | return NULL; |
michael@0 | 169 | } |
michael@0 | 170 | fp->maplen = statbuf.st_size; |
michael@0 | 171 | fp->mapping = mmap(NULL, fp->maplen, PROT_READ, MAP_PRIVATE, fp->fd, 0); |
michael@0 | 172 | if (fp->mapping == MAP_FAILED) { |
michael@0 | 173 | close(fp->fd); |
michael@0 | 174 | fp->fd = -1; |
michael@0 | 175 | return NULL; |
michael@0 | 176 | } |
michael@0 | 177 | fp->offset = 0; |
michael@0 | 178 | return fp; |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | static void |
michael@0 | 182 | _pseudo_rewind(_pseudo_FILE * __restrict__ fp) |
michael@0 | 183 | { |
michael@0 | 184 | assert(fp); |
michael@0 | 185 | fp->offset = 0; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | static char* |
michael@0 | 189 | _pseudo_fgets(char* buf, int bufsize, _pseudo_FILE * __restrict__ fp) |
michael@0 | 190 | { |
michael@0 | 191 | char* current; |
michael@0 | 192 | char* endp; |
michael@0 | 193 | int maxcopy; |
michael@0 | 194 | assert(fp); |
michael@0 | 195 | maxcopy = fp->maplen - fp->offset; |
michael@0 | 196 | if (fp->mapping == MAP_FAILED) |
michael@0 | 197 | return NULL; |
michael@0 | 198 | if (maxcopy > bufsize - 1) |
michael@0 | 199 | maxcopy = bufsize - 1; |
michael@0 | 200 | if (maxcopy <= 0) |
michael@0 | 201 | return NULL; |
michael@0 | 202 | current = ((char*) fp->mapping) + fp->offset; |
michael@0 | 203 | endp = memccpy(buf, current, '\n', maxcopy); |
michael@0 | 204 | if (endp) |
michael@0 | 205 | maxcopy = endp - buf; |
michael@0 | 206 | buf[maxcopy] = '\0'; |
michael@0 | 207 | fp->offset += maxcopy; |
michael@0 | 208 | return buf; |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | typedef union sockaddr_union { |
michael@0 | 212 | struct sockaddr generic; |
michael@0 | 213 | struct sockaddr_in in; |
michael@0 | 214 | struct sockaddr_in6 in6; |
michael@0 | 215 | } sockaddr_union; |
michael@0 | 216 | |
michael@0 | 217 | #define SUCCESS 0 |
michael@0 | 218 | #define ANY 0 |
michael@0 | 219 | #define YES 1 |
michael@0 | 220 | #define NO 0 |
michael@0 | 221 | |
michael@0 | 222 | static const char in_addrany[] = { 0, 0, 0, 0 }; |
michael@0 | 223 | static const char in_loopback[] = { 127, 0, 0, 1 }; |
michael@0 | 224 | #ifdef INET6 |
michael@0 | 225 | static const char in6_addrany[] = { |
michael@0 | 226 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
michael@0 | 227 | }; |
michael@0 | 228 | static const char in6_loopback[] = { |
michael@0 | 229 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 |
michael@0 | 230 | }; |
michael@0 | 231 | #endif |
michael@0 | 232 | |
michael@0 | 233 | static const struct afd { |
michael@0 | 234 | int a_af; |
michael@0 | 235 | int a_addrlen; |
michael@0 | 236 | int a_socklen; |
michael@0 | 237 | int a_off; |
michael@0 | 238 | const char *a_addrany; |
michael@0 | 239 | const char *a_loopback; |
michael@0 | 240 | int a_scoped; |
michael@0 | 241 | } afdl [] = { |
michael@0 | 242 | #ifdef INET6 |
michael@0 | 243 | {PF_INET6, sizeof(struct in6_addr), |
michael@0 | 244 | sizeof(struct sockaddr_in6), |
michael@0 | 245 | offsetof(struct sockaddr_in6, sin6_addr), |
michael@0 | 246 | in6_addrany, in6_loopback, 1}, |
michael@0 | 247 | #endif |
michael@0 | 248 | {PF_INET, sizeof(struct in_addr), |
michael@0 | 249 | sizeof(struct sockaddr_in), |
michael@0 | 250 | offsetof(struct sockaddr_in, sin_addr), |
michael@0 | 251 | in_addrany, in_loopback, 0}, |
michael@0 | 252 | {0, 0, 0, 0, NULL, NULL, 0}, |
michael@0 | 253 | }; |
michael@0 | 254 | |
michael@0 | 255 | struct explore { |
michael@0 | 256 | int e_af; |
michael@0 | 257 | int e_socktype; |
michael@0 | 258 | int e_protocol; |
michael@0 | 259 | const char *e_protostr; |
michael@0 | 260 | int e_wild; |
michael@0 | 261 | #define WILD_AF(ex) ((ex)->e_wild & 0x01) |
michael@0 | 262 | #define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) |
michael@0 | 263 | #define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) |
michael@0 | 264 | }; |
michael@0 | 265 | |
michael@0 | 266 | static const struct explore explore[] = { |
michael@0 | 267 | #if 0 |
michael@0 | 268 | { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, |
michael@0 | 269 | #endif |
michael@0 | 270 | #ifdef INET6 |
michael@0 | 271 | { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, |
michael@0 | 272 | { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, |
michael@0 | 273 | { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, |
michael@0 | 274 | #endif |
michael@0 | 275 | { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, |
michael@0 | 276 | { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, |
michael@0 | 277 | { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, |
michael@0 | 278 | { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, |
michael@0 | 279 | { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, |
michael@0 | 280 | { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, |
michael@0 | 281 | { -1, 0, 0, NULL, 0 }, |
michael@0 | 282 | }; |
michael@0 | 283 | |
michael@0 | 284 | #ifdef INET6 |
michael@0 | 285 | #define PTON_MAX 16 |
michael@0 | 286 | #else |
michael@0 | 287 | #define PTON_MAX 4 |
michael@0 | 288 | #endif |
michael@0 | 289 | |
michael@0 | 290 | static const ns_src default_dns_files[] = { |
michael@0 | 291 | { NSSRC_FILES, NS_SUCCESS }, |
michael@0 | 292 | { NSSRC_DNS, NS_SUCCESS }, |
michael@0 | 293 | { 0, 0 } |
michael@0 | 294 | }; |
michael@0 | 295 | |
michael@0 | 296 | #define MAXPACKET (64*1024) |
michael@0 | 297 | |
michael@0 | 298 | typedef union { |
michael@0 | 299 | HEADER hdr; |
michael@0 | 300 | u_char buf[MAXPACKET]; |
michael@0 | 301 | } querybuf; |
michael@0 | 302 | |
michael@0 | 303 | struct res_target { |
michael@0 | 304 | struct res_target *next; |
michael@0 | 305 | const char *name; /* domain name */ |
michael@0 | 306 | int qclass, qtype; /* class and type of query */ |
michael@0 | 307 | u_char *answer; /* buffer to put answer */ |
michael@0 | 308 | int anslen; /* size of answer buffer */ |
michael@0 | 309 | int n; /* result length */ |
michael@0 | 310 | }; |
michael@0 | 311 | |
michael@0 | 312 | static int str2number(const char *); |
michael@0 | 313 | static int explore_fqdn(const struct addrinfo *, const char *, |
michael@0 | 314 | const char *, struct addrinfo **); |
michael@0 | 315 | static int explore_null(const struct addrinfo *, |
michael@0 | 316 | const char *, struct addrinfo **); |
michael@0 | 317 | static int explore_numeric(const struct addrinfo *, const char *, |
michael@0 | 318 | const char *, struct addrinfo **, const char *); |
michael@0 | 319 | static int explore_numeric_scope(const struct addrinfo *, const char *, |
michael@0 | 320 | const char *, struct addrinfo **); |
michael@0 | 321 | static int get_canonname(const struct addrinfo *, |
michael@0 | 322 | struct addrinfo *, const char *); |
michael@0 | 323 | static struct addrinfo *get_ai(const struct addrinfo *, |
michael@0 | 324 | const struct afd *, const char *); |
michael@0 | 325 | static int get_portmatch(const struct addrinfo *, const char *); |
michael@0 | 326 | static int get_port(const struct addrinfo *, const char *, int); |
michael@0 | 327 | static const struct afd *find_afd(int); |
michael@0 | 328 | #ifdef INET6 |
michael@0 | 329 | static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); |
michael@0 | 330 | #endif |
michael@0 | 331 | |
michael@0 | 332 | static struct addrinfo *getanswer(const querybuf *, int, const char *, int, |
michael@0 | 333 | const struct addrinfo *); |
michael@0 | 334 | static int _dns_getaddrinfo(void *, void *, va_list); |
michael@0 | 335 | static void _sethtent(_pseudo_FILE * __restrict__); |
michael@0 | 336 | static void _endhtent(_pseudo_FILE * __restrict__); |
michael@0 | 337 | static struct addrinfo *_gethtent(_pseudo_FILE * __restrict__, const char *, |
michael@0 | 338 | const struct addrinfo *); |
michael@0 | 339 | static int _files_getaddrinfo(void *, void *, va_list); |
michael@0 | 340 | |
michael@0 | 341 | static int res_queryN(const char *, struct res_target *, res_state); |
michael@0 | 342 | static int res_searchN(const char *, struct res_target *, res_state); |
michael@0 | 343 | static int res_querydomainN(const char *, const char *, |
michael@0 | 344 | struct res_target *, res_state); |
michael@0 | 345 | |
michael@0 | 346 | static const char * const ai_errlist[] = { |
michael@0 | 347 | "Success", |
michael@0 | 348 | "Address family for hostname not supported", /* EAI_ADDRFAMILY */ |
michael@0 | 349 | "Temporary failure in name resolution", /* EAI_AGAIN */ |
michael@0 | 350 | "Invalid value for ai_flags", /* EAI_BADFLAGS */ |
michael@0 | 351 | "Non-recoverable failure in name resolution", /* EAI_FAIL */ |
michael@0 | 352 | "ai_family not supported", /* EAI_FAMILY */ |
michael@0 | 353 | "Memory allocation failure", /* EAI_MEMORY */ |
michael@0 | 354 | "No address associated with hostname", /* EAI_NODATA */ |
michael@0 | 355 | "hostname nor servname provided, or not known", /* EAI_NONAME */ |
michael@0 | 356 | "servname not supported for ai_socktype", /* EAI_SERVICE */ |
michael@0 | 357 | "ai_socktype not supported", /* EAI_SOCKTYPE */ |
michael@0 | 358 | "System error returned in errno", /* EAI_SYSTEM */ |
michael@0 | 359 | "Invalid value for hints", /* EAI_BADHINTS */ |
michael@0 | 360 | "Resolved protocol is unknown", /* EAI_PROTOCOL */ |
michael@0 | 361 | "Argument buffer overflow", /* EAI_OVERFLOW */ |
michael@0 | 362 | "Unknown error", /* EAI_MAX */ |
michael@0 | 363 | }; |
michael@0 | 364 | |
michael@0 | 365 | /* XXX macros that make external reference is BAD. */ |
michael@0 | 366 | |
michael@0 | 367 | #define GET_AI(ai, afd, addr) \ |
michael@0 | 368 | do { \ |
michael@0 | 369 | /* external reference: pai, error, and label free */ \ |
michael@0 | 370 | (ai) = get_ai(pai, (afd), (addr)); \ |
michael@0 | 371 | if ((ai) == NULL) { \ |
michael@0 | 372 | error = EAI_MEMORY; \ |
michael@0 | 373 | goto free; \ |
michael@0 | 374 | } \ |
michael@0 | 375 | } while (/*CONSTCOND*/0) |
michael@0 | 376 | |
michael@0 | 377 | #define GET_PORT(ai, serv) \ |
michael@0 | 378 | do { \ |
michael@0 | 379 | /* external reference: error and label free */ \ |
michael@0 | 380 | error = get_port((ai), (serv), 0); \ |
michael@0 | 381 | if (error != 0) \ |
michael@0 | 382 | goto free; \ |
michael@0 | 383 | } while (/*CONSTCOND*/0) |
michael@0 | 384 | |
michael@0 | 385 | #define GET_CANONNAME(ai, str) \ |
michael@0 | 386 | do { \ |
michael@0 | 387 | /* external reference: pai, error and label free */ \ |
michael@0 | 388 | error = get_canonname(pai, (ai), (str)); \ |
michael@0 | 389 | if (error != 0) \ |
michael@0 | 390 | goto free; \ |
michael@0 | 391 | } while (/*CONSTCOND*/0) |
michael@0 | 392 | |
michael@0 | 393 | #define ERR(err) \ |
michael@0 | 394 | do { \ |
michael@0 | 395 | /* external reference: error, and label bad */ \ |
michael@0 | 396 | error = (err); \ |
michael@0 | 397 | goto bad; \ |
michael@0 | 398 | /*NOTREACHED*/ \ |
michael@0 | 399 | } while (/*CONSTCOND*/0) |
michael@0 | 400 | |
michael@0 | 401 | #define MATCH_FAMILY(x, y, w) \ |
michael@0 | 402 | ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || \ |
michael@0 | 403 | (y) == PF_UNSPEC))) |
michael@0 | 404 | #define MATCH(x, y, w) \ |
michael@0 | 405 | ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) |
michael@0 | 406 | |
michael@0 | 407 | #pragma GCC visibility push(default) |
michael@0 | 408 | |
michael@0 | 409 | extern const char * |
michael@0 | 410 | __wrap_gai_strerror(int ecode); |
michael@0 | 411 | extern void |
michael@0 | 412 | __wrap_freeaddrinfo(struct addrinfo *ai); |
michael@0 | 413 | extern int |
michael@0 | 414 | __wrap_getaddrinfo(const char *hostname, const char *servname, |
michael@0 | 415 | const struct addrinfo *hints, struct addrinfo **res); |
michael@0 | 416 | |
michael@0 | 417 | int android_sdk_version; |
michael@0 | 418 | |
michael@0 | 419 | #pragma GCC visibility pop |
michael@0 | 420 | |
michael@0 | 421 | int android_sdk_version = -1; |
michael@0 | 422 | |
michael@0 | 423 | static int honeycomb_or_later() |
michael@0 | 424 | { |
michael@0 | 425 | #ifdef MOZ_GETADDRINFO_LOG_VERBOSE |
michael@0 | 426 | __android_log_print(ANDROID_LOG_INFO, "getaddrinfo", |
michael@0 | 427 | "I am%s Honeycomb\n", |
michael@0 | 428 | (android_sdk_version >= 11) ? "" : " not"); |
michael@0 | 429 | #endif |
michael@0 | 430 | return android_sdk_version >= 11; |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | const char * |
michael@0 | 434 | __wrap_gai_strerror(int ecode) |
michael@0 | 435 | { |
michael@0 | 436 | if (honeycomb_or_later()) |
michael@0 | 437 | return gai_strerror(ecode); |
michael@0 | 438 | if (ecode < 0 || ecode > EAI_MAX) |
michael@0 | 439 | ecode = EAI_MAX; |
michael@0 | 440 | return ai_errlist[ecode]; |
michael@0 | 441 | } |
michael@0 | 442 | |
michael@0 | 443 | void |
michael@0 | 444 | __wrap_freeaddrinfo(struct addrinfo *ai) |
michael@0 | 445 | { |
michael@0 | 446 | struct addrinfo *next; |
michael@0 | 447 | |
michael@0 | 448 | if (honeycomb_or_later()) { |
michael@0 | 449 | freeaddrinfo(ai); |
michael@0 | 450 | return; |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | assert(ai != NULL); |
michael@0 | 454 | |
michael@0 | 455 | do { |
michael@0 | 456 | next = ai->ai_next; |
michael@0 | 457 | if (ai->ai_canonname) |
michael@0 | 458 | free(ai->ai_canonname); |
michael@0 | 459 | /* no need to free(ai->ai_addr) */ |
michael@0 | 460 | free(ai); |
michael@0 | 461 | ai = next; |
michael@0 | 462 | } while (ai); |
michael@0 | 463 | } |
michael@0 | 464 | |
michael@0 | 465 | static int |
michael@0 | 466 | str2number(const char *p) |
michael@0 | 467 | { |
michael@0 | 468 | char *ep; |
michael@0 | 469 | unsigned long v; |
michael@0 | 470 | |
michael@0 | 471 | assert(p != NULL); |
michael@0 | 472 | |
michael@0 | 473 | if (*p == '\0') |
michael@0 | 474 | return -1; |
michael@0 | 475 | ep = NULL; |
michael@0 | 476 | errno = 0; |
michael@0 | 477 | v = strtoul(p, &ep, 10); |
michael@0 | 478 | if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) |
michael@0 | 479 | return v; |
michael@0 | 480 | else |
michael@0 | 481 | return -1; |
michael@0 | 482 | } |
michael@0 | 483 | |
michael@0 | 484 | /* |
michael@0 | 485 | * Connect a UDP socket to a given unicast address. This will cause no network |
michael@0 | 486 | * traffic, but will fail fast if the system has no or limited reachability to |
michael@0 | 487 | * the destination (e.g., no IPv4 address, no IPv6 default route, ...). |
michael@0 | 488 | */ |
michael@0 | 489 | static int |
michael@0 | 490 | _test_connect(int pf, struct sockaddr *addr, size_t addrlen) { |
michael@0 | 491 | int s = socket(pf, SOCK_DGRAM, IPPROTO_UDP); |
michael@0 | 492 | if (s < 0) |
michael@0 | 493 | return 0; |
michael@0 | 494 | int ret; |
michael@0 | 495 | do { |
michael@0 | 496 | ret = connect(s, addr, addrlen); |
michael@0 | 497 | } while (ret < 0 && errno == EINTR); |
michael@0 | 498 | int success = (ret == 0); |
michael@0 | 499 | do { |
michael@0 | 500 | ret = close(s); |
michael@0 | 501 | } while (ret < 0 && errno == EINTR); |
michael@0 | 502 | return success; |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | /* |
michael@0 | 506 | * The following functions determine whether IPv4 or IPv6 connectivity is |
michael@0 | 507 | * available in order to implement AI_ADDRCONFIG. |
michael@0 | 508 | * |
michael@0 | 509 | * Strictly speaking, AI_ADDRCONFIG should not look at whether connectivity is |
michael@0 | 510 | * available, but whether addresses of the specified family are "configured |
michael@0 | 511 | * on the local system". However, bionic doesn't currently support getifaddrs, |
michael@0 | 512 | * so checking for connectivity is the next best thing. |
michael@0 | 513 | */ |
michael@0 | 514 | static int |
michael@0 | 515 | _have_ipv6() { |
michael@0 | 516 | static const struct sockaddr_in6 sin6_test = { |
michael@0 | 517 | .sin6_family = AF_INET6, |
michael@0 | 518 | .sin6_addr.s6_addr = { // 2000:: |
michael@0 | 519 | 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
michael@0 | 520 | }; |
michael@0 | 521 | sockaddr_union addr = { .in6 = sin6_test }; |
michael@0 | 522 | return _test_connect(PF_INET6, &addr.generic, sizeof(addr.in6)); |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | static int |
michael@0 | 526 | _have_ipv4() { |
michael@0 | 527 | static const struct sockaddr_in sin_test = { |
michael@0 | 528 | .sin_family = AF_INET, |
michael@0 | 529 | .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8 |
michael@0 | 530 | }; |
michael@0 | 531 | sockaddr_union addr = { .in = sin_test }; |
michael@0 | 532 | return _test_connect(PF_INET, &addr.generic, sizeof(addr.in)); |
michael@0 | 533 | } |
michael@0 | 534 | |
michael@0 | 535 | int |
michael@0 | 536 | __wrap_getaddrinfo(const char *hostname, const char *servname, |
michael@0 | 537 | const struct addrinfo *hints, struct addrinfo **res) |
michael@0 | 538 | { |
michael@0 | 539 | struct addrinfo sentinel; |
michael@0 | 540 | struct addrinfo *cur; |
michael@0 | 541 | int error = 0; |
michael@0 | 542 | struct addrinfo ai; |
michael@0 | 543 | struct addrinfo ai0; |
michael@0 | 544 | struct addrinfo *pai; |
michael@0 | 545 | const struct explore *ex; |
michael@0 | 546 | |
michael@0 | 547 | if (honeycomb_or_later()) |
michael@0 | 548 | return getaddrinfo(hostname, servname, hints, res); |
michael@0 | 549 | |
michael@0 | 550 | /* hostname is allowed to be NULL */ |
michael@0 | 551 | /* servname is allowed to be NULL */ |
michael@0 | 552 | /* hints is allowed to be NULL */ |
michael@0 | 553 | assert(res != NULL); |
michael@0 | 554 | |
michael@0 | 555 | memset(&sentinel, 0, sizeof(sentinel)); |
michael@0 | 556 | cur = &sentinel; |
michael@0 | 557 | pai = &ai; |
michael@0 | 558 | pai->ai_flags = 0; |
michael@0 | 559 | pai->ai_family = PF_UNSPEC; |
michael@0 | 560 | pai->ai_socktype = ANY; |
michael@0 | 561 | pai->ai_protocol = ANY; |
michael@0 | 562 | pai->ai_addrlen = 0; |
michael@0 | 563 | pai->ai_canonname = NULL; |
michael@0 | 564 | pai->ai_addr = NULL; |
michael@0 | 565 | pai->ai_next = NULL; |
michael@0 | 566 | |
michael@0 | 567 | if (hostname == NULL && servname == NULL) |
michael@0 | 568 | return EAI_NONAME; |
michael@0 | 569 | if (hints) { |
michael@0 | 570 | /* error check for hints */ |
michael@0 | 571 | if (hints->ai_addrlen || hints->ai_canonname || |
michael@0 | 572 | hints->ai_addr || hints->ai_next) |
michael@0 | 573 | ERR(EAI_BADHINTS); /* xxx */ |
michael@0 | 574 | if (hints->ai_flags & ~AI_MASK) |
michael@0 | 575 | ERR(EAI_BADFLAGS); |
michael@0 | 576 | switch (hints->ai_family) { |
michael@0 | 577 | case PF_UNSPEC: |
michael@0 | 578 | case PF_INET: |
michael@0 | 579 | #ifdef INET6 |
michael@0 | 580 | case PF_INET6: |
michael@0 | 581 | #endif |
michael@0 | 582 | break; |
michael@0 | 583 | default: |
michael@0 | 584 | ERR(EAI_FAMILY); |
michael@0 | 585 | } |
michael@0 | 586 | memcpy(pai, hints, sizeof(*pai)); |
michael@0 | 587 | |
michael@0 | 588 | /* |
michael@0 | 589 | * if both socktype/protocol are specified, check if they |
michael@0 | 590 | * are meaningful combination. |
michael@0 | 591 | */ |
michael@0 | 592 | if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { |
michael@0 | 593 | for (ex = explore; ex->e_af >= 0; ex++) { |
michael@0 | 594 | if (pai->ai_family != ex->e_af) |
michael@0 | 595 | continue; |
michael@0 | 596 | if (ex->e_socktype == ANY) |
michael@0 | 597 | continue; |
michael@0 | 598 | if (ex->e_protocol == ANY) |
michael@0 | 599 | continue; |
michael@0 | 600 | if (pai->ai_socktype == ex->e_socktype |
michael@0 | 601 | && pai->ai_protocol != ex->e_protocol) { |
michael@0 | 602 | ERR(EAI_BADHINTS); |
michael@0 | 603 | } |
michael@0 | 604 | } |
michael@0 | 605 | } |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | /* |
michael@0 | 609 | * check for special cases. (1) numeric servname is disallowed if |
michael@0 | 610 | * socktype/protocol are left unspecified. (2) servname is disallowed |
michael@0 | 611 | * for raw and other inet{,6} sockets. |
michael@0 | 612 | */ |
michael@0 | 613 | if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) |
michael@0 | 614 | #ifdef PF_INET6 |
michael@0 | 615 | || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) |
michael@0 | 616 | #endif |
michael@0 | 617 | ) { |
michael@0 | 618 | ai0 = *pai; /* backup *pai */ |
michael@0 | 619 | |
michael@0 | 620 | if (pai->ai_family == PF_UNSPEC) { |
michael@0 | 621 | #ifdef PF_INET6 |
michael@0 | 622 | pai->ai_family = PF_INET6; |
michael@0 | 623 | #else |
michael@0 | 624 | pai->ai_family = PF_INET; |
michael@0 | 625 | #endif |
michael@0 | 626 | } |
michael@0 | 627 | error = get_portmatch(pai, servname); |
michael@0 | 628 | if (error) |
michael@0 | 629 | ERR(error); |
michael@0 | 630 | |
michael@0 | 631 | *pai = ai0; |
michael@0 | 632 | } |
michael@0 | 633 | |
michael@0 | 634 | ai0 = *pai; |
michael@0 | 635 | |
michael@0 | 636 | /* NULL hostname, or numeric hostname */ |
michael@0 | 637 | for (ex = explore; ex->e_af >= 0; ex++) { |
michael@0 | 638 | *pai = ai0; |
michael@0 | 639 | |
michael@0 | 640 | /* PF_UNSPEC entries are prepared for DNS queries only */ |
michael@0 | 641 | if (ex->e_af == PF_UNSPEC) |
michael@0 | 642 | continue; |
michael@0 | 643 | |
michael@0 | 644 | if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) |
michael@0 | 645 | continue; |
michael@0 | 646 | if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) |
michael@0 | 647 | continue; |
michael@0 | 648 | if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) |
michael@0 | 649 | continue; |
michael@0 | 650 | |
michael@0 | 651 | if (pai->ai_family == PF_UNSPEC) |
michael@0 | 652 | pai->ai_family = ex->e_af; |
michael@0 | 653 | if (pai->ai_socktype == ANY && ex->e_socktype != ANY) |
michael@0 | 654 | pai->ai_socktype = ex->e_socktype; |
michael@0 | 655 | if (pai->ai_protocol == ANY && ex->e_protocol != ANY) |
michael@0 | 656 | pai->ai_protocol = ex->e_protocol; |
michael@0 | 657 | |
michael@0 | 658 | if (hostname == NULL) |
michael@0 | 659 | error = explore_null(pai, servname, &cur->ai_next); |
michael@0 | 660 | else |
michael@0 | 661 | error = explore_numeric_scope(pai, hostname, servname, |
michael@0 | 662 | &cur->ai_next); |
michael@0 | 663 | |
michael@0 | 664 | if (error) |
michael@0 | 665 | goto free; |
michael@0 | 666 | |
michael@0 | 667 | while (cur->ai_next) |
michael@0 | 668 | cur = cur->ai_next; |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | /* |
michael@0 | 672 | * XXX |
michael@0 | 673 | * If numeric representation of AF1 can be interpreted as FQDN |
michael@0 | 674 | * representation of AF2, we need to think again about the code below. |
michael@0 | 675 | */ |
michael@0 | 676 | if (sentinel.ai_next) |
michael@0 | 677 | goto good; |
michael@0 | 678 | |
michael@0 | 679 | if (hostname == NULL) |
michael@0 | 680 | ERR(EAI_NODATA); |
michael@0 | 681 | if (pai->ai_flags & AI_NUMERICHOST) |
michael@0 | 682 | ERR(EAI_NONAME); |
michael@0 | 683 | |
michael@0 | 684 | /* |
michael@0 | 685 | * hostname as alphabetical name. |
michael@0 | 686 | * we would like to prefer AF_INET6 than AF_INET, so we'll make a |
michael@0 | 687 | * outer loop by AFs. |
michael@0 | 688 | */ |
michael@0 | 689 | for (ex = explore; ex->e_af >= 0; ex++) { |
michael@0 | 690 | *pai = ai0; |
michael@0 | 691 | |
michael@0 | 692 | /* require exact match for family field */ |
michael@0 | 693 | if (pai->ai_family != ex->e_af) |
michael@0 | 694 | continue; |
michael@0 | 695 | |
michael@0 | 696 | if (!MATCH(pai->ai_socktype, ex->e_socktype, |
michael@0 | 697 | WILD_SOCKTYPE(ex))) { |
michael@0 | 698 | continue; |
michael@0 | 699 | } |
michael@0 | 700 | if (!MATCH(pai->ai_protocol, ex->e_protocol, |
michael@0 | 701 | WILD_PROTOCOL(ex))) { |
michael@0 | 702 | continue; |
michael@0 | 703 | } |
michael@0 | 704 | |
michael@0 | 705 | if (pai->ai_socktype == ANY && ex->e_socktype != ANY) |
michael@0 | 706 | pai->ai_socktype = ex->e_socktype; |
michael@0 | 707 | if (pai->ai_protocol == ANY && ex->e_protocol != ANY) |
michael@0 | 708 | pai->ai_protocol = ex->e_protocol; |
michael@0 | 709 | |
michael@0 | 710 | error = explore_fqdn(pai, hostname, servname, |
michael@0 | 711 | &cur->ai_next); |
michael@0 | 712 | |
michael@0 | 713 | while (cur && cur->ai_next) |
michael@0 | 714 | cur = cur->ai_next; |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | /* XXX */ |
michael@0 | 718 | if (sentinel.ai_next) |
michael@0 | 719 | error = 0; |
michael@0 | 720 | |
michael@0 | 721 | if (error) |
michael@0 | 722 | goto free; |
michael@0 | 723 | if (error == 0) { |
michael@0 | 724 | if (sentinel.ai_next) { |
michael@0 | 725 | good: |
michael@0 | 726 | *res = sentinel.ai_next; |
michael@0 | 727 | return SUCCESS; |
michael@0 | 728 | } else |
michael@0 | 729 | error = EAI_FAIL; |
michael@0 | 730 | } |
michael@0 | 731 | free: |
michael@0 | 732 | bad: |
michael@0 | 733 | if (sentinel.ai_next) |
michael@0 | 734 | __wrap_freeaddrinfo(sentinel.ai_next); |
michael@0 | 735 | *res = NULL; |
michael@0 | 736 | return error; |
michael@0 | 737 | } |
michael@0 | 738 | |
michael@0 | 739 | /* |
michael@0 | 740 | * FQDN hostname, DNS lookup |
michael@0 | 741 | */ |
michael@0 | 742 | static int |
michael@0 | 743 | explore_fqdn(const struct addrinfo *pai, const char *hostname, |
michael@0 | 744 | const char *servname, struct addrinfo **res) |
michael@0 | 745 | { |
michael@0 | 746 | struct addrinfo *result; |
michael@0 | 747 | struct addrinfo *cur; |
michael@0 | 748 | int error = 0; |
michael@0 | 749 | static const ns_dtab dtab[] = { |
michael@0 | 750 | NS_FILES_CB(_files_getaddrinfo, NULL) |
michael@0 | 751 | { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ |
michael@0 | 752 | NS_NIS_CB(_yp_getaddrinfo, NULL) |
michael@0 | 753 | { 0, 0, 0 } |
michael@0 | 754 | }; |
michael@0 | 755 | |
michael@0 | 756 | assert(pai != NULL); |
michael@0 | 757 | /* hostname may be NULL */ |
michael@0 | 758 | /* servname may be NULL */ |
michael@0 | 759 | assert(res != NULL); |
michael@0 | 760 | |
michael@0 | 761 | result = NULL; |
michael@0 | 762 | |
michael@0 | 763 | /* |
michael@0 | 764 | * if the servname does not match socktype/protocol, ignore it. |
michael@0 | 765 | */ |
michael@0 | 766 | if (get_portmatch(pai, servname) != 0) |
michael@0 | 767 | return 0; |
michael@0 | 768 | |
michael@0 | 769 | switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", |
michael@0 | 770 | default_dns_files, hostname, pai)) { |
michael@0 | 771 | case NS_TRYAGAIN: |
michael@0 | 772 | error = EAI_AGAIN; |
michael@0 | 773 | goto free; |
michael@0 | 774 | case NS_UNAVAIL: |
michael@0 | 775 | error = EAI_FAIL; |
michael@0 | 776 | goto free; |
michael@0 | 777 | case NS_NOTFOUND: |
michael@0 | 778 | error = EAI_NODATA; |
michael@0 | 779 | goto free; |
michael@0 | 780 | case NS_SUCCESS: |
michael@0 | 781 | error = 0; |
michael@0 | 782 | for (cur = result; cur; cur = cur->ai_next) { |
michael@0 | 783 | GET_PORT(cur, servname); |
michael@0 | 784 | /* canonname should be filled already */ |
michael@0 | 785 | } |
michael@0 | 786 | break; |
michael@0 | 787 | } |
michael@0 | 788 | |
michael@0 | 789 | *res = result; |
michael@0 | 790 | |
michael@0 | 791 | return 0; |
michael@0 | 792 | |
michael@0 | 793 | free: |
michael@0 | 794 | if (result) |
michael@0 | 795 | __wrap_freeaddrinfo(result); |
michael@0 | 796 | return error; |
michael@0 | 797 | } |
michael@0 | 798 | |
michael@0 | 799 | /* |
michael@0 | 800 | * hostname == NULL. |
michael@0 | 801 | * passive socket -> anyaddr (0.0.0.0 or ::) |
michael@0 | 802 | * non-passive socket -> localhost (127.0.0.1 or ::1) |
michael@0 | 803 | */ |
michael@0 | 804 | static int |
michael@0 | 805 | explore_null(const struct addrinfo *pai, const char *servname, |
michael@0 | 806 | struct addrinfo **res) |
michael@0 | 807 | { |
michael@0 | 808 | int s; |
michael@0 | 809 | const struct afd *afd; |
michael@0 | 810 | struct addrinfo *cur; |
michael@0 | 811 | struct addrinfo sentinel; |
michael@0 | 812 | int error; |
michael@0 | 813 | |
michael@0 | 814 | assert(pai != NULL); |
michael@0 | 815 | /* servname may be NULL */ |
michael@0 | 816 | assert(res != NULL); |
michael@0 | 817 | |
michael@0 | 818 | *res = NULL; |
michael@0 | 819 | sentinel.ai_next = NULL; |
michael@0 | 820 | cur = &sentinel; |
michael@0 | 821 | |
michael@0 | 822 | /* |
michael@0 | 823 | * filter out AFs that are not supported by the kernel |
michael@0 | 824 | * XXX errno? |
michael@0 | 825 | */ |
michael@0 | 826 | s = socket(pai->ai_family, SOCK_DGRAM, 0); |
michael@0 | 827 | if (s < 0) { |
michael@0 | 828 | if (errno != EMFILE) |
michael@0 | 829 | return 0; |
michael@0 | 830 | } else |
michael@0 | 831 | close(s); |
michael@0 | 832 | |
michael@0 | 833 | /* |
michael@0 | 834 | * if the servname does not match socktype/protocol, ignore it. |
michael@0 | 835 | */ |
michael@0 | 836 | if (get_portmatch(pai, servname) != 0) |
michael@0 | 837 | return 0; |
michael@0 | 838 | |
michael@0 | 839 | afd = find_afd(pai->ai_family); |
michael@0 | 840 | if (afd == NULL) |
michael@0 | 841 | return 0; |
michael@0 | 842 | |
michael@0 | 843 | if (pai->ai_flags & AI_PASSIVE) { |
michael@0 | 844 | GET_AI(cur->ai_next, afd, afd->a_addrany); |
michael@0 | 845 | /* xxx meaningless? |
michael@0 | 846 | * GET_CANONNAME(cur->ai_next, "anyaddr"); |
michael@0 | 847 | */ |
michael@0 | 848 | GET_PORT(cur->ai_next, servname); |
michael@0 | 849 | } else { |
michael@0 | 850 | GET_AI(cur->ai_next, afd, afd->a_loopback); |
michael@0 | 851 | /* xxx meaningless? |
michael@0 | 852 | * GET_CANONNAME(cur->ai_next, "localhost"); |
michael@0 | 853 | */ |
michael@0 | 854 | GET_PORT(cur->ai_next, servname); |
michael@0 | 855 | } |
michael@0 | 856 | cur = cur->ai_next; |
michael@0 | 857 | |
michael@0 | 858 | *res = sentinel.ai_next; |
michael@0 | 859 | return 0; |
michael@0 | 860 | |
michael@0 | 861 | free: |
michael@0 | 862 | if (sentinel.ai_next) |
michael@0 | 863 | __wrap_freeaddrinfo(sentinel.ai_next); |
michael@0 | 864 | return error; |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | /* |
michael@0 | 868 | * numeric hostname |
michael@0 | 869 | */ |
michael@0 | 870 | static int |
michael@0 | 871 | explore_numeric(const struct addrinfo *pai, const char *hostname, |
michael@0 | 872 | const char *servname, struct addrinfo **res, const char *canonname) |
michael@0 | 873 | { |
michael@0 | 874 | const struct afd *afd; |
michael@0 | 875 | struct addrinfo *cur; |
michael@0 | 876 | struct addrinfo sentinel; |
michael@0 | 877 | int error; |
michael@0 | 878 | char pton[PTON_MAX]; |
michael@0 | 879 | |
michael@0 | 880 | assert(pai != NULL); |
michael@0 | 881 | /* hostname may be NULL */ |
michael@0 | 882 | /* servname may be NULL */ |
michael@0 | 883 | assert(res != NULL); |
michael@0 | 884 | |
michael@0 | 885 | *res = NULL; |
michael@0 | 886 | sentinel.ai_next = NULL; |
michael@0 | 887 | cur = &sentinel; |
michael@0 | 888 | |
michael@0 | 889 | /* |
michael@0 | 890 | * if the servname does not match socktype/protocol, ignore it. |
michael@0 | 891 | */ |
michael@0 | 892 | if (get_portmatch(pai, servname) != 0) |
michael@0 | 893 | return 0; |
michael@0 | 894 | |
michael@0 | 895 | afd = find_afd(pai->ai_family); |
michael@0 | 896 | if (afd == NULL) |
michael@0 | 897 | return 0; |
michael@0 | 898 | |
michael@0 | 899 | switch (afd->a_af) { |
michael@0 | 900 | #if 0 /*X/Open spec*/ |
michael@0 | 901 | case AF_INET: |
michael@0 | 902 | if (inet_aton(hostname, (struct in_addr *)pton) == 1) { |
michael@0 | 903 | if (pai->ai_family == afd->a_af || |
michael@0 | 904 | pai->ai_family == PF_UNSPEC /*?*/) { |
michael@0 | 905 | GET_AI(cur->ai_next, afd, pton); |
michael@0 | 906 | GET_PORT(cur->ai_next, servname); |
michael@0 | 907 | if ((pai->ai_flags & AI_CANONNAME)) { |
michael@0 | 908 | /* |
michael@0 | 909 | * Set the numeric address itself as |
michael@0 | 910 | * the canonical name, based on a |
michael@0 | 911 | * clarification in rfc2553bis-03. |
michael@0 | 912 | */ |
michael@0 | 913 | GET_CANONNAME(cur->ai_next, canonname); |
michael@0 | 914 | } |
michael@0 | 915 | while (cur && cur->ai_next) |
michael@0 | 916 | cur = cur->ai_next; |
michael@0 | 917 | } else |
michael@0 | 918 | ERR(EAI_FAMILY); /*xxx*/ |
michael@0 | 919 | } |
michael@0 | 920 | break; |
michael@0 | 921 | #endif |
michael@0 | 922 | default: |
michael@0 | 923 | if (inet_pton(afd->a_af, hostname, pton) == 1) { |
michael@0 | 924 | if (pai->ai_family == afd->a_af || |
michael@0 | 925 | pai->ai_family == PF_UNSPEC /*?*/) { |
michael@0 | 926 | GET_AI(cur->ai_next, afd, pton); |
michael@0 | 927 | GET_PORT(cur->ai_next, servname); |
michael@0 | 928 | if ((pai->ai_flags & AI_CANONNAME)) { |
michael@0 | 929 | /* |
michael@0 | 930 | * Set the numeric address itself as |
michael@0 | 931 | * the canonical name, based on a |
michael@0 | 932 | * clarification in rfc2553bis-03. |
michael@0 | 933 | */ |
michael@0 | 934 | GET_CANONNAME(cur->ai_next, canonname); |
michael@0 | 935 | } |
michael@0 | 936 | while (cur->ai_next) |
michael@0 | 937 | cur = cur->ai_next; |
michael@0 | 938 | } else |
michael@0 | 939 | ERR(EAI_FAMILY); /*xxx*/ |
michael@0 | 940 | } |
michael@0 | 941 | break; |
michael@0 | 942 | } |
michael@0 | 943 | |
michael@0 | 944 | *res = sentinel.ai_next; |
michael@0 | 945 | return 0; |
michael@0 | 946 | |
michael@0 | 947 | free: |
michael@0 | 948 | bad: |
michael@0 | 949 | if (sentinel.ai_next) |
michael@0 | 950 | __wrap_freeaddrinfo(sentinel.ai_next); |
michael@0 | 951 | return error; |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | /* |
michael@0 | 955 | * numeric hostname with scope |
michael@0 | 956 | */ |
michael@0 | 957 | static int |
michael@0 | 958 | explore_numeric_scope(const struct addrinfo *pai, const char *hostname, |
michael@0 | 959 | const char *servname, struct addrinfo **res) |
michael@0 | 960 | { |
michael@0 | 961 | #if !defined(SCOPE_DELIMITER) || !defined(INET6) |
michael@0 | 962 | return explore_numeric(pai, hostname, servname, res, hostname); |
michael@0 | 963 | #else |
michael@0 | 964 | const struct afd *afd; |
michael@0 | 965 | struct addrinfo *cur; |
michael@0 | 966 | int error; |
michael@0 | 967 | char *cp, *hostname2 = NULL, *scope, *addr; |
michael@0 | 968 | struct sockaddr_in6 *sin6; |
michael@0 | 969 | |
michael@0 | 970 | assert(pai != NULL); |
michael@0 | 971 | /* hostname may be NULL */ |
michael@0 | 972 | /* servname may be NULL */ |
michael@0 | 973 | assert(res != NULL); |
michael@0 | 974 | |
michael@0 | 975 | /* |
michael@0 | 976 | * if the servname does not match socktype/protocol, ignore it. |
michael@0 | 977 | */ |
michael@0 | 978 | if (get_portmatch(pai, servname) != 0) |
michael@0 | 979 | return 0; |
michael@0 | 980 | |
michael@0 | 981 | afd = find_afd(pai->ai_family); |
michael@0 | 982 | if (afd == NULL) |
michael@0 | 983 | return 0; |
michael@0 | 984 | |
michael@0 | 985 | if (!afd->a_scoped) |
michael@0 | 986 | return explore_numeric(pai, hostname, servname, res, hostname); |
michael@0 | 987 | |
michael@0 | 988 | cp = strchr(hostname, SCOPE_DELIMITER); |
michael@0 | 989 | if (cp == NULL) |
michael@0 | 990 | return explore_numeric(pai, hostname, servname, res, hostname); |
michael@0 | 991 | |
michael@0 | 992 | /* |
michael@0 | 993 | * Handle special case of <scoped_address><delimiter><scope id> |
michael@0 | 994 | */ |
michael@0 | 995 | hostname2 = strdup(hostname); |
michael@0 | 996 | if (hostname2 == NULL) |
michael@0 | 997 | return EAI_MEMORY; |
michael@0 | 998 | /* terminate at the delimiter */ |
michael@0 | 999 | hostname2[cp - hostname] = '\0'; |
michael@0 | 1000 | addr = hostname2; |
michael@0 | 1001 | scope = cp + 1; |
michael@0 | 1002 | |
michael@0 | 1003 | error = explore_numeric(pai, addr, servname, res, hostname); |
michael@0 | 1004 | if (error == 0) { |
michael@0 | 1005 | u_int32_t scopeid; |
michael@0 | 1006 | |
michael@0 | 1007 | for (cur = *res; cur; cur = cur->ai_next) { |
michael@0 | 1008 | if (cur->ai_family != AF_INET6) |
michael@0 | 1009 | continue; |
michael@0 | 1010 | sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; |
michael@0 | 1011 | if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { |
michael@0 | 1012 | free(hostname2); |
michael@0 | 1013 | return(EAI_NODATA); /* XXX: is return OK? */ |
michael@0 | 1014 | } |
michael@0 | 1015 | sin6->sin6_scope_id = scopeid; |
michael@0 | 1016 | } |
michael@0 | 1017 | } |
michael@0 | 1018 | |
michael@0 | 1019 | free(hostname2); |
michael@0 | 1020 | |
michael@0 | 1021 | return error; |
michael@0 | 1022 | #endif |
michael@0 | 1023 | } |
michael@0 | 1024 | |
michael@0 | 1025 | static int |
michael@0 | 1026 | get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) |
michael@0 | 1027 | { |
michael@0 | 1028 | |
michael@0 | 1029 | assert(pai != NULL); |
michael@0 | 1030 | assert(ai != NULL); |
michael@0 | 1031 | assert(str != NULL); |
michael@0 | 1032 | |
michael@0 | 1033 | if ((pai->ai_flags & AI_CANONNAME) != 0) { |
michael@0 | 1034 | ai->ai_canonname = strdup(str); |
michael@0 | 1035 | if (ai->ai_canonname == NULL) |
michael@0 | 1036 | return EAI_MEMORY; |
michael@0 | 1037 | } |
michael@0 | 1038 | return 0; |
michael@0 | 1039 | } |
michael@0 | 1040 | |
michael@0 | 1041 | static struct addrinfo * |
michael@0 | 1042 | get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) |
michael@0 | 1043 | { |
michael@0 | 1044 | char *p; |
michael@0 | 1045 | struct addrinfo *ai; |
michael@0 | 1046 | |
michael@0 | 1047 | assert(pai != NULL); |
michael@0 | 1048 | assert(afd != NULL); |
michael@0 | 1049 | assert(addr != NULL); |
michael@0 | 1050 | |
michael@0 | 1051 | ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) |
michael@0 | 1052 | + (afd->a_socklen)); |
michael@0 | 1053 | if (ai == NULL) |
michael@0 | 1054 | return NULL; |
michael@0 | 1055 | |
michael@0 | 1056 | memcpy(ai, pai, sizeof(struct addrinfo)); |
michael@0 | 1057 | ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); |
michael@0 | 1058 | memset(ai->ai_addr, 0, (size_t)afd->a_socklen); |
michael@0 | 1059 | |
michael@0 | 1060 | #ifdef HAVE_SA_LEN |
michael@0 | 1061 | ai->ai_addr->sa_len = afd->a_socklen; |
michael@0 | 1062 | #endif |
michael@0 | 1063 | |
michael@0 | 1064 | ai->ai_addrlen = afd->a_socklen; |
michael@0 | 1065 | #if defined (__alpha__) || (defined(__i386__) && defined(_LP64)) || defined(__sparc64__) |
michael@0 | 1066 | ai->__ai_pad0 = 0; |
michael@0 | 1067 | #endif |
michael@0 | 1068 | ai->ai_addr->sa_family = ai->ai_family = afd->a_af; |
michael@0 | 1069 | p = (char *)(void *)(ai->ai_addr); |
michael@0 | 1070 | memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); |
michael@0 | 1071 | return ai; |
michael@0 | 1072 | } |
michael@0 | 1073 | |
michael@0 | 1074 | static int |
michael@0 | 1075 | get_portmatch(const struct addrinfo *ai, const char *servname) |
michael@0 | 1076 | { |
michael@0 | 1077 | |
michael@0 | 1078 | assert(ai != NULL); |
michael@0 | 1079 | /* servname may be NULL */ |
michael@0 | 1080 | |
michael@0 | 1081 | return get_port(ai, servname, 1); |
michael@0 | 1082 | } |
michael@0 | 1083 | |
michael@0 | 1084 | static int |
michael@0 | 1085 | get_port(const struct addrinfo *ai, const char *servname, int matchonly) |
michael@0 | 1086 | { |
michael@0 | 1087 | const char *proto; |
michael@0 | 1088 | struct servent *sp; |
michael@0 | 1089 | int port; |
michael@0 | 1090 | int allownumeric; |
michael@0 | 1091 | |
michael@0 | 1092 | assert(ai != NULL); |
michael@0 | 1093 | /* servname may be NULL */ |
michael@0 | 1094 | |
michael@0 | 1095 | if (servname == NULL) |
michael@0 | 1096 | return 0; |
michael@0 | 1097 | switch (ai->ai_family) { |
michael@0 | 1098 | case AF_INET: |
michael@0 | 1099 | #ifdef AF_INET6 |
michael@0 | 1100 | case AF_INET6: |
michael@0 | 1101 | #endif |
michael@0 | 1102 | break; |
michael@0 | 1103 | default: |
michael@0 | 1104 | return 0; |
michael@0 | 1105 | } |
michael@0 | 1106 | |
michael@0 | 1107 | switch (ai->ai_socktype) { |
michael@0 | 1108 | case SOCK_RAW: |
michael@0 | 1109 | return EAI_SERVICE; |
michael@0 | 1110 | case SOCK_DGRAM: |
michael@0 | 1111 | case SOCK_STREAM: |
michael@0 | 1112 | allownumeric = 1; |
michael@0 | 1113 | break; |
michael@0 | 1114 | case ANY: |
michael@0 | 1115 | #if 1 /* ANDROID-SPECIFIC CHANGE TO MATCH GLIBC */ |
michael@0 | 1116 | allownumeric = 1; |
michael@0 | 1117 | #else |
michael@0 | 1118 | allownumeric = 0; |
michael@0 | 1119 | #endif |
michael@0 | 1120 | break; |
michael@0 | 1121 | default: |
michael@0 | 1122 | return EAI_SOCKTYPE; |
michael@0 | 1123 | } |
michael@0 | 1124 | |
michael@0 | 1125 | port = str2number(servname); |
michael@0 | 1126 | if (port >= 0) { |
michael@0 | 1127 | if (!allownumeric) |
michael@0 | 1128 | return EAI_SERVICE; |
michael@0 | 1129 | if (port < 0 || port > 65535) |
michael@0 | 1130 | return EAI_SERVICE; |
michael@0 | 1131 | port = htons(port); |
michael@0 | 1132 | } else { |
michael@0 | 1133 | if (ai->ai_flags & AI_NUMERICSERV) |
michael@0 | 1134 | return EAI_NONAME; |
michael@0 | 1135 | |
michael@0 | 1136 | switch (ai->ai_socktype) { |
michael@0 | 1137 | case SOCK_DGRAM: |
michael@0 | 1138 | proto = "udp"; |
michael@0 | 1139 | break; |
michael@0 | 1140 | case SOCK_STREAM: |
michael@0 | 1141 | proto = "tcp"; |
michael@0 | 1142 | break; |
michael@0 | 1143 | default: |
michael@0 | 1144 | proto = NULL; |
michael@0 | 1145 | break; |
michael@0 | 1146 | } |
michael@0 | 1147 | |
michael@0 | 1148 | if ((sp = getservbyname(servname, proto)) == NULL) |
michael@0 | 1149 | return EAI_SERVICE; |
michael@0 | 1150 | port = sp->s_port; |
michael@0 | 1151 | } |
michael@0 | 1152 | |
michael@0 | 1153 | if (!matchonly) { |
michael@0 | 1154 | switch (ai->ai_family) { |
michael@0 | 1155 | case AF_INET: |
michael@0 | 1156 | ((struct sockaddr_in *)(void *) |
michael@0 | 1157 | ai->ai_addr)->sin_port = port; |
michael@0 | 1158 | break; |
michael@0 | 1159 | #ifdef INET6 |
michael@0 | 1160 | case AF_INET6: |
michael@0 | 1161 | ((struct sockaddr_in6 *)(void *) |
michael@0 | 1162 | ai->ai_addr)->sin6_port = port; |
michael@0 | 1163 | break; |
michael@0 | 1164 | #endif |
michael@0 | 1165 | } |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | return 0; |
michael@0 | 1169 | } |
michael@0 | 1170 | |
michael@0 | 1171 | static const struct afd * |
michael@0 | 1172 | find_afd(int af) |
michael@0 | 1173 | { |
michael@0 | 1174 | const struct afd *afd; |
michael@0 | 1175 | |
michael@0 | 1176 | if (af == PF_UNSPEC) |
michael@0 | 1177 | return NULL; |
michael@0 | 1178 | for (afd = afdl; afd->a_af; afd++) { |
michael@0 | 1179 | if (afd->a_af == af) |
michael@0 | 1180 | return afd; |
michael@0 | 1181 | } |
michael@0 | 1182 | return NULL; |
michael@0 | 1183 | } |
michael@0 | 1184 | |
michael@0 | 1185 | #ifdef INET6 |
michael@0 | 1186 | /* convert a string to a scope identifier. XXX: IPv6 specific */ |
michael@0 | 1187 | static int |
michael@0 | 1188 | ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) |
michael@0 | 1189 | { |
michael@0 | 1190 | u_long lscopeid; |
michael@0 | 1191 | struct in6_addr *a6; |
michael@0 | 1192 | char *ep; |
michael@0 | 1193 | |
michael@0 | 1194 | assert(scope != NULL); |
michael@0 | 1195 | assert(sin6 != NULL); |
michael@0 | 1196 | assert(scopeid != NULL); |
michael@0 | 1197 | |
michael@0 | 1198 | a6 = &sin6->sin6_addr; |
michael@0 | 1199 | |
michael@0 | 1200 | /* empty scopeid portion is invalid */ |
michael@0 | 1201 | if (*scope == '\0') |
michael@0 | 1202 | return -1; |
michael@0 | 1203 | |
michael@0 | 1204 | if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { |
michael@0 | 1205 | /* |
michael@0 | 1206 | * We currently assume a one-to-one mapping between links |
michael@0 | 1207 | * and interfaces, so we simply use interface indices for |
michael@0 | 1208 | * like-local scopes. |
michael@0 | 1209 | */ |
michael@0 | 1210 | *scopeid = if_nametoindex(scope); |
michael@0 | 1211 | if (*scopeid == 0) |
michael@0 | 1212 | goto trynumeric; |
michael@0 | 1213 | return 0; |
michael@0 | 1214 | } |
michael@0 | 1215 | |
michael@0 | 1216 | /* still unclear about literal, allow numeric only - placeholder */ |
michael@0 | 1217 | if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) |
michael@0 | 1218 | goto trynumeric; |
michael@0 | 1219 | if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) |
michael@0 | 1220 | goto trynumeric; |
michael@0 | 1221 | else |
michael@0 | 1222 | goto trynumeric; /* global */ |
michael@0 | 1223 | |
michael@0 | 1224 | /* try to convert to a numeric id as a last resort */ |
michael@0 | 1225 | trynumeric: |
michael@0 | 1226 | errno = 0; |
michael@0 | 1227 | lscopeid = strtoul(scope, &ep, 10); |
michael@0 | 1228 | *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); |
michael@0 | 1229 | if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) |
michael@0 | 1230 | return 0; |
michael@0 | 1231 | else |
michael@0 | 1232 | return -1; |
michael@0 | 1233 | } |
michael@0 | 1234 | #endif |
michael@0 | 1235 | |
michael@0 | 1236 | /* code duplicate with gethnamaddr.c */ |
michael@0 | 1237 | |
michael@0 | 1238 | static const char AskedForGot[] = |
michael@0 | 1239 | "gethostby*.getanswer: asked for \"%s\", got \"%s\""; |
michael@0 | 1240 | |
michael@0 | 1241 | static struct addrinfo * |
michael@0 | 1242 | getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, |
michael@0 | 1243 | const struct addrinfo *pai) |
michael@0 | 1244 | { |
michael@0 | 1245 | struct addrinfo sentinel, *cur; |
michael@0 | 1246 | struct addrinfo ai; |
michael@0 | 1247 | const struct afd *afd; |
michael@0 | 1248 | char *canonname; |
michael@0 | 1249 | const HEADER *hp; |
michael@0 | 1250 | const u_char *cp; |
michael@0 | 1251 | int n; |
michael@0 | 1252 | const u_char *eom; |
michael@0 | 1253 | char *bp, *ep; |
michael@0 | 1254 | int type, class, ancount, qdcount; |
michael@0 | 1255 | int haveanswer, had_error; |
michael@0 | 1256 | char tbuf[MAXDNAME]; |
michael@0 | 1257 | int (*name_ok) (const char *); |
michael@0 | 1258 | char hostbuf[8*1024]; |
michael@0 | 1259 | |
michael@0 | 1260 | assert(answer != NULL); |
michael@0 | 1261 | assert(qname != NULL); |
michael@0 | 1262 | assert(pai != NULL); |
michael@0 | 1263 | |
michael@0 | 1264 | memset(&sentinel, 0, sizeof(sentinel)); |
michael@0 | 1265 | cur = &sentinel; |
michael@0 | 1266 | |
michael@0 | 1267 | canonname = NULL; |
michael@0 | 1268 | eom = answer->buf + anslen; |
michael@0 | 1269 | switch (qtype) { |
michael@0 | 1270 | case T_A: |
michael@0 | 1271 | case T_AAAA: |
michael@0 | 1272 | case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ |
michael@0 | 1273 | name_ok = res_hnok; |
michael@0 | 1274 | break; |
michael@0 | 1275 | default: |
michael@0 | 1276 | return NULL; /* XXX should be abort(); */ |
michael@0 | 1277 | } |
michael@0 | 1278 | /* |
michael@0 | 1279 | * find first satisfactory answer |
michael@0 | 1280 | */ |
michael@0 | 1281 | hp = &answer->hdr; |
michael@0 | 1282 | ancount = ntohs(hp->ancount); |
michael@0 | 1283 | qdcount = ntohs(hp->qdcount); |
michael@0 | 1284 | bp = hostbuf; |
michael@0 | 1285 | ep = hostbuf + sizeof hostbuf; |
michael@0 | 1286 | cp = answer->buf + HFIXEDSZ; |
michael@0 | 1287 | if (qdcount != 1) { |
michael@0 | 1288 | h_errno = NO_RECOVERY; |
michael@0 | 1289 | return (NULL); |
michael@0 | 1290 | } |
michael@0 | 1291 | n = dn_expand(answer->buf, eom, cp, bp, ep - bp); |
michael@0 | 1292 | if ((n < 0) || !(*name_ok)(bp)) { |
michael@0 | 1293 | h_errno = NO_RECOVERY; |
michael@0 | 1294 | return (NULL); |
michael@0 | 1295 | } |
michael@0 | 1296 | cp += n + QFIXEDSZ; |
michael@0 | 1297 | if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { |
michael@0 | 1298 | /* res_send() has already verified that the query name is the |
michael@0 | 1299 | * same as the one we sent; this just gets the expanded name |
michael@0 | 1300 | * (i.e., with the succeeding search-domain tacked on). |
michael@0 | 1301 | */ |
michael@0 | 1302 | n = strlen(bp) + 1; /* for the \0 */ |
michael@0 | 1303 | if (n >= MAXHOSTNAMELEN) { |
michael@0 | 1304 | h_errno = NO_RECOVERY; |
michael@0 | 1305 | return (NULL); |
michael@0 | 1306 | } |
michael@0 | 1307 | canonname = bp; |
michael@0 | 1308 | bp += n; |
michael@0 | 1309 | /* The qname can be abbreviated, but h_name is now absolute. */ |
michael@0 | 1310 | qname = canonname; |
michael@0 | 1311 | } |
michael@0 | 1312 | haveanswer = 0; |
michael@0 | 1313 | had_error = 0; |
michael@0 | 1314 | while (ancount-- > 0 && cp < eom && !had_error) { |
michael@0 | 1315 | n = dn_expand(answer->buf, eom, cp, bp, ep - bp); |
michael@0 | 1316 | if ((n < 0) || !(*name_ok)(bp)) { |
michael@0 | 1317 | had_error++; |
michael@0 | 1318 | continue; |
michael@0 | 1319 | } |
michael@0 | 1320 | cp += n; /* name */ |
michael@0 | 1321 | type = _getshort(cp); |
michael@0 | 1322 | cp += INT16SZ; /* type */ |
michael@0 | 1323 | class = _getshort(cp); |
michael@0 | 1324 | cp += INT16SZ + INT32SZ; /* class, TTL */ |
michael@0 | 1325 | n = _getshort(cp); |
michael@0 | 1326 | cp += INT16SZ; /* len */ |
michael@0 | 1327 | if (class != C_IN) { |
michael@0 | 1328 | /* XXX - debug? syslog? */ |
michael@0 | 1329 | cp += n; |
michael@0 | 1330 | continue; /* XXX - had_error++ ? */ |
michael@0 | 1331 | } |
michael@0 | 1332 | if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && |
michael@0 | 1333 | type == T_CNAME) { |
michael@0 | 1334 | n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); |
michael@0 | 1335 | if ((n < 0) || !(*name_ok)(tbuf)) { |
michael@0 | 1336 | had_error++; |
michael@0 | 1337 | continue; |
michael@0 | 1338 | } |
michael@0 | 1339 | cp += n; |
michael@0 | 1340 | /* Get canonical name. */ |
michael@0 | 1341 | n = strlen(tbuf) + 1; /* for the \0 */ |
michael@0 | 1342 | if (n > ep - bp || n >= MAXHOSTNAMELEN) { |
michael@0 | 1343 | had_error++; |
michael@0 | 1344 | continue; |
michael@0 | 1345 | } |
michael@0 | 1346 | strlcpy(bp, tbuf, (size_t)(ep - bp)); |
michael@0 | 1347 | canonname = bp; |
michael@0 | 1348 | bp += n; |
michael@0 | 1349 | continue; |
michael@0 | 1350 | } |
michael@0 | 1351 | if (qtype == T_ANY) { |
michael@0 | 1352 | if (!(type == T_A || type == T_AAAA)) { |
michael@0 | 1353 | cp += n; |
michael@0 | 1354 | continue; |
michael@0 | 1355 | } |
michael@0 | 1356 | } else if (type != qtype) { |
michael@0 | 1357 | if (type != T_KEY && type != T_SIG) |
michael@0 | 1358 | syslog(LOG_NOTICE|LOG_AUTH, |
michael@0 | 1359 | "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", |
michael@0 | 1360 | qname, p_class(C_IN), p_type(qtype), |
michael@0 | 1361 | p_type(type)); |
michael@0 | 1362 | cp += n; |
michael@0 | 1363 | continue; /* XXX - had_error++ ? */ |
michael@0 | 1364 | } |
michael@0 | 1365 | switch (type) { |
michael@0 | 1366 | case T_A: |
michael@0 | 1367 | case T_AAAA: |
michael@0 | 1368 | if (strcasecmp(canonname, bp) != 0) { |
michael@0 | 1369 | syslog(LOG_NOTICE|LOG_AUTH, |
michael@0 | 1370 | AskedForGot, canonname, bp); |
michael@0 | 1371 | cp += n; |
michael@0 | 1372 | continue; /* XXX - had_error++ ? */ |
michael@0 | 1373 | } |
michael@0 | 1374 | if (type == T_A && n != INADDRSZ) { |
michael@0 | 1375 | cp += n; |
michael@0 | 1376 | continue; |
michael@0 | 1377 | } |
michael@0 | 1378 | if (type == T_AAAA && n != IN6ADDRSZ) { |
michael@0 | 1379 | cp += n; |
michael@0 | 1380 | continue; |
michael@0 | 1381 | } |
michael@0 | 1382 | if (type == T_AAAA) { |
michael@0 | 1383 | struct in6_addr in6; |
michael@0 | 1384 | memcpy(&in6, cp, IN6ADDRSZ); |
michael@0 | 1385 | if (IN6_IS_ADDR_V4MAPPED(&in6)) { |
michael@0 | 1386 | cp += n; |
michael@0 | 1387 | continue; |
michael@0 | 1388 | } |
michael@0 | 1389 | } |
michael@0 | 1390 | if (!haveanswer) { |
michael@0 | 1391 | int nn; |
michael@0 | 1392 | |
michael@0 | 1393 | canonname = bp; |
michael@0 | 1394 | nn = strlen(bp) + 1; /* for the \0 */ |
michael@0 | 1395 | bp += nn; |
michael@0 | 1396 | } |
michael@0 | 1397 | |
michael@0 | 1398 | /* don't overwrite pai */ |
michael@0 | 1399 | ai = *pai; |
michael@0 | 1400 | ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; |
michael@0 | 1401 | afd = find_afd(ai.ai_family); |
michael@0 | 1402 | if (afd == NULL) { |
michael@0 | 1403 | cp += n; |
michael@0 | 1404 | continue; |
michael@0 | 1405 | } |
michael@0 | 1406 | cur->ai_next = get_ai(&ai, afd, (const char *)cp); |
michael@0 | 1407 | if (cur->ai_next == NULL) |
michael@0 | 1408 | had_error++; |
michael@0 | 1409 | while (cur && cur->ai_next) |
michael@0 | 1410 | cur = cur->ai_next; |
michael@0 | 1411 | cp += n; |
michael@0 | 1412 | break; |
michael@0 | 1413 | default: |
michael@0 | 1414 | abort(); |
michael@0 | 1415 | } |
michael@0 | 1416 | if (!had_error) |
michael@0 | 1417 | haveanswer++; |
michael@0 | 1418 | } |
michael@0 | 1419 | if (haveanswer) { |
michael@0 | 1420 | if (!canonname) |
michael@0 | 1421 | (void)get_canonname(pai, sentinel.ai_next, qname); |
michael@0 | 1422 | else |
michael@0 | 1423 | (void)get_canonname(pai, sentinel.ai_next, canonname); |
michael@0 | 1424 | h_errno = NETDB_SUCCESS; |
michael@0 | 1425 | return sentinel.ai_next; |
michael@0 | 1426 | } |
michael@0 | 1427 | |
michael@0 | 1428 | h_errno = NO_RECOVERY; |
michael@0 | 1429 | return NULL; |
michael@0 | 1430 | } |
michael@0 | 1431 | |
michael@0 | 1432 | struct addrinfo_sort_elem { |
michael@0 | 1433 | struct addrinfo *ai; |
michael@0 | 1434 | int has_src_addr; |
michael@0 | 1435 | sockaddr_union src_addr; |
michael@0 | 1436 | int original_order; |
michael@0 | 1437 | }; |
michael@0 | 1438 | |
michael@0 | 1439 | /*ARGSUSED*/ |
michael@0 | 1440 | static int |
michael@0 | 1441 | _get_scope(const struct sockaddr *addr) |
michael@0 | 1442 | { |
michael@0 | 1443 | if (addr->sa_family == AF_INET6) { |
michael@0 | 1444 | const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; |
michael@0 | 1445 | if (IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) { |
michael@0 | 1446 | return IPV6_ADDR_MC_SCOPE(&addr6->sin6_addr); |
michael@0 | 1447 | } else if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr) || |
michael@0 | 1448 | IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) { |
michael@0 | 1449 | /* |
michael@0 | 1450 | * RFC 4291 section 2.5.3 says loopback is to be treated as having |
michael@0 | 1451 | * link-local scope. |
michael@0 | 1452 | */ |
michael@0 | 1453 | return IPV6_ADDR_SCOPE_LINKLOCAL; |
michael@0 | 1454 | } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) { |
michael@0 | 1455 | return IPV6_ADDR_SCOPE_SITELOCAL; |
michael@0 | 1456 | } else { |
michael@0 | 1457 | return IPV6_ADDR_SCOPE_GLOBAL; |
michael@0 | 1458 | } |
michael@0 | 1459 | } else if (addr->sa_family == AF_INET) { |
michael@0 | 1460 | const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; |
michael@0 | 1461 | unsigned long int na = ntohl(addr4->sin_addr.s_addr); |
michael@0 | 1462 | |
michael@0 | 1463 | if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */ |
michael@0 | 1464 | (na & 0xffff0000) == 0xa9fe0000) { /* 169.254.0.0/16 */ |
michael@0 | 1465 | return IPV6_ADDR_SCOPE_LINKLOCAL; |
michael@0 | 1466 | } else { |
michael@0 | 1467 | /* |
michael@0 | 1468 | * According to draft-ietf-6man-rfc3484-revise-01 section 2.3, |
michael@0 | 1469 | * it is best not to treat the private IPv4 ranges |
michael@0 | 1470 | * (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16) as being |
michael@0 | 1471 | * in a special scope, so we don't. |
michael@0 | 1472 | */ |
michael@0 | 1473 | return IPV6_ADDR_SCOPE_GLOBAL; |
michael@0 | 1474 | } |
michael@0 | 1475 | } else { |
michael@0 | 1476 | /* |
michael@0 | 1477 | * This should never happen. |
michael@0 | 1478 | * Return a scope with low priority as a last resort. |
michael@0 | 1479 | */ |
michael@0 | 1480 | return IPV6_ADDR_SCOPE_NODELOCAL; |
michael@0 | 1481 | } |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | /* These macros are modelled after the ones in <netinet/in6.h>. */ |
michael@0 | 1485 | |
michael@0 | 1486 | /* RFC 4380, section 2.6 */ |
michael@0 | 1487 | #define IN6_IS_ADDR_TEREDO(a) \ |
michael@0 | 1488 | ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == ntohl(0x20010000))) |
michael@0 | 1489 | |
michael@0 | 1490 | /* RFC 3056, section 2. */ |
michael@0 | 1491 | #define IN6_IS_ADDR_6TO4(a) \ |
michael@0 | 1492 | (((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02)) |
michael@0 | 1493 | |
michael@0 | 1494 | /* 6bone testing address area (3ffe::/16), deprecated in RFC 3701. */ |
michael@0 | 1495 | #define IN6_IS_ADDR_6BONE(a) \ |
michael@0 | 1496 | (((a)->s6_addr[0] == 0x3f) && ((a)->s6_addr[1] == 0xfe)) |
michael@0 | 1497 | |
michael@0 | 1498 | /* |
michael@0 | 1499 | * Get the label for a given IPv4/IPv6 address. |
michael@0 | 1500 | * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01. |
michael@0 | 1501 | */ |
michael@0 | 1502 | |
michael@0 | 1503 | /*ARGSUSED*/ |
michael@0 | 1504 | static int |
michael@0 | 1505 | _get_label(const struct sockaddr *addr) |
michael@0 | 1506 | { |
michael@0 | 1507 | if (addr->sa_family == AF_INET) { |
michael@0 | 1508 | return 3; |
michael@0 | 1509 | } else if (addr->sa_family == AF_INET6) { |
michael@0 | 1510 | const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; |
michael@0 | 1511 | if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { |
michael@0 | 1512 | return 0; |
michael@0 | 1513 | } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) { |
michael@0 | 1514 | return 3; |
michael@0 | 1515 | } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { |
michael@0 | 1516 | return 4; |
michael@0 | 1517 | } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { |
michael@0 | 1518 | return 5; |
michael@0 | 1519 | } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { |
michael@0 | 1520 | return 10; |
michael@0 | 1521 | } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) { |
michael@0 | 1522 | return 11; |
michael@0 | 1523 | } else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) { |
michael@0 | 1524 | return 12; |
michael@0 | 1525 | } else { |
michael@0 | 1526 | return 2; |
michael@0 | 1527 | } |
michael@0 | 1528 | } else { |
michael@0 | 1529 | /* |
michael@0 | 1530 | * This should never happen. |
michael@0 | 1531 | * Return a semi-random label as a last resort. |
michael@0 | 1532 | */ |
michael@0 | 1533 | return 1; |
michael@0 | 1534 | } |
michael@0 | 1535 | } |
michael@0 | 1536 | |
michael@0 | 1537 | /* |
michael@0 | 1538 | * Get the precedence for a given IPv4/IPv6 address. |
michael@0 | 1539 | * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01. |
michael@0 | 1540 | */ |
michael@0 | 1541 | |
michael@0 | 1542 | /*ARGSUSED*/ |
michael@0 | 1543 | static int |
michael@0 | 1544 | _get_precedence(const struct sockaddr *addr) |
michael@0 | 1545 | { |
michael@0 | 1546 | if (addr->sa_family == AF_INET) { |
michael@0 | 1547 | return 30; |
michael@0 | 1548 | } else if (addr->sa_family == AF_INET6) { |
michael@0 | 1549 | const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; |
michael@0 | 1550 | if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { |
michael@0 | 1551 | return 60; |
michael@0 | 1552 | } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) { |
michael@0 | 1553 | return 30; |
michael@0 | 1554 | } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { |
michael@0 | 1555 | return 20; |
michael@0 | 1556 | } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { |
michael@0 | 1557 | return 10; |
michael@0 | 1558 | } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr) || |
michael@0 | 1559 | IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) || |
michael@0 | 1560 | IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) { |
michael@0 | 1561 | return 1; |
michael@0 | 1562 | } else { |
michael@0 | 1563 | return 40; |
michael@0 | 1564 | } |
michael@0 | 1565 | } else { |
michael@0 | 1566 | return 1; |
michael@0 | 1567 | } |
michael@0 | 1568 | } |
michael@0 | 1569 | |
michael@0 | 1570 | /* |
michael@0 | 1571 | * Find number of matching initial bits between the two addresses a1 and a2. |
michael@0 | 1572 | */ |
michael@0 | 1573 | |
michael@0 | 1574 | /*ARGSUSED*/ |
michael@0 | 1575 | static int |
michael@0 | 1576 | _common_prefix_len(const struct in6_addr *a1, const struct in6_addr *a2) |
michael@0 | 1577 | { |
michael@0 | 1578 | const char *p1 = (const char *)a1; |
michael@0 | 1579 | const char *p2 = (const char *)a2; |
michael@0 | 1580 | unsigned i; |
michael@0 | 1581 | |
michael@0 | 1582 | for (i = 0; i < sizeof(*a1); ++i) { |
michael@0 | 1583 | int x, j; |
michael@0 | 1584 | |
michael@0 | 1585 | if (p1[i] == p2[i]) { |
michael@0 | 1586 | continue; |
michael@0 | 1587 | } |
michael@0 | 1588 | x = p1[i] ^ p2[i]; |
michael@0 | 1589 | for (j = 0; j < CHAR_BIT; ++j) { |
michael@0 | 1590 | if (x & (1 << (CHAR_BIT - 1))) { |
michael@0 | 1591 | return i * CHAR_BIT + j; |
michael@0 | 1592 | } |
michael@0 | 1593 | x <<= 1; |
michael@0 | 1594 | } |
michael@0 | 1595 | } |
michael@0 | 1596 | return sizeof(*a1) * CHAR_BIT; |
michael@0 | 1597 | } |
michael@0 | 1598 | |
michael@0 | 1599 | /* |
michael@0 | 1600 | * Compare two source/destination address pairs. |
michael@0 | 1601 | * RFC 3484, section 6. |
michael@0 | 1602 | */ |
michael@0 | 1603 | |
michael@0 | 1604 | /*ARGSUSED*/ |
michael@0 | 1605 | static int |
michael@0 | 1606 | _rfc3484_compare(const void *ptr1, const void* ptr2) |
michael@0 | 1607 | { |
michael@0 | 1608 | const struct addrinfo_sort_elem *a1 = (const struct addrinfo_sort_elem *)ptr1; |
michael@0 | 1609 | const struct addrinfo_sort_elem *a2 = (const struct addrinfo_sort_elem *)ptr2; |
michael@0 | 1610 | int scope_src1, scope_dst1, scope_match1; |
michael@0 | 1611 | int scope_src2, scope_dst2, scope_match2; |
michael@0 | 1612 | int label_src1, label_dst1, label_match1; |
michael@0 | 1613 | int label_src2, label_dst2, label_match2; |
michael@0 | 1614 | int precedence1, precedence2; |
michael@0 | 1615 | int prefixlen1, prefixlen2; |
michael@0 | 1616 | |
michael@0 | 1617 | /* Rule 1: Avoid unusable destinations. */ |
michael@0 | 1618 | if (a1->has_src_addr != a2->has_src_addr) { |
michael@0 | 1619 | return a2->has_src_addr - a1->has_src_addr; |
michael@0 | 1620 | } |
michael@0 | 1621 | |
michael@0 | 1622 | /* Rule 2: Prefer matching scope. */ |
michael@0 | 1623 | scope_src1 = _get_scope(&a1->src_addr.generic); |
michael@0 | 1624 | scope_dst1 = _get_scope(a1->ai->ai_addr); |
michael@0 | 1625 | scope_match1 = (scope_src1 == scope_dst1); |
michael@0 | 1626 | |
michael@0 | 1627 | scope_src2 = _get_scope(&a2->src_addr.generic); |
michael@0 | 1628 | scope_dst2 = _get_scope(a2->ai->ai_addr); |
michael@0 | 1629 | scope_match2 = (scope_src2 == scope_dst2); |
michael@0 | 1630 | |
michael@0 | 1631 | if (scope_match1 != scope_match2) { |
michael@0 | 1632 | return scope_match2 - scope_match1; |
michael@0 | 1633 | } |
michael@0 | 1634 | |
michael@0 | 1635 | /* |
michael@0 | 1636 | * Rule 3: Avoid deprecated addresses. |
michael@0 | 1637 | * TODO(sesse): We don't currently have a good way of finding this. |
michael@0 | 1638 | */ |
michael@0 | 1639 | |
michael@0 | 1640 | /* |
michael@0 | 1641 | * Rule 4: Prefer home addresses. |
michael@0 | 1642 | * TODO(sesse): We don't currently have a good way of finding this. |
michael@0 | 1643 | */ |
michael@0 | 1644 | |
michael@0 | 1645 | /* Rule 5: Prefer matching label. */ |
michael@0 | 1646 | label_src1 = _get_label(&a1->src_addr.generic); |
michael@0 | 1647 | label_dst1 = _get_label(a1->ai->ai_addr); |
michael@0 | 1648 | label_match1 = (label_src1 == label_dst1); |
michael@0 | 1649 | |
michael@0 | 1650 | label_src2 = _get_label(&a2->src_addr.generic); |
michael@0 | 1651 | label_dst2 = _get_label(a2->ai->ai_addr); |
michael@0 | 1652 | label_match2 = (label_src2 == label_dst2); |
michael@0 | 1653 | |
michael@0 | 1654 | if (label_match1 != label_match2) { |
michael@0 | 1655 | return label_match2 - label_match1; |
michael@0 | 1656 | } |
michael@0 | 1657 | |
michael@0 | 1658 | /* Rule 6: Prefer higher precedence. */ |
michael@0 | 1659 | precedence1 = _get_precedence(a1->ai->ai_addr); |
michael@0 | 1660 | precedence2 = _get_precedence(a2->ai->ai_addr); |
michael@0 | 1661 | if (precedence1 != precedence2) { |
michael@0 | 1662 | return precedence2 - precedence1; |
michael@0 | 1663 | } |
michael@0 | 1664 | |
michael@0 | 1665 | /* |
michael@0 | 1666 | * Rule 7: Prefer native transport. |
michael@0 | 1667 | * TODO(sesse): We don't currently have a good way of finding this. |
michael@0 | 1668 | */ |
michael@0 | 1669 | |
michael@0 | 1670 | /* Rule 8: Prefer smaller scope. */ |
michael@0 | 1671 | if (scope_dst1 != scope_dst2) { |
michael@0 | 1672 | return scope_dst1 - scope_dst2; |
michael@0 | 1673 | } |
michael@0 | 1674 | |
michael@0 | 1675 | /* |
michael@0 | 1676 | * Rule 9: Use longest matching prefix. |
michael@0 | 1677 | * We implement this for IPv6 only, as the rules in RFC 3484 don't seem |
michael@0 | 1678 | * to work very well directly applied to IPv4. (glibc uses information from |
michael@0 | 1679 | * the routing table for a custom IPv4 implementation here.) |
michael@0 | 1680 | */ |
michael@0 | 1681 | if (a1->has_src_addr && a1->ai->ai_addr->sa_family == AF_INET6 && |
michael@0 | 1682 | a2->has_src_addr && a2->ai->ai_addr->sa_family == AF_INET6) { |
michael@0 | 1683 | const struct sockaddr_in6 *a1_src = &a1->src_addr.in6; |
michael@0 | 1684 | const struct sockaddr_in6 *a1_dst = (const struct sockaddr_in6 *)a1->ai->ai_addr; |
michael@0 | 1685 | const struct sockaddr_in6 *a2_src = &a2->src_addr.in6; |
michael@0 | 1686 | const struct sockaddr_in6 *a2_dst = (const struct sockaddr_in6 *)a2->ai->ai_addr; |
michael@0 | 1687 | prefixlen1 = _common_prefix_len(&a1_src->sin6_addr, &a1_dst->sin6_addr); |
michael@0 | 1688 | prefixlen2 = _common_prefix_len(&a2_src->sin6_addr, &a2_dst->sin6_addr); |
michael@0 | 1689 | if (prefixlen1 != prefixlen2) { |
michael@0 | 1690 | return prefixlen2 - prefixlen1; |
michael@0 | 1691 | } |
michael@0 | 1692 | } |
michael@0 | 1693 | |
michael@0 | 1694 | /* |
michael@0 | 1695 | * Rule 10: Leave the order unchanged. |
michael@0 | 1696 | * We need this since qsort() is not necessarily stable. |
michael@0 | 1697 | */ |
michael@0 | 1698 | return a1->original_order - a2->original_order; |
michael@0 | 1699 | } |
michael@0 | 1700 | |
michael@0 | 1701 | /* |
michael@0 | 1702 | * Find the source address that will be used if trying to connect to the given |
michael@0 | 1703 | * address. src_addr must be large enough to hold a struct sockaddr_in6. |
michael@0 | 1704 | * |
michael@0 | 1705 | * Returns 1 if a source address was found, 0 if the address is unreachable, |
michael@0 | 1706 | * and -1 if a fatal error occurred. If 0 or 1, the contents of src_addr are |
michael@0 | 1707 | * undefined. |
michael@0 | 1708 | */ |
michael@0 | 1709 | |
michael@0 | 1710 | /*ARGSUSED*/ |
michael@0 | 1711 | static int |
michael@0 | 1712 | _find_src_addr(const struct sockaddr *addr, struct sockaddr *src_addr) |
michael@0 | 1713 | { |
michael@0 | 1714 | int sock; |
michael@0 | 1715 | int ret; |
michael@0 | 1716 | socklen_t len; |
michael@0 | 1717 | |
michael@0 | 1718 | switch (addr->sa_family) { |
michael@0 | 1719 | case AF_INET: |
michael@0 | 1720 | len = sizeof(struct sockaddr_in); |
michael@0 | 1721 | break; |
michael@0 | 1722 | case AF_INET6: |
michael@0 | 1723 | len = sizeof(struct sockaddr_in6); |
michael@0 | 1724 | break; |
michael@0 | 1725 | default: |
michael@0 | 1726 | /* No known usable source address for non-INET families. */ |
michael@0 | 1727 | return 0; |
michael@0 | 1728 | } |
michael@0 | 1729 | |
michael@0 | 1730 | sock = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); |
michael@0 | 1731 | if (sock == -1) { |
michael@0 | 1732 | if (errno == EAFNOSUPPORT) { |
michael@0 | 1733 | return 0; |
michael@0 | 1734 | } else { |
michael@0 | 1735 | return -1; |
michael@0 | 1736 | } |
michael@0 | 1737 | } |
michael@0 | 1738 | |
michael@0 | 1739 | do { |
michael@0 | 1740 | ret = connect(sock, addr, len); |
michael@0 | 1741 | } while (ret == -1 && errno == EINTR); |
michael@0 | 1742 | |
michael@0 | 1743 | if (ret == -1) { |
michael@0 | 1744 | close(sock); |
michael@0 | 1745 | return 0; |
michael@0 | 1746 | } |
michael@0 | 1747 | |
michael@0 | 1748 | if (getsockname(sock, src_addr, &len) == -1) { |
michael@0 | 1749 | close(sock); |
michael@0 | 1750 | return -1; |
michael@0 | 1751 | } |
michael@0 | 1752 | close(sock); |
michael@0 | 1753 | return 1; |
michael@0 | 1754 | } |
michael@0 | 1755 | |
michael@0 | 1756 | /* |
michael@0 | 1757 | * Sort the linked list starting at sentinel->ai_next in RFC3484 order. |
michael@0 | 1758 | * Will leave the list unchanged if an error occurs. |
michael@0 | 1759 | */ |
michael@0 | 1760 | |
michael@0 | 1761 | /*ARGSUSED*/ |
michael@0 | 1762 | static void |
michael@0 | 1763 | _rfc3484_sort(struct addrinfo *list_sentinel) |
michael@0 | 1764 | { |
michael@0 | 1765 | struct addrinfo *cur; |
michael@0 | 1766 | int nelem = 0, i; |
michael@0 | 1767 | struct addrinfo_sort_elem *elems; |
michael@0 | 1768 | |
michael@0 | 1769 | cur = list_sentinel->ai_next; |
michael@0 | 1770 | while (cur) { |
michael@0 | 1771 | ++nelem; |
michael@0 | 1772 | cur = cur->ai_next; |
michael@0 | 1773 | } |
michael@0 | 1774 | |
michael@0 | 1775 | elems = (struct addrinfo_sort_elem *)malloc(nelem * sizeof(struct addrinfo_sort_elem)); |
michael@0 | 1776 | if (elems == NULL) { |
michael@0 | 1777 | goto error; |
michael@0 | 1778 | } |
michael@0 | 1779 | |
michael@0 | 1780 | /* |
michael@0 | 1781 | * Convert the linked list to an array that also contains the candidate |
michael@0 | 1782 | * source address for each destination address. |
michael@0 | 1783 | */ |
michael@0 | 1784 | for (i = 0, cur = list_sentinel->ai_next; i < nelem; ++i, cur = cur->ai_next) { |
michael@0 | 1785 | int has_src_addr; |
michael@0 | 1786 | assert(cur != NULL); |
michael@0 | 1787 | elems[i].ai = cur; |
michael@0 | 1788 | elems[i].original_order = i; |
michael@0 | 1789 | |
michael@0 | 1790 | has_src_addr = _find_src_addr(cur->ai_addr, &elems[i].src_addr.generic); |
michael@0 | 1791 | if (has_src_addr == -1) { |
michael@0 | 1792 | goto error; |
michael@0 | 1793 | } |
michael@0 | 1794 | elems[i].has_src_addr = has_src_addr; |
michael@0 | 1795 | } |
michael@0 | 1796 | |
michael@0 | 1797 | /* Sort the addresses, and rearrange the linked list so it matches the sorted order. */ |
michael@0 | 1798 | qsort((void *)elems, nelem, sizeof(struct addrinfo_sort_elem), _rfc3484_compare); |
michael@0 | 1799 | |
michael@0 | 1800 | list_sentinel->ai_next = elems[0].ai; |
michael@0 | 1801 | for (i = 0; i < nelem - 1; ++i) { |
michael@0 | 1802 | elems[i].ai->ai_next = elems[i + 1].ai; |
michael@0 | 1803 | } |
michael@0 | 1804 | elems[nelem - 1].ai->ai_next = NULL; |
michael@0 | 1805 | |
michael@0 | 1806 | error: |
michael@0 | 1807 | free(elems); |
michael@0 | 1808 | } |
michael@0 | 1809 | |
michael@0 | 1810 | /*ARGSUSED*/ |
michael@0 | 1811 | static int |
michael@0 | 1812 | _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) |
michael@0 | 1813 | { |
michael@0 | 1814 | struct addrinfo *ai; |
michael@0 | 1815 | querybuf *buf, *buf2; |
michael@0 | 1816 | const char *name; |
michael@0 | 1817 | const struct addrinfo *pai; |
michael@0 | 1818 | struct addrinfo sentinel, *cur; |
michael@0 | 1819 | struct res_target q, q2; |
michael@0 | 1820 | res_state res; |
michael@0 | 1821 | |
michael@0 | 1822 | name = va_arg(ap, char *); |
michael@0 | 1823 | pai = va_arg(ap, const struct addrinfo *); |
michael@0 | 1824 | //fprintf(stderr, "_dns_getaddrinfo() name = '%s'\n", name); |
michael@0 | 1825 | |
michael@0 | 1826 | memset(&q, 0, sizeof(q)); |
michael@0 | 1827 | memset(&q2, 0, sizeof(q2)); |
michael@0 | 1828 | memset(&sentinel, 0, sizeof(sentinel)); |
michael@0 | 1829 | cur = &sentinel; |
michael@0 | 1830 | |
michael@0 | 1831 | buf = malloc(sizeof(*buf)); |
michael@0 | 1832 | if (buf == NULL) { |
michael@0 | 1833 | h_errno = NETDB_INTERNAL; |
michael@0 | 1834 | return NS_NOTFOUND; |
michael@0 | 1835 | } |
michael@0 | 1836 | buf2 = malloc(sizeof(*buf2)); |
michael@0 | 1837 | if (buf2 == NULL) { |
michael@0 | 1838 | free(buf); |
michael@0 | 1839 | h_errno = NETDB_INTERNAL; |
michael@0 | 1840 | return NS_NOTFOUND; |
michael@0 | 1841 | } |
michael@0 | 1842 | |
michael@0 | 1843 | switch (pai->ai_family) { |
michael@0 | 1844 | case AF_UNSPEC: |
michael@0 | 1845 | /* prefer IPv6 */ |
michael@0 | 1846 | q.name = name; |
michael@0 | 1847 | q.qclass = C_IN; |
michael@0 | 1848 | q.answer = buf->buf; |
michael@0 | 1849 | q.anslen = sizeof(buf->buf); |
michael@0 | 1850 | int query_ipv6 = 1, query_ipv4 = 1; |
michael@0 | 1851 | if (pai->ai_flags & AI_ADDRCONFIG) { |
michael@0 | 1852 | query_ipv6 = _have_ipv6(); |
michael@0 | 1853 | query_ipv4 = _have_ipv4(); |
michael@0 | 1854 | } |
michael@0 | 1855 | if (query_ipv6) { |
michael@0 | 1856 | q.qtype = T_AAAA; |
michael@0 | 1857 | if (query_ipv4) { |
michael@0 | 1858 | q.next = &q2; |
michael@0 | 1859 | q2.name = name; |
michael@0 | 1860 | q2.qclass = C_IN; |
michael@0 | 1861 | q2.qtype = T_A; |
michael@0 | 1862 | q2.answer = buf2->buf; |
michael@0 | 1863 | q2.anslen = sizeof(buf2->buf); |
michael@0 | 1864 | } |
michael@0 | 1865 | } else if (query_ipv4) { |
michael@0 | 1866 | q.qtype = T_A; |
michael@0 | 1867 | } else { |
michael@0 | 1868 | free(buf); |
michael@0 | 1869 | free(buf2); |
michael@0 | 1870 | return NS_NOTFOUND; |
michael@0 | 1871 | } |
michael@0 | 1872 | break; |
michael@0 | 1873 | case AF_INET: |
michael@0 | 1874 | q.name = name; |
michael@0 | 1875 | q.qclass = C_IN; |
michael@0 | 1876 | q.qtype = T_A; |
michael@0 | 1877 | q.answer = buf->buf; |
michael@0 | 1878 | q.anslen = sizeof(buf->buf); |
michael@0 | 1879 | break; |
michael@0 | 1880 | case AF_INET6: |
michael@0 | 1881 | q.name = name; |
michael@0 | 1882 | q.qclass = C_IN; |
michael@0 | 1883 | q.qtype = T_AAAA; |
michael@0 | 1884 | q.answer = buf->buf; |
michael@0 | 1885 | q.anslen = sizeof(buf->buf); |
michael@0 | 1886 | break; |
michael@0 | 1887 | default: |
michael@0 | 1888 | free(buf); |
michael@0 | 1889 | free(buf2); |
michael@0 | 1890 | return NS_UNAVAIL; |
michael@0 | 1891 | } |
michael@0 | 1892 | |
michael@0 | 1893 | res = __res_get_state(); |
michael@0 | 1894 | if (res == NULL) { |
michael@0 | 1895 | free(buf); |
michael@0 | 1896 | free(buf2); |
michael@0 | 1897 | return NS_NOTFOUND; |
michael@0 | 1898 | } |
michael@0 | 1899 | |
michael@0 | 1900 | if (res_searchN(name, &q, res) < 0) { |
michael@0 | 1901 | __res_put_state(res); |
michael@0 | 1902 | free(buf); |
michael@0 | 1903 | free(buf2); |
michael@0 | 1904 | return NS_NOTFOUND; |
michael@0 | 1905 | } |
michael@0 | 1906 | ai = getanswer(buf, q.n, q.name, q.qtype, pai); |
michael@0 | 1907 | if (ai) { |
michael@0 | 1908 | cur->ai_next = ai; |
michael@0 | 1909 | while (cur && cur->ai_next) |
michael@0 | 1910 | cur = cur->ai_next; |
michael@0 | 1911 | } |
michael@0 | 1912 | if (q.next) { |
michael@0 | 1913 | ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai); |
michael@0 | 1914 | if (ai) |
michael@0 | 1915 | cur->ai_next = ai; |
michael@0 | 1916 | } |
michael@0 | 1917 | free(buf); |
michael@0 | 1918 | free(buf2); |
michael@0 | 1919 | if (sentinel.ai_next == NULL) { |
michael@0 | 1920 | __res_put_state(res); |
michael@0 | 1921 | switch (h_errno) { |
michael@0 | 1922 | case HOST_NOT_FOUND: |
michael@0 | 1923 | return NS_NOTFOUND; |
michael@0 | 1924 | case TRY_AGAIN: |
michael@0 | 1925 | return NS_TRYAGAIN; |
michael@0 | 1926 | default: |
michael@0 | 1927 | return NS_UNAVAIL; |
michael@0 | 1928 | } |
michael@0 | 1929 | } |
michael@0 | 1930 | |
michael@0 | 1931 | _rfc3484_sort(&sentinel); |
michael@0 | 1932 | |
michael@0 | 1933 | __res_put_state(res); |
michael@0 | 1934 | |
michael@0 | 1935 | *((struct addrinfo **)rv) = sentinel.ai_next; |
michael@0 | 1936 | return NS_SUCCESS; |
michael@0 | 1937 | } |
michael@0 | 1938 | |
michael@0 | 1939 | static void |
michael@0 | 1940 | _sethtent(_pseudo_FILE * __restrict__ hostf) |
michael@0 | 1941 | { |
michael@0 | 1942 | assert(hostf); |
michael@0 | 1943 | if (hostf->mapping == MAP_FAILED) |
michael@0 | 1944 | (void) _pseudo_fopen_r(hostf, _PATH_HOSTS); |
michael@0 | 1945 | else |
michael@0 | 1946 | _pseudo_rewind(hostf); |
michael@0 | 1947 | } |
michael@0 | 1948 | |
michael@0 | 1949 | static void |
michael@0 | 1950 | _endhtent(_pseudo_FILE * __restrict__ hostf) |
michael@0 | 1951 | { |
michael@0 | 1952 | assert(hostf); |
michael@0 | 1953 | (void) _pseudo_fclose(hostf); |
michael@0 | 1954 | } |
michael@0 | 1955 | |
michael@0 | 1956 | static struct addrinfo * |
michael@0 | 1957 | _gethtent(_pseudo_FILE * __restrict__ hostf, const char *name, const struct addrinfo *pai) |
michael@0 | 1958 | { |
michael@0 | 1959 | char *p; |
michael@0 | 1960 | char *cp, *tname, *cname; |
michael@0 | 1961 | struct addrinfo hints, *res0, *res; |
michael@0 | 1962 | int error; |
michael@0 | 1963 | const char *addr; |
michael@0 | 1964 | char hostbuf[8*1024]; |
michael@0 | 1965 | |
michael@0 | 1966 | assert(hostf); |
michael@0 | 1967 | // fprintf(stderr, "_gethtent() name = '%s'\n", name); |
michael@0 | 1968 | assert(name != NULL); |
michael@0 | 1969 | assert(pai != NULL); |
michael@0 | 1970 | |
michael@0 | 1971 | if (hostf->mapping == MAP_FAILED) |
michael@0 | 1972 | (void) _pseudo_fopen_r(hostf, _PATH_HOSTS); |
michael@0 | 1973 | if (hostf->mapping == MAP_FAILED) |
michael@0 | 1974 | return (NULL); |
michael@0 | 1975 | again: |
michael@0 | 1976 | if (!(p = _pseudo_fgets(hostbuf, sizeof hostbuf, hostf))) |
michael@0 | 1977 | return (NULL); |
michael@0 | 1978 | if (*p == '#') |
michael@0 | 1979 | goto again; |
michael@0 | 1980 | if (!(cp = strpbrk(p, "#\n"))) |
michael@0 | 1981 | goto again; |
michael@0 | 1982 | *cp = '\0'; |
michael@0 | 1983 | if (!(cp = strpbrk(p, " \t"))) |
michael@0 | 1984 | goto again; |
michael@0 | 1985 | *cp++ = '\0'; |
michael@0 | 1986 | addr = p; |
michael@0 | 1987 | /* if this is not something we're looking for, skip it. */ |
michael@0 | 1988 | cname = NULL; |
michael@0 | 1989 | while (cp && *cp) { |
michael@0 | 1990 | if (*cp == ' ' || *cp == '\t') { |
michael@0 | 1991 | cp++; |
michael@0 | 1992 | continue; |
michael@0 | 1993 | } |
michael@0 | 1994 | if (!cname) |
michael@0 | 1995 | cname = cp; |
michael@0 | 1996 | tname = cp; |
michael@0 | 1997 | if ((cp = strpbrk(cp, " \t")) != NULL) |
michael@0 | 1998 | *cp++ = '\0'; |
michael@0 | 1999 | // fprintf(stderr, "\ttname = '%s'", tname); |
michael@0 | 2000 | if (strcasecmp(name, tname) == 0) |
michael@0 | 2001 | goto found; |
michael@0 | 2002 | } |
michael@0 | 2003 | goto again; |
michael@0 | 2004 | |
michael@0 | 2005 | found: |
michael@0 | 2006 | hints = *pai; |
michael@0 | 2007 | hints.ai_flags = AI_NUMERICHOST; |
michael@0 | 2008 | error = __wrap_getaddrinfo(addr, NULL, &hints, &res0); |
michael@0 | 2009 | if (error) |
michael@0 | 2010 | goto again; |
michael@0 | 2011 | for (res = res0; res; res = res->ai_next) { |
michael@0 | 2012 | /* cover it up */ |
michael@0 | 2013 | res->ai_flags = pai->ai_flags; |
michael@0 | 2014 | |
michael@0 | 2015 | if (pai->ai_flags & AI_CANONNAME) { |
michael@0 | 2016 | if (get_canonname(pai, res, cname) != 0) { |
michael@0 | 2017 | __wrap_freeaddrinfo(res0); |
michael@0 | 2018 | goto again; |
michael@0 | 2019 | } |
michael@0 | 2020 | } |
michael@0 | 2021 | } |
michael@0 | 2022 | return res0; |
michael@0 | 2023 | } |
michael@0 | 2024 | |
michael@0 | 2025 | /*ARGSUSED*/ |
michael@0 | 2026 | static int |
michael@0 | 2027 | _files_getaddrinfo(void *rv, void *cb_data, va_list ap) |
michael@0 | 2028 | { |
michael@0 | 2029 | const char *name; |
michael@0 | 2030 | const struct addrinfo *pai; |
michael@0 | 2031 | struct addrinfo sentinel, *cur; |
michael@0 | 2032 | struct addrinfo *p; |
michael@0 | 2033 | _pseudo_FILE hostf = _PSEUDO_FILE_INITIALIZER; |
michael@0 | 2034 | |
michael@0 | 2035 | name = va_arg(ap, char *); |
michael@0 | 2036 | pai = va_arg(ap, struct addrinfo *); |
michael@0 | 2037 | |
michael@0 | 2038 | // fprintf(stderr, "_files_getaddrinfo() name = '%s'\n", name); |
michael@0 | 2039 | memset(&sentinel, 0, sizeof(sentinel)); |
michael@0 | 2040 | cur = &sentinel; |
michael@0 | 2041 | |
michael@0 | 2042 | _sethtent(&hostf); |
michael@0 | 2043 | while ((p = _gethtent(&hostf, name, pai)) != NULL) { |
michael@0 | 2044 | cur->ai_next = p; |
michael@0 | 2045 | while (cur && cur->ai_next) |
michael@0 | 2046 | cur = cur->ai_next; |
michael@0 | 2047 | } |
michael@0 | 2048 | _endhtent(&hostf); |
michael@0 | 2049 | |
michael@0 | 2050 | *((struct addrinfo **)rv) = sentinel.ai_next; |
michael@0 | 2051 | if (sentinel.ai_next == NULL) |
michael@0 | 2052 | return NS_NOTFOUND; |
michael@0 | 2053 | return NS_SUCCESS; |
michael@0 | 2054 | } |
michael@0 | 2055 | |
michael@0 | 2056 | /* resolver logic */ |
michael@0 | 2057 | |
michael@0 | 2058 | /* |
michael@0 | 2059 | * Formulate a normal query, send, and await answer. |
michael@0 | 2060 | * Returned answer is placed in supplied buffer "answer". |
michael@0 | 2061 | * Perform preliminary check of answer, returning success only |
michael@0 | 2062 | * if no error is indicated and the answer count is nonzero. |
michael@0 | 2063 | * Return the size of the response on success, -1 on error. |
michael@0 | 2064 | * Error number is left in h_errno. |
michael@0 | 2065 | * |
michael@0 | 2066 | * Caller must parse answer and determine whether it answers the question. |
michael@0 | 2067 | */ |
michael@0 | 2068 | static int |
michael@0 | 2069 | res_queryN(const char *name, /* domain name */ struct res_target *target, |
michael@0 | 2070 | res_state res) |
michael@0 | 2071 | { |
michael@0 | 2072 | u_char buf[MAXPACKET]; |
michael@0 | 2073 | HEADER *hp; |
michael@0 | 2074 | int n; |
michael@0 | 2075 | struct res_target *t; |
michael@0 | 2076 | int rcode; |
michael@0 | 2077 | int ancount; |
michael@0 | 2078 | |
michael@0 | 2079 | assert(name != NULL); |
michael@0 | 2080 | /* XXX: target may be NULL??? */ |
michael@0 | 2081 | |
michael@0 | 2082 | rcode = NOERROR; |
michael@0 | 2083 | ancount = 0; |
michael@0 | 2084 | |
michael@0 | 2085 | for (t = target; t; t = t->next) { |
michael@0 | 2086 | int class, type; |
michael@0 | 2087 | u_char *answer; |
michael@0 | 2088 | int anslen; |
michael@0 | 2089 | |
michael@0 | 2090 | hp = (HEADER *)(void *)t->answer; |
michael@0 | 2091 | hp->rcode = NOERROR; /* default */ |
michael@0 | 2092 | |
michael@0 | 2093 | /* make it easier... */ |
michael@0 | 2094 | class = t->qclass; |
michael@0 | 2095 | type = t->qtype; |
michael@0 | 2096 | answer = t->answer; |
michael@0 | 2097 | anslen = t->anslen; |
michael@0 | 2098 | #ifdef DEBUG |
michael@0 | 2099 | if (res->options & RES_DEBUG) |
michael@0 | 2100 | printf(";; res_nquery(%s, %d, %d)\n", name, class, type); |
michael@0 | 2101 | #endif |
michael@0 | 2102 | |
michael@0 | 2103 | n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, |
michael@0 | 2104 | buf, sizeof(buf)); |
michael@0 | 2105 | #ifdef RES_USE_EDNS0 |
michael@0 | 2106 | if (n > 0 && (res->options & RES_USE_EDNS0) != 0) |
michael@0 | 2107 | n = res_nopt(res, n, buf, sizeof(buf), anslen); |
michael@0 | 2108 | #endif |
michael@0 | 2109 | if (n <= 0) { |
michael@0 | 2110 | #ifdef DEBUG |
michael@0 | 2111 | if (res->options & RES_DEBUG) |
michael@0 | 2112 | printf(";; res_nquery: mkquery failed\n"); |
michael@0 | 2113 | #endif |
michael@0 | 2114 | h_errno = NO_RECOVERY; |
michael@0 | 2115 | return n; |
michael@0 | 2116 | } |
michael@0 | 2117 | n = res_nsend(res, buf, n, answer, anslen); |
michael@0 | 2118 | #if 0 |
michael@0 | 2119 | if (n < 0) { |
michael@0 | 2120 | #ifdef DEBUG |
michael@0 | 2121 | if (res->options & RES_DEBUG) |
michael@0 | 2122 | printf(";; res_query: send error\n"); |
michael@0 | 2123 | #endif |
michael@0 | 2124 | h_errno = TRY_AGAIN; |
michael@0 | 2125 | return n; |
michael@0 | 2126 | } |
michael@0 | 2127 | #endif |
michael@0 | 2128 | |
michael@0 | 2129 | if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { |
michael@0 | 2130 | rcode = hp->rcode; /* record most recent error */ |
michael@0 | 2131 | #ifdef DEBUG |
michael@0 | 2132 | if (res->options & RES_DEBUG) |
michael@0 | 2133 | printf(";; rcode = %u, ancount=%u\n", hp->rcode, |
michael@0 | 2134 | ntohs(hp->ancount)); |
michael@0 | 2135 | #endif |
michael@0 | 2136 | continue; |
michael@0 | 2137 | } |
michael@0 | 2138 | |
michael@0 | 2139 | ancount += ntohs(hp->ancount); |
michael@0 | 2140 | |
michael@0 | 2141 | t->n = n; |
michael@0 | 2142 | } |
michael@0 | 2143 | |
michael@0 | 2144 | if (ancount == 0) { |
michael@0 | 2145 | switch (rcode) { |
michael@0 | 2146 | case NXDOMAIN: |
michael@0 | 2147 | h_errno = HOST_NOT_FOUND; |
michael@0 | 2148 | break; |
michael@0 | 2149 | case SERVFAIL: |
michael@0 | 2150 | h_errno = TRY_AGAIN; |
michael@0 | 2151 | break; |
michael@0 | 2152 | case NOERROR: |
michael@0 | 2153 | h_errno = NO_DATA; |
michael@0 | 2154 | break; |
michael@0 | 2155 | case FORMERR: |
michael@0 | 2156 | case NOTIMP: |
michael@0 | 2157 | case REFUSED: |
michael@0 | 2158 | default: |
michael@0 | 2159 | h_errno = NO_RECOVERY; |
michael@0 | 2160 | break; |
michael@0 | 2161 | } |
michael@0 | 2162 | return -1; |
michael@0 | 2163 | } |
michael@0 | 2164 | return ancount; |
michael@0 | 2165 | } |
michael@0 | 2166 | |
michael@0 | 2167 | /* |
michael@0 | 2168 | * Formulate a normal query, send, and retrieve answer in supplied buffer. |
michael@0 | 2169 | * Return the size of the response on success, -1 on error. |
michael@0 | 2170 | * If enabled, implement search rules until answer or unrecoverable failure |
michael@0 | 2171 | * is detected. Error code, if any, is left in h_errno. |
michael@0 | 2172 | */ |
michael@0 | 2173 | static int |
michael@0 | 2174 | res_searchN(const char *name, struct res_target *target, res_state res) |
michael@0 | 2175 | { |
michael@0 | 2176 | const char *cp, * const *domain; |
michael@0 | 2177 | HEADER *hp; |
michael@0 | 2178 | u_int dots; |
michael@0 | 2179 | int trailing_dot, ret, saved_herrno; |
michael@0 | 2180 | int got_nodata = 0, got_servfail = 0, tried_as_is = 0; |
michael@0 | 2181 | |
michael@0 | 2182 | assert(name != NULL); |
michael@0 | 2183 | assert(target != NULL); |
michael@0 | 2184 | |
michael@0 | 2185 | hp = (HEADER *)(void *)target->answer; /*XXX*/ |
michael@0 | 2186 | |
michael@0 | 2187 | errno = 0; |
michael@0 | 2188 | h_errno = HOST_NOT_FOUND; /* default, if we never query */ |
michael@0 | 2189 | dots = 0; |
michael@0 | 2190 | for (cp = name; *cp; cp++) |
michael@0 | 2191 | dots += (*cp == '.'); |
michael@0 | 2192 | trailing_dot = 0; |
michael@0 | 2193 | if (cp > name && *--cp == '.') |
michael@0 | 2194 | trailing_dot++; |
michael@0 | 2195 | |
michael@0 | 2196 | |
michael@0 | 2197 | //fprintf(stderr, "res_searchN() name = '%s'\n", name); |
michael@0 | 2198 | |
michael@0 | 2199 | /* |
michael@0 | 2200 | * if there aren't any dots, it could be a user-level alias |
michael@0 | 2201 | */ |
michael@0 | 2202 | if (!dots && (cp = __hostalias(name)) != NULL) { |
michael@0 | 2203 | ret = res_queryN(cp, target, res); |
michael@0 | 2204 | return ret; |
michael@0 | 2205 | } |
michael@0 | 2206 | |
michael@0 | 2207 | /* |
michael@0 | 2208 | * If there are dots in the name already, let's just give it a try |
michael@0 | 2209 | * 'as is'. The threshold can be set with the "ndots" option. |
michael@0 | 2210 | */ |
michael@0 | 2211 | saved_herrno = -1; |
michael@0 | 2212 | if (dots >= res->ndots) { |
michael@0 | 2213 | ret = res_querydomainN(name, NULL, target, res); |
michael@0 | 2214 | if (ret > 0) |
michael@0 | 2215 | return (ret); |
michael@0 | 2216 | saved_herrno = h_errno; |
michael@0 | 2217 | tried_as_is++; |
michael@0 | 2218 | } |
michael@0 | 2219 | |
michael@0 | 2220 | /* |
michael@0 | 2221 | * We do at least one level of search if |
michael@0 | 2222 | * - there is no dot and RES_DEFNAME is set, or |
michael@0 | 2223 | * - there is at least one dot, there is no trailing dot, |
michael@0 | 2224 | * and RES_DNSRCH is set. |
michael@0 | 2225 | */ |
michael@0 | 2226 | if ((!dots && (res->options & RES_DEFNAMES)) || |
michael@0 | 2227 | (dots && !trailing_dot && (res->options & RES_DNSRCH))) { |
michael@0 | 2228 | int done = 0; |
michael@0 | 2229 | |
michael@0 | 2230 | for (domain = (const char * const *)res->dnsrch; |
michael@0 | 2231 | *domain && !done; |
michael@0 | 2232 | domain++) { |
michael@0 | 2233 | |
michael@0 | 2234 | ret = res_querydomainN(name, *domain, target, res); |
michael@0 | 2235 | if (ret > 0) |
michael@0 | 2236 | return ret; |
michael@0 | 2237 | |
michael@0 | 2238 | /* |
michael@0 | 2239 | * If no server present, give up. |
michael@0 | 2240 | * If name isn't found in this domain, |
michael@0 | 2241 | * keep trying higher domains in the search list |
michael@0 | 2242 | * (if that's enabled). |
michael@0 | 2243 | * On a NO_DATA error, keep trying, otherwise |
michael@0 | 2244 | * a wildcard entry of another type could keep us |
michael@0 | 2245 | * from finding this entry higher in the domain. |
michael@0 | 2246 | * If we get some other error (negative answer or |
michael@0 | 2247 | * server failure), then stop searching up, |
michael@0 | 2248 | * but try the input name below in case it's |
michael@0 | 2249 | * fully-qualified. |
michael@0 | 2250 | */ |
michael@0 | 2251 | if (errno == ECONNREFUSED) { |
michael@0 | 2252 | h_errno = TRY_AGAIN; |
michael@0 | 2253 | return -1; |
michael@0 | 2254 | } |
michael@0 | 2255 | |
michael@0 | 2256 | switch (h_errno) { |
michael@0 | 2257 | case NO_DATA: |
michael@0 | 2258 | got_nodata++; |
michael@0 | 2259 | /* FALLTHROUGH */ |
michael@0 | 2260 | case HOST_NOT_FOUND: |
michael@0 | 2261 | /* keep trying */ |
michael@0 | 2262 | break; |
michael@0 | 2263 | case TRY_AGAIN: |
michael@0 | 2264 | if (hp->rcode == SERVFAIL) { |
michael@0 | 2265 | /* try next search element, if any */ |
michael@0 | 2266 | got_servfail++; |
michael@0 | 2267 | break; |
michael@0 | 2268 | } |
michael@0 | 2269 | /* FALLTHROUGH */ |
michael@0 | 2270 | default: |
michael@0 | 2271 | /* anything else implies that we're done */ |
michael@0 | 2272 | done++; |
michael@0 | 2273 | } |
michael@0 | 2274 | /* |
michael@0 | 2275 | * if we got here for some reason other than DNSRCH, |
michael@0 | 2276 | * we only wanted one iteration of the loop, so stop. |
michael@0 | 2277 | */ |
michael@0 | 2278 | if (!(res->options & RES_DNSRCH)) |
michael@0 | 2279 | done++; |
michael@0 | 2280 | } |
michael@0 | 2281 | } |
michael@0 | 2282 | |
michael@0 | 2283 | /* |
michael@0 | 2284 | * if we have not already tried the name "as is", do that now. |
michael@0 | 2285 | * note that we do this regardless of how many dots were in the |
michael@0 | 2286 | * name or whether it ends with a dot. |
michael@0 | 2287 | */ |
michael@0 | 2288 | if (!tried_as_is) { |
michael@0 | 2289 | ret = res_querydomainN(name, NULL, target, res); |
michael@0 | 2290 | if (ret > 0) |
michael@0 | 2291 | return ret; |
michael@0 | 2292 | } |
michael@0 | 2293 | |
michael@0 | 2294 | /* |
michael@0 | 2295 | * if we got here, we didn't satisfy the search. |
michael@0 | 2296 | * if we did an initial full query, return that query's h_errno |
michael@0 | 2297 | * (note that we wouldn't be here if that query had succeeded). |
michael@0 | 2298 | * else if we ever got a nodata, send that back as the reason. |
michael@0 | 2299 | * else send back meaningless h_errno, that being the one from |
michael@0 | 2300 | * the last DNSRCH we did. |
michael@0 | 2301 | */ |
michael@0 | 2302 | if (saved_herrno != -1) |
michael@0 | 2303 | h_errno = saved_herrno; |
michael@0 | 2304 | else if (got_nodata) |
michael@0 | 2305 | h_errno = NO_DATA; |
michael@0 | 2306 | else if (got_servfail) |
michael@0 | 2307 | h_errno = TRY_AGAIN; |
michael@0 | 2308 | return -1; |
michael@0 | 2309 | } |
michael@0 | 2310 | |
michael@0 | 2311 | /* |
michael@0 | 2312 | * Perform a call on res_query on the concatenation of name and domain, |
michael@0 | 2313 | * removing a trailing dot from name if domain is NULL. |
michael@0 | 2314 | */ |
michael@0 | 2315 | static int |
michael@0 | 2316 | res_querydomainN(const char *name, const char *domain, |
michael@0 | 2317 | struct res_target *target, res_state res) |
michael@0 | 2318 | { |
michael@0 | 2319 | char nbuf[MAXDNAME]; |
michael@0 | 2320 | const char *longname = nbuf; |
michael@0 | 2321 | size_t n, d; |
michael@0 | 2322 | |
michael@0 | 2323 | assert(name != NULL); |
michael@0 | 2324 | /* XXX: target may be NULL??? */ |
michael@0 | 2325 | |
michael@0 | 2326 | #ifdef DEBUG |
michael@0 | 2327 | if (res->options & RES_DEBUG) |
michael@0 | 2328 | printf(";; res_querydomain(%s, %s)\n", |
michael@0 | 2329 | name, domain?domain:"<Nil>"); |
michael@0 | 2330 | #endif |
michael@0 | 2331 | if (domain == NULL) { |
michael@0 | 2332 | /* |
michael@0 | 2333 | * Check for trailing '.'; |
michael@0 | 2334 | * copy without '.' if present. |
michael@0 | 2335 | */ |
michael@0 | 2336 | n = strlen(name); |
michael@0 | 2337 | if (n + 1 > sizeof(nbuf)) { |
michael@0 | 2338 | h_errno = NO_RECOVERY; |
michael@0 | 2339 | return -1; |
michael@0 | 2340 | } |
michael@0 | 2341 | if (n > 0 && name[--n] == '.') { |
michael@0 | 2342 | strncpy(nbuf, name, n); |
michael@0 | 2343 | nbuf[n] = '\0'; |
michael@0 | 2344 | } else |
michael@0 | 2345 | longname = name; |
michael@0 | 2346 | } else { |
michael@0 | 2347 | n = strlen(name); |
michael@0 | 2348 | d = strlen(domain); |
michael@0 | 2349 | if (n + 1 + d + 1 > sizeof(nbuf)) { |
michael@0 | 2350 | h_errno = NO_RECOVERY; |
michael@0 | 2351 | return -1; |
michael@0 | 2352 | } |
michael@0 | 2353 | snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); |
michael@0 | 2354 | } |
michael@0 | 2355 | return res_queryN(longname, target, res); |
michael@0 | 2356 | } |