1*d1f9129bSflorian /* $OpenBSD: getaddrinfo_async.c,v 1.63 2024/08/21 05:53:10 florian Exp $ */ 2b44da627Seric /* 3b44da627Seric * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4b44da627Seric * 5b44da627Seric * Permission to use, copy, modify, and distribute this software for any 6b44da627Seric * purpose with or without fee is hereby granted, provided that the above 7b44da627Seric * copyright notice and this permission notice appear in all copies. 8b44da627Seric * 9b44da627Seric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10b44da627Seric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11b44da627Seric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12b44da627Seric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13b44da627Seric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14b44da627Seric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15b44da627Seric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b44da627Seric */ 1780f48568Seric 18b44da627Seric #include <sys/types.h> 19d216d6b1Seric #include <sys/socket.h> 20b44da627Seric #include <sys/uio.h> 210af765e7Sderaadt #include <netinet/in.h> 22b44da627Seric #include <arpa/nameser.h> 2320149d17Ssperreault #include <net/if.h> 24d216d6b1Seric #include <netdb.h> 25b44da627Seric 26d216d6b1Seric #include <asr.h> 2780f48568Seric #include <errno.h> 2820149d17Ssperreault #include <ifaddrs.h> 298f32d9f2Sjca #include <resolv.h> 3080f48568Seric #include <stdlib.h> 3180f48568Seric #include <string.h> 3280f48568Seric #include <unistd.h> 33e7e445a1Sderaadt #include <limits.h> 34e7e445a1Sderaadt 35b44da627Seric #include "asr_private.h" 36b44da627Seric 37b44da627Seric struct match { 38b44da627Seric int family; 39b44da627Seric int socktype; 40b44da627Seric int protocol; 41b44da627Seric }; 42b44da627Seric 435be03f8fSeric static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); 44b44da627Seric static int get_port(const char *, const char *, int); 455be03f8fSeric static int iter_family(struct asr_query *, int); 465be03f8fSeric static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); 475be03f8fSeric static int addrinfo_from_file(struct asr_query *, int, FILE *); 485be03f8fSeric static int addrinfo_from_pkt(struct asr_query *, char *, size_t); 499c0437c0Sjca static int addrconfig_setup(struct asr_query *); 50b44da627Seric 51b44da627Seric static const struct match matches[] = { 52b44da627Seric { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, 53b44da627Seric { PF_INET, SOCK_STREAM, IPPROTO_TCP }, 54b44da627Seric { PF_INET, SOCK_RAW, 0 }, 55b44da627Seric { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, 56b44da627Seric { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, 57b44da627Seric { PF_INET6, SOCK_RAW, 0 }, 58b44da627Seric { -1, 0, 0, }, 59b44da627Seric }; 60b44da627Seric 61b44da627Seric #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) 6299210259Seric #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) 63c43131adSkrw /* Do not match SOCK_RAW unless explicitly specified */ 64b44da627Seric #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ 65b44da627Seric matches[(b)].socktype != SOCK_RAW)) 66b44da627Seric 673e41c455Seric enum { 683e41c455Seric DOM_INIT, 693e41c455Seric DOM_DOMAIN, 703e41c455Seric DOM_DONE 713e41c455Seric }; 723e41c455Seric 735be03f8fSeric struct asr_query * 74b44da627Seric getaddrinfo_async(const char *hostname, const char *servname, 755be03f8fSeric const struct addrinfo *hints, void *asr) 76b44da627Seric { 77b44da627Seric struct asr_ctx *ac; 785be03f8fSeric struct asr_query *as; 79b44da627Seric 809936a0e9Seric if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0) 81253ef892Sderaadt ac = _asr_use_resolver(asr); 82656b8d51Sderaadt else 83656b8d51Sderaadt ac = _asr_no_resolver(); 84253ef892Sderaadt if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL) 85b44da627Seric goto abort; /* errno set */ 86b44da627Seric as->as_run = getaddrinfo_async_run; 87b44da627Seric 8820f7c2a3Seric if (hostname) { 8920f7c2a3Seric if ((as->as.ai.hostname = strdup(hostname)) == NULL) 90b44da627Seric goto abort; /* errno set */ 9120f7c2a3Seric } 92b44da627Seric if (servname && (as->as.ai.servname = strdup(servname)) == NULL) 93b44da627Seric goto abort; /* errno set */ 94b44da627Seric if (hints) 95b44da627Seric memmove(&as->as.ai.hints, hints, sizeof *hints); 96b44da627Seric else { 97b44da627Seric memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); 98b44da627Seric as->as.ai.hints.ai_family = PF_UNSPEC; 9920149d17Ssperreault as->as.ai.hints.ai_flags = AI_ADDRCONFIG; 100b44da627Seric } 101b44da627Seric 102253ef892Sderaadt _asr_ctx_unref(ac); 103b44da627Seric return (as); 104b44da627Seric abort: 105b44da627Seric if (as) 106253ef892Sderaadt _asr_async_free(as); 107253ef892Sderaadt _asr_ctx_unref(ac); 108b44da627Seric return (NULL); 109b44da627Seric } 1105826fd8cSguenther DEF_WEAK(getaddrinfo_async); 111b44da627Seric 112b44da627Seric static int 1135be03f8fSeric getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) 114b44da627Seric { 1153e41c455Seric char fqdn[MAXDNAME]; 116b44da627Seric const char *str; 117b44da627Seric struct addrinfo *ai; 118fada2b0bSflorian int i, family, r, is_localhost = 0; 119c5c8c49bSeric FILE *f; 120b44da627Seric union { 121b44da627Seric struct sockaddr sa; 122b44da627Seric struct sockaddr_in sain; 123b44da627Seric struct sockaddr_in6 sain6; 124b44da627Seric } sa; 125b44da627Seric 126b44da627Seric next: 127b44da627Seric switch (as->as_state) { 128b44da627Seric 129b44da627Seric case ASR_STATE_INIT: 130b44da627Seric 131b44da627Seric /* 132b44da627Seric * First, make sure the parameters are valid. 133b44da627Seric */ 134b44da627Seric 135b44da627Seric as->as_count = 0; 136b44da627Seric 137b44da627Seric if (as->as.ai.hostname == NULL && 138b44da627Seric as->as.ai.servname == NULL) { 139b44da627Seric ar->ar_gai_errno = EAI_NONAME; 1404a508230Seric async_set_state(as, ASR_STATE_HALT); 141b44da627Seric break; 142b44da627Seric } 143b44da627Seric 144ab50be5eSeric if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') { 145ab50be5eSeric ar->ar_gai_errno = EAI_NODATA; 146ab50be5eSeric async_set_state(as, ASR_STATE_HALT); 147ab50be5eSeric break; 148ab50be5eSeric } 149ab50be5eSeric 150b44da627Seric ai = &as->as.ai.hints; 151b44da627Seric 152b44da627Seric if (ai->ai_addrlen || 153b44da627Seric ai->ai_canonname || 154b44da627Seric ai->ai_addr || 155b44da627Seric ai->ai_next) { 156b44da627Seric ar->ar_gai_errno = EAI_BADHINTS; 1574a508230Seric async_set_state(as, ASR_STATE_HALT); 158b44da627Seric break; 159b44da627Seric } 160b44da627Seric 161b44da627Seric if (ai->ai_flags & ~AI_MASK || 162b44da627Seric (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { 163b44da627Seric ar->ar_gai_errno = EAI_BADFLAGS; 1644a508230Seric async_set_state(as, ASR_STATE_HALT); 165b44da627Seric break; 166b44da627Seric } 167b44da627Seric 168b44da627Seric if (ai->ai_family != PF_UNSPEC && 169b44da627Seric ai->ai_family != PF_INET && 170b44da627Seric ai->ai_family != PF_INET6) { 171b44da627Seric ar->ar_gai_errno = EAI_FAMILY; 1724a508230Seric async_set_state(as, ASR_STATE_HALT); 173b44da627Seric break; 174b44da627Seric } 175b44da627Seric 176b44da627Seric if (ai->ai_socktype && 177b44da627Seric ai->ai_socktype != SOCK_DGRAM && 178b44da627Seric ai->ai_socktype != SOCK_STREAM && 179b44da627Seric ai->ai_socktype != SOCK_RAW) { 180b44da627Seric ar->ar_gai_errno = EAI_SOCKTYPE; 1814a508230Seric async_set_state(as, ASR_STATE_HALT); 182b44da627Seric break; 183b44da627Seric } 184b44da627Seric 185b44da627Seric if (ai->ai_socktype == SOCK_RAW && 1863d13cefaSeric get_port(as->as.ai.servname, NULL, 1) != 0) { 187b44da627Seric ar->ar_gai_errno = EAI_SERVICE; 1884a508230Seric async_set_state(as, ASR_STATE_HALT); 189b44da627Seric break; 190b44da627Seric } 191b44da627Seric 19220149d17Ssperreault /* Restrict result set to configured address families */ 19320149d17Ssperreault if (ai->ai_flags & AI_ADDRCONFIG) { 194cbd3ae78Seric if (addrconfig_setup(as) == -1) { 195cbd3ae78Seric ar->ar_errno = errno; 196cbd3ae78Seric ar->ar_gai_errno = EAI_SYSTEM; 19720149d17Ssperreault async_set_state(as, ASR_STATE_HALT); 19820149d17Ssperreault break; 19920149d17Ssperreault } 20020149d17Ssperreault } 20120149d17Ssperreault 202b44da627Seric /* Make sure there is at least a valid combination */ 203b44da627Seric for (i = 0; matches[i].family != -1; i++) 204b44da627Seric if (MATCH_FAMILY(ai->ai_family, i) && 205b44da627Seric MATCH_SOCKTYPE(ai->ai_socktype, i) && 206b44da627Seric MATCH_PROTO(ai->ai_protocol, i)) 207b44da627Seric break; 208b44da627Seric if (matches[i].family == -1) { 209b44da627Seric ar->ar_gai_errno = EAI_BADHINTS; 2104a508230Seric async_set_state(as, ASR_STATE_HALT); 211b44da627Seric break; 212b44da627Seric } 213b44da627Seric 21448756ee8Seric if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) 21548756ee8Seric as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", 21648756ee8Seric as->as.ai.hints.ai_flags & AI_NUMERICSERV); 21748756ee8Seric if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) 21848756ee8Seric as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", 21948756ee8Seric as->as.ai.hints.ai_flags & AI_NUMERICSERV); 22048756ee8Seric if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || 2218b2098d5Seric (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || 2228b2098d5Seric (ai->ai_protocol && (as->as.ai.port_udp == -1 || 2238b2098d5Seric as->as.ai.port_tcp == -1))) { 224b44da627Seric ar->ar_gai_errno = EAI_SERVICE; 2254a508230Seric async_set_state(as, ASR_STATE_HALT); 226b44da627Seric break; 227b44da627Seric } 228b44da627Seric 2294a508230Seric ar->ar_gai_errno = 0; 2304a508230Seric 231fada2b0bSflorian if (!(ai->ai_flags & AI_NUMERICHOST)) 232373da8abSflorian is_localhost = _asr_is_localhost(as->as.ai.hostname); 233373da8abSflorian /* 234373da8abSflorian * If hostname is NULL, "localhost" or falls within the 235373da8abSflorian * ".localhost." domain, use local address. 236373da8abSflorian * RFC 6761, 6.3: 237373da8abSflorian * 3. Name resolution APIs and libraries SHOULD recognize 238373da8abSflorian * localhost names as special and SHOULD always return the IP 239373da8abSflorian * loopback address for address queries and negative responses 240373da8abSflorian * for all other query types. Name resolution APIs SHOULD NOT 241373da8abSflorian * send queries for localhost names to their configured caching 242373da8abSflorian * DNS server(s). 243373da8abSflorian */ 244373da8abSflorian if (as->as.ai.hostname == NULL || is_localhost) { 245b44da627Seric for (family = iter_family(as, 1); 246b44da627Seric family != -1; 247b44da627Seric family = iter_family(as, 0)) { 248b44da627Seric /* 249b44da627Seric * We could use statically built sockaddrs for 250b44da627Seric * those, rather than parsing over and over. 251b44da627Seric */ 252b44da627Seric if (family == PF_INET) 253373da8abSflorian str = (ai->ai_flags & AI_PASSIVE && 254373da8abSflorian !is_localhost) ? "0.0.0.0" : 255373da8abSflorian "127.0.0.1"; 256b44da627Seric else /* PF_INET6 */ 257373da8abSflorian str = (ai->ai_flags & AI_PASSIVE && 258373da8abSflorian !is_localhost) ? "::" : "::1"; 259b44da627Seric /* This can't fail */ 260253ef892Sderaadt _asr_sockaddr_from_str(&sa.sa, family, str); 261ab51fa82Sflorian if ((r = addrinfo_add(as, &sa.sa, 262ab51fa82Sflorian "localhost."))) { 263b44da627Seric ar->ar_gai_errno = r; 264b44da627Seric break; 265b44da627Seric } 266b44da627Seric } 267b44da627Seric if (ar->ar_gai_errno == 0 && as->as_count == 0) { 268b44da627Seric ar->ar_gai_errno = EAI_NODATA; 269b44da627Seric } 2704a508230Seric async_set_state(as, ASR_STATE_HALT); 271b44da627Seric break; 272b44da627Seric } 273b44da627Seric 274b44da627Seric /* Try numeric addresses first */ 275b44da627Seric for (family = iter_family(as, 1); 276b44da627Seric family != -1; 277b44da627Seric family = iter_family(as, 0)) { 278b44da627Seric 279253ef892Sderaadt if (_asr_sockaddr_from_str(&sa.sa, family, 280b44da627Seric as->as.ai.hostname) == -1) 281b44da627Seric continue; 282b44da627Seric 283*d1f9129bSflorian if ((r = addrinfo_add(as, &sa.sa, as->as.ai.hostname))) 284b44da627Seric ar->ar_gai_errno = r; 2854a508230Seric break; 2864a508230Seric } 2874a508230Seric if (ar->ar_gai_errno || as->as_count) { 288b44da627Seric async_set_state(as, ASR_STATE_HALT); 289b44da627Seric break; 290b44da627Seric } 291b44da627Seric 292b44da627Seric if (ai->ai_flags & AI_NUMERICHOST) { 293ff1bfb95Schrisz ar->ar_gai_errno = EAI_NONAME; 294b44da627Seric async_set_state(as, ASR_STATE_HALT); 295b44da627Seric break; 296b44da627Seric } 297b44da627Seric 2981b04c78cSflorian /* make sure there are no funny characters in hostname */ 2991b04c78cSflorian if (!hnok_lenient(as->as.ai.hostname)) { 3001b04c78cSflorian ar->ar_gai_errno = EAI_FAIL; 3011b04c78cSflorian async_set_state(as, ASR_STATE_HALT); 3021b04c78cSflorian break; 3031b04c78cSflorian } 3041b04c78cSflorian 305c5c8c49bSeric async_set_state(as, ASR_STATE_NEXT_DB); 306c5c8c49bSeric break; 307b44da627Seric 308c5c8c49bSeric case ASR_STATE_NEXT_DB: 309253ef892Sderaadt if (_asr_iter_db(as) == -1) { 3101e1dfc0cSeric async_set_state(as, ASR_STATE_NOT_FOUND); 311c5c8c49bSeric break; 312c5c8c49bSeric } 313c5c8c49bSeric as->as_family_idx = 0; 314c5c8c49bSeric async_set_state(as, ASR_STATE_SAME_DB); 315c5c8c49bSeric break; 316c5c8c49bSeric 317c5c8c49bSeric case ASR_STATE_NEXT_FAMILY: 318c5c8c49bSeric as->as_family_idx += 1; 319c5c8c49bSeric if (as->as.ai.hints.ai_family != AF_UNSPEC || 320c5c8c49bSeric AS_FAMILY(as) == -1) { 321c5c8c49bSeric /* The family was specified, or we have tried all 322c5c8c49bSeric * families with this DB. 323b44da627Seric */ 324c5c8c49bSeric if (as->as_count) { 325c5c8c49bSeric ar->ar_gai_errno = 0; 326c5c8c49bSeric async_set_state(as, ASR_STATE_HALT); 327c5c8c49bSeric } else 3283e41c455Seric async_set_state(as, ASR_STATE_NEXT_DOMAIN); 3293e41c455Seric break; 3303e41c455Seric } 3313e41c455Seric async_set_state(as, ASR_STATE_SAME_DB); 3323e41c455Seric break; 3333e41c455Seric 3343e41c455Seric case ASR_STATE_NEXT_DOMAIN: 3353e41c455Seric /* domain search is only for dns */ 3363e41c455Seric if (AS_DB(as) != ASR_DB_DNS) { 337c5c8c49bSeric async_set_state(as, ASR_STATE_NEXT_DB); 338c5c8c49bSeric break; 339c5c8c49bSeric } 3403e41c455Seric as->as_family_idx = 0; 3413e41c455Seric 3423e41c455Seric free(as->as.ai.fqdn); 3433e41c455Seric as->as.ai.fqdn = NULL; 344253ef892Sderaadt r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); 3453e41c455Seric if (r == -1) { 3463e41c455Seric async_set_state(as, ASR_STATE_NEXT_DB); 3473e41c455Seric break; 3483e41c455Seric } 3493e41c455Seric if (r == 0) { 3503e41c455Seric ar->ar_gai_errno = EAI_FAIL; 3513e41c455Seric async_set_state(as, ASR_STATE_HALT); 3523e41c455Seric break; 3533e41c455Seric } 3543e41c455Seric as->as.ai.fqdn = strdup(fqdn); 3553e41c455Seric if (as->as.ai.fqdn == NULL) { 3563e41c455Seric ar->ar_gai_errno = EAI_MEMORY; 3573e41c455Seric async_set_state(as, ASR_STATE_HALT); 3586c455d5cSeric break; 3593e41c455Seric } 3603e41c455Seric 361c5c8c49bSeric async_set_state(as, ASR_STATE_SAME_DB); 362c5c8c49bSeric break; 363c5c8c49bSeric 364c5c8c49bSeric case ASR_STATE_SAME_DB: 3651e1dfc0cSeric /* query the current DB again */ 366c5c8c49bSeric switch (AS_DB(as)) { 367c5c8c49bSeric case ASR_DB_DNS: 3683e41c455Seric if (as->as.ai.fqdn == NULL) { 3693e41c455Seric /* First try, initialize domain iteration */ 3703e41c455Seric as->as_dom_flags = 0; 3713e41c455Seric as->as_dom_step = DOM_INIT; 3723e41c455Seric async_set_state(as, ASR_STATE_NEXT_DOMAIN); 3733e41c455Seric break; 3743e41c455Seric } 3753e41c455Seric 376c5c8c49bSeric family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 377c5c8c49bSeric AS_FAMILY(as) : as->as.ai.hints.ai_family; 3783e41c455Seric 3798b59b78cSjca if (family == AF_INET && 380abe78e02Sjca as->as_flags & ASYNC_NO_INET) { 3818b59b78cSjca async_set_state(as, ASR_STATE_NEXT_FAMILY); 3828b59b78cSjca break; 3838b59b78cSjca } else if (family == AF_INET6 && 384abe78e02Sjca as->as_flags & ASYNC_NO_INET6) { 3858b59b78cSjca async_set_state(as, ASR_STATE_NEXT_FAMILY); 3868b59b78cSjca break; 3878b59b78cSjca } 3888b59b78cSjca 389f6f51dadSeric as->as_subq = _res_query_async_ctx(as->as.ai.fqdn, 3903e41c455Seric C_IN, (family == AF_INET6) ? T_AAAA : T_A, 391b44da627Seric as->as_ctx); 3923e41c455Seric 393f6f51dadSeric if (as->as_subq == NULL) { 394c5c8c49bSeric if (errno == ENOMEM) 395b44da627Seric ar->ar_gai_errno = EAI_MEMORY; 396c5c8c49bSeric else 397c5c8c49bSeric ar->ar_gai_errno = EAI_FAIL; 398b44da627Seric async_set_state(as, ASR_STATE_HALT); 399b44da627Seric break; 400b44da627Seric } 401c5c8c49bSeric async_set_state(as, ASR_STATE_SUBQUERY); 402b44da627Seric break; 403b44da627Seric 404c5c8c49bSeric case ASR_DB_FILE: 405d2d7f9c9Seric f = fopen(_PATH_HOSTS, "re"); 406c5c8c49bSeric if (f == NULL) { 407c5c8c49bSeric async_set_state(as, ASR_STATE_NEXT_DB); 408c5c8c49bSeric break; 409c5c8c49bSeric } 410c5c8c49bSeric family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 411c5c8c49bSeric AS_FAMILY(as) : as->as.ai.hints.ai_family; 412b44da627Seric 413c5c8c49bSeric r = addrinfo_from_file(as, family, f); 414c5c8c49bSeric if (r == -1) { 415c5c8c49bSeric if (errno == ENOMEM) 416c5c8c49bSeric ar->ar_gai_errno = EAI_MEMORY; 417c5c8c49bSeric else 418c5c8c49bSeric ar->ar_gai_errno = EAI_FAIL; 419c5c8c49bSeric async_set_state(as, ASR_STATE_HALT); 420c5c8c49bSeric } else 421c5c8c49bSeric async_set_state(as, ASR_STATE_NEXT_FAMILY); 422c5c8c49bSeric fclose(f); 423c5c8c49bSeric break; 424c5c8c49bSeric 425c5c8c49bSeric default: 426c5c8c49bSeric async_set_state(as, ASR_STATE_NEXT_DB); 427c5c8c49bSeric } 428c5c8c49bSeric break; 429c5c8c49bSeric 430c5c8c49bSeric case ASR_STATE_SUBQUERY: 431f6f51dadSeric if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) 432b44da627Seric return (ASYNC_COND); 4335be03f8fSeric 434f6f51dadSeric as->as_subq = NULL; 435c5c8c49bSeric 436c5c8c49bSeric if (ar->ar_datalen == -1) { 4371e1dfc0cSeric async_set_state(as, ASR_STATE_NEXT_FAMILY); 438b44da627Seric break; 439d95d6a55Seric } 440d95d6a55Seric 441c5c8c49bSeric r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); 442c5c8c49bSeric if (r == -1) { 443c5c8c49bSeric if (errno == ENOMEM) 444c5c8c49bSeric ar->ar_gai_errno = EAI_MEMORY; 445c5c8c49bSeric else 446c5c8c49bSeric ar->ar_gai_errno = EAI_FAIL; 447d95d6a55Seric async_set_state(as, ASR_STATE_HALT); 448c5c8c49bSeric } else 449c5c8c49bSeric async_set_state(as, ASR_STATE_NEXT_FAMILY); 450c5c8c49bSeric free(ar->ar_data); 451d95d6a55Seric break; 452b44da627Seric 453b44da627Seric case ASR_STATE_NOT_FOUND: 454c5c8c49bSeric /* No result found. Maybe we can try again. */ 455abe78e02Sjca if (as->as_flags & ASYNC_AGAIN) 456b44da627Seric ar->ar_gai_errno = EAI_AGAIN; 457c5c8c49bSeric else 458b44da627Seric ar->ar_gai_errno = EAI_NODATA; 459b44da627Seric async_set_state(as, ASR_STATE_HALT); 460b44da627Seric break; 461b44da627Seric 462b44da627Seric case ASR_STATE_HALT: 463b44da627Seric if (ar->ar_gai_errno == 0) { 464b44da627Seric ar->ar_count = as->as_count; 465b44da627Seric ar->ar_addrinfo = as->as.ai.aifirst; 466b44da627Seric as->as.ai.aifirst = NULL; 467b44da627Seric } else { 468b44da627Seric ar->ar_count = 0; 469b44da627Seric ar->ar_addrinfo = NULL; 470b44da627Seric } 471b44da627Seric return (ASYNC_DONE); 472b44da627Seric 473b44da627Seric default: 474b44da627Seric ar->ar_errno = EOPNOTSUPP; 475b44da627Seric ar->ar_gai_errno = EAI_SYSTEM; 476b44da627Seric async_set_state(as, ASR_STATE_HALT); 477b44da627Seric break; 478b44da627Seric } 479b44da627Seric goto next; 480b44da627Seric } 481b44da627Seric 482b44da627Seric /* 4832c53affbSjmc * Retrieve the port number for the service name "servname" and 484b44da627Seric * the protocol "proto". 485b44da627Seric */ 486b44da627Seric static int 487b44da627Seric get_port(const char *servname, const char *proto, int numonly) 488b44da627Seric { 489b44da627Seric struct servent se; 490b44da627Seric struct servent_data sed; 491172d89a7Seric int port; 492b44da627Seric const char *e; 493b44da627Seric 494b44da627Seric if (servname == NULL) 495b44da627Seric return (0); 496b44da627Seric 497b44da627Seric e = NULL; 498b44da627Seric port = strtonum(servname, 0, USHRT_MAX, &e); 499b44da627Seric if (e == NULL) 500b44da627Seric return (port); 501b44da627Seric if (errno == ERANGE) 502b44da627Seric return (-2); /* invalid */ 503b44da627Seric if (numonly) 504b44da627Seric return (-2); 505b44da627Seric 506172d89a7Seric port = -1; 507b44da627Seric memset(&sed, 0, sizeof(sed)); 508172d89a7Seric if (getservbyname_r(servname, proto, &se, &sed) != -1) 509b44da627Seric port = ntohs(se.s_port); 510b44da627Seric endservent_r(&sed); 511b44da627Seric 512b44da627Seric return (port); 513b44da627Seric } 514b44da627Seric 515b44da627Seric /* 516b44da627Seric * Iterate over the address families that are to be queried. Use the 517b44da627Seric * list on the async context, unless a specific family was given in hints. 518b44da627Seric */ 519b44da627Seric static int 5205be03f8fSeric iter_family(struct asr_query *as, int first) 521b44da627Seric { 522b44da627Seric if (first) { 523b44da627Seric as->as_family_idx = 0; 524b44da627Seric if (as->as.ai.hints.ai_family != PF_UNSPEC) 525b44da627Seric return as->as.ai.hints.ai_family; 526b44da627Seric return AS_FAMILY(as); 527b44da627Seric } 528b44da627Seric 529b44da627Seric if (as->as.ai.hints.ai_family != PF_UNSPEC) 530b44da627Seric return (-1); 531b44da627Seric 532b44da627Seric as->as_family_idx++; 533b44da627Seric 534b44da627Seric return AS_FAMILY(as); 535b44da627Seric } 536b44da627Seric 537b44da627Seric /* 538b44da627Seric * Use the sockaddr at "sa" to extend the result list on the "as" context, 539b44da627Seric * with the specified canonical name "cname". This function adds one 540b44da627Seric * entry per protocol/socktype match. 541b44da627Seric */ 542b44da627Seric static int 5435be03f8fSeric addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) 544b44da627Seric { 545b44da627Seric struct addrinfo *ai; 54699210259Seric int i, port, proto; 547b44da627Seric 548b44da627Seric for (i = 0; matches[i].family != -1; i++) { 549b44da627Seric if (matches[i].family != sa->sa_family || 550b44da627Seric !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 551b44da627Seric !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 552b44da627Seric continue; 553b44da627Seric 55499210259Seric proto = as->as.ai.hints.ai_protocol; 55599210259Seric if (!proto) 55699210259Seric proto = matches[i].protocol; 55799210259Seric 55899210259Seric if (proto == IPPROTO_TCP) 559b44da627Seric port = as->as.ai.port_tcp; 56099210259Seric else if (proto == IPPROTO_UDP) 561b44da627Seric port = as->as.ai.port_udp; 562b44da627Seric else 563b44da627Seric port = 0; 564b44da627Seric 56548756ee8Seric /* servname specified, but not defined for this protocol */ 56648756ee8Seric if (port == -1) 56748756ee8Seric continue; 56848756ee8Seric 569b44da627Seric ai = calloc(1, sizeof(*ai) + sa->sa_len); 570b44da627Seric if (ai == NULL) 571b44da627Seric return (EAI_MEMORY); 572b44da627Seric ai->ai_family = sa->sa_family; 573b44da627Seric ai->ai_socktype = matches[i].socktype; 57499210259Seric ai->ai_protocol = proto; 575024ee4aaSeric ai->ai_flags = as->as.ai.hints.ai_flags; 576b44da627Seric ai->ai_addrlen = sa->sa_len; 577b44da627Seric ai->ai_addr = (void *)(ai + 1); 578b44da627Seric if (cname && 579b44da627Seric as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 580b44da627Seric if ((ai->ai_canonname = strdup(cname)) == NULL) { 581b44da627Seric free(ai); 582b44da627Seric return (EAI_MEMORY); 583b44da627Seric } 584b44da627Seric } 585b44da627Seric memmove(ai->ai_addr, sa, sa->sa_len); 586b44da627Seric if (sa->sa_family == PF_INET) 587b44da627Seric ((struct sockaddr_in *)ai->ai_addr)->sin_port = 588b44da627Seric htons(port); 589b44da627Seric else if (sa->sa_family == PF_INET6) 590b44da627Seric ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 591b44da627Seric htons(port); 592b44da627Seric 593b44da627Seric if (as->as.ai.aifirst == NULL) 594b44da627Seric as->as.ai.aifirst = ai; 595b44da627Seric if (as->as.ai.ailast) 596b44da627Seric as->as.ai.ailast->ai_next = ai; 597b44da627Seric as->as.ai.ailast = ai; 598b44da627Seric as->as_count += 1; 599b44da627Seric } 600b44da627Seric 601b44da627Seric return (0); 602b44da627Seric } 603c5c8c49bSeric 604c5c8c49bSeric static int 6055be03f8fSeric addrinfo_from_file(struct asr_query *as, int family, FILE *f) 606c5c8c49bSeric { 607f108579bSeric char *tokens[MAXTOKEN], *c, buf[BUFSIZ + 1]; 608c5c8c49bSeric int n, i; 609c5c8c49bSeric union { 610c5c8c49bSeric struct sockaddr sa; 611c5c8c49bSeric struct sockaddr_in sain; 612c5c8c49bSeric struct sockaddr_in6 sain6; 613c5c8c49bSeric } u; 614c5c8c49bSeric 615c5c8c49bSeric for (;;) { 616253ef892Sderaadt n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); 617c5c8c49bSeric if (n == -1) 618c5c8c49bSeric break; /* ignore errors reading the file */ 619c5c8c49bSeric 620c5c8c49bSeric for (i = 1; i < n; i++) { 62120f7c2a3Seric if (strcasecmp(as->as.ai.hostname, tokens[i])) 622c5c8c49bSeric continue; 623253ef892Sderaadt if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 624c5c8c49bSeric continue; 625c5c8c49bSeric break; 626c5c8c49bSeric } 627c5c8c49bSeric if (i == n) 628c5c8c49bSeric continue; 629c5c8c49bSeric 6301e1dfc0cSeric if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) 631c5c8c49bSeric c = tokens[1]; 632c5c8c49bSeric else 633c5c8c49bSeric c = NULL; 634c5c8c49bSeric 635c5c8c49bSeric if (addrinfo_add(as, &u.sa, c)) 636c5c8c49bSeric return (-1); /* errno set */ 637c5c8c49bSeric } 638c5c8c49bSeric return (0); 639c5c8c49bSeric } 640c5c8c49bSeric 641c5c8c49bSeric static int 6425be03f8fSeric addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) 643c5c8c49bSeric { 644f90bf415Seric struct asr_unpack p; 645f90bf415Seric struct asr_dns_header h; 646f90bf415Seric struct asr_dns_query q; 647f90bf415Seric struct asr_dns_rr rr; 648c5c8c49bSeric int i; 649c5c8c49bSeric union { 650c5c8c49bSeric struct sockaddr sa; 651c5c8c49bSeric struct sockaddr_in sain; 652c5c8c49bSeric struct sockaddr_in6 sain6; 653c5c8c49bSeric } u; 654c5c8c49bSeric char buf[MAXDNAME], *c; 655c5c8c49bSeric 656253ef892Sderaadt _asr_unpack_init(&p, pkt, pktlen); 657253ef892Sderaadt _asr_unpack_header(&p, &h); 658c5c8c49bSeric for (; h.qdcount; h.qdcount--) 659253ef892Sderaadt _asr_unpack_query(&p, &q); 660c5c8c49bSeric 661c5c8c49bSeric for (i = 0; i < h.ancount; i++) { 662253ef892Sderaadt _asr_unpack_rr(&p, &rr); 663c5c8c49bSeric if (rr.rr_type != q.q_type || 664c5c8c49bSeric rr.rr_class != q.q_class) 665c5c8c49bSeric continue; 666c5c8c49bSeric 667c5c8c49bSeric memset(&u, 0, sizeof u); 668c5c8c49bSeric if (rr.rr_type == T_A) { 669c5c8c49bSeric u.sain.sin_len = sizeof u.sain; 670c5c8c49bSeric u.sain.sin_family = AF_INET; 671c5c8c49bSeric u.sain.sin_addr = rr.rr.in_a.addr; 672c5c8c49bSeric u.sain.sin_port = 0; 673c5c8c49bSeric } else if (rr.rr_type == T_AAAA) { 674c5c8c49bSeric u.sain6.sin6_len = sizeof u.sain6; 675c5c8c49bSeric u.sain6.sin6_family = AF_INET6; 676c5c8c49bSeric u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; 677c5c8c49bSeric u.sain6.sin6_port = 0; 678c5c8c49bSeric } else 679c5c8c49bSeric continue; 680c5c8c49bSeric 681c5c8c49bSeric if (as->as.ai.hints.ai_flags & AI_CANONNAME) { 682253ef892Sderaadt _asr_strdname(rr.rr_dname, buf, sizeof buf); 683c5c8c49bSeric buf[strlen(buf) - 1] = '\0'; 684*d1f9129bSflorian c = res_hnok(buf) ? buf : as->as.ai.hostname; 685c5c8c49bSeric } else if (as->as.ai.hints.ai_flags & AI_FQDN) 686c5c8c49bSeric c = as->as.ai.fqdn; 687c5c8c49bSeric else 688c5c8c49bSeric c = NULL; 689c5c8c49bSeric 690c5c8c49bSeric if (addrinfo_add(as, &u.sa, c)) 691c5c8c49bSeric return (-1); /* errno set */ 692c5c8c49bSeric } 693c5c8c49bSeric return (0); 694c5c8c49bSeric } 695c5c8c49bSeric 6969c0437c0Sjca static int 6979c0437c0Sjca addrconfig_setup(struct asr_query *as) 6989c0437c0Sjca { 6999c0437c0Sjca struct ifaddrs *ifa, *ifa0; 70052b8ecd4Sflorian struct if_data *ifa_data; 701d6498094Sjca struct sockaddr_in *sinp; 7029c0437c0Sjca struct sockaddr_in6 *sin6p; 70352b8ecd4Sflorian int rtable, ifa_rtable = -1; 7049c0437c0Sjca 705cbd3ae78Seric if (getifaddrs(&ifa0) == -1) 7069c0437c0Sjca return (-1); 7079c0437c0Sjca 70852b8ecd4Sflorian rtable = getrtable(); 70952b8ecd4Sflorian 710abe78e02Sjca as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6; 7119c0437c0Sjca 7129c0437c0Sjca for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 7139c0437c0Sjca if (ifa->ifa_addr == NULL) 7149c0437c0Sjca continue; 7159c0437c0Sjca 7169c0437c0Sjca switch (ifa->ifa_addr->sa_family) { 71752b8ecd4Sflorian case PF_LINK: 71852b8ecd4Sflorian /* AF_LINK comes before inet / inet6 on an interface */ 71952b8ecd4Sflorian ifa_data = (struct if_data *)ifa->ifa_data; 72052b8ecd4Sflorian ifa_rtable = ifa_data->ifi_rdomain; 72152b8ecd4Sflorian break; 7229c0437c0Sjca case PF_INET: 72352b8ecd4Sflorian if (ifa_rtable != rtable) 72452b8ecd4Sflorian continue; 72552b8ecd4Sflorian 726d6498094Sjca sinp = (struct sockaddr_in *)ifa->ifa_addr; 727d6498094Sjca 728873f6271Sjca if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) 729d6498094Sjca continue; 730d6498094Sjca 731abe78e02Sjca as->as_flags &= ~ASYNC_NO_INET; 7329c0437c0Sjca break; 7339c0437c0Sjca case PF_INET6: 73452b8ecd4Sflorian if (ifa_rtable != rtable) 73552b8ecd4Sflorian continue; 73652b8ecd4Sflorian 7379c0437c0Sjca sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; 7389c0437c0Sjca 739d6498094Sjca if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) 740d6498094Sjca continue; 741d6498094Sjca 7429c0437c0Sjca if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) 7439c0437c0Sjca continue; 7449c0437c0Sjca 745abe78e02Sjca as->as_flags &= ~ASYNC_NO_INET6; 7469c0437c0Sjca break; 7479c0437c0Sjca } 7489c0437c0Sjca } 7499c0437c0Sjca 7509c0437c0Sjca freeifaddrs(ifa0); 7519c0437c0Sjca 7529c0437c0Sjca return (0); 7539c0437c0Sjca } 754