/* * Parse a resolv.conf(5) nameserver string into a sockaddr. */ static int asr_parse_nameserver(struct sockaddr *sa, const char *s) { in_port_t portno = 53; if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1) return (-1); if (sa->sa_family == PF_INET) ((struct sockaddr_in *)sa)->sin_port = htons(portno); else if (sa->sa_family == PF_INET6) ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); return (0); }
static int addrinfo_from_file(struct asr_query *as, int family, FILE *f) { char *tokens[MAXTOKEN], *c, buf[ASR_BUFSIZ + 1]; int n, i; union { struct sockaddr sa; struct sockaddr_in sain; struct sockaddr_in6 sain6; } u; for (;;) { n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); if (n == -1) break; /* ignore errors reading the file */ for (i = 1; i < n; i++) { if (strcasecmp(as->as.ai.hostname, tokens[i])) continue; if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) continue; break; } if (i == n) continue; if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) c = tokens[1]; else c = NULL; if (addrinfo_add(as, &u.sa, c)) return (-1); /* errno set */ } return (0); }
static int getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) { char fqdn[MAXDNAME]; const char *str; struct addrinfo *ai; int i, family, r; FILE *f; union { struct sockaddr sa; struct sockaddr_in sain; struct sockaddr_in6 sain6; } sa; next: switch (as->as_state) { case ASR_STATE_INIT: /* * First, make sure the parameters are valid. */ as->as_count = 0; if (as->as.ai.hostname == NULL && as->as.ai.servname == NULL) { ar->ar_gai_errno = EAI_NONAME; async_set_state(as, ASR_STATE_HALT); break; } if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') { ar->ar_gai_errno = EAI_NODATA; async_set_state(as, ASR_STATE_HALT); break; } ai = &as->as.ai.hints; #ifdef EAI_BADHINTS if (ai->ai_addrlen || ai->ai_canonname || ai->ai_addr || ai->ai_next) { ar->ar_gai_errno = EAI_BADHINTS; async_set_state(as, ASR_STATE_HALT); break; } #endif if (ai->ai_flags & ~AI_MASK || (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { ar->ar_gai_errno = EAI_BADFLAGS; async_set_state(as, ASR_STATE_HALT); break; } if (ai->ai_family != PF_UNSPEC && ai->ai_family != PF_INET && ai->ai_family != PF_INET6) { ar->ar_gai_errno = EAI_FAMILY; async_set_state(as, ASR_STATE_HALT); break; } if (ai->ai_socktype && ai->ai_socktype != SOCK_DGRAM && ai->ai_socktype != SOCK_STREAM && ai->ai_socktype != SOCK_RAW) { ar->ar_gai_errno = EAI_SOCKTYPE; async_set_state(as, ASR_STATE_HALT); break; } if (ai->ai_socktype == SOCK_RAW && get_port(as->as.ai.servname, NULL, 1) != 0) { ar->ar_gai_errno = EAI_SERVICE; async_set_state(as, ASR_STATE_HALT); break; } /* Restrict result set to configured address families */ if (ai->ai_flags & AI_ADDRCONFIG) { if (addrconfig_setup(as) == -1) { ar->ar_errno = errno; ar->ar_gai_errno = EAI_SYSTEM; async_set_state(as, ASR_STATE_HALT); break; } } /* Make sure there is at least a valid combination */ for (i = 0; matches[i].family != -1; i++) if (MATCH_FAMILY(ai->ai_family, i) && MATCH_SOCKTYPE(ai->ai_socktype, i) && MATCH_PROTO(ai->ai_protocol, i)) break; if (matches[i].family == -1) { #ifdef EAI_BADHINTS ar->ar_gai_errno = EAI_BADHINTS; #else ar->ar_gai_errno = EAI_FAIL; #endif async_set_state(as, ASR_STATE_HALT); break; } if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", as->as.ai.hints.ai_flags & AI_NUMERICSERV); if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", as->as.ai.hints.ai_flags & AI_NUMERICSERV); if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || (ai->ai_protocol && (as->as.ai.port_udp == -1 || as->as.ai.port_tcp == -1))) { ar->ar_gai_errno = EAI_SERVICE; async_set_state(as, ASR_STATE_HALT); break; } ar->ar_gai_errno = 0; /* If hostname is NULL, use local address */ if (as->as.ai.hostname == NULL) { for (family = iter_family(as, 1); family != -1; family = iter_family(as, 0)) { /* * We could use statically built sockaddrs for * those, rather than parsing over and over. */ if (family == PF_INET) str = (ai->ai_flags & AI_PASSIVE) ? \ "0.0.0.0" : "127.0.0.1"; else /* PF_INET6 */ str = (ai->ai_flags & AI_PASSIVE) ? \ "::" : "::1"; /* This can't fail */ _asr_sockaddr_from_str(&sa.sa, family, str); if ((r = addrinfo_add(as, &sa.sa, NULL))) { ar->ar_gai_errno = r; break; } } if (ar->ar_gai_errno == 0 && as->as_count == 0) { ar->ar_gai_errno = EAI_NODATA; } async_set_state(as, ASR_STATE_HALT); break; } /* Try numeric addresses first */ for (family = iter_family(as, 1); family != -1; family = iter_family(as, 0)) { if (_asr_sockaddr_from_str(&sa.sa, family, as->as.ai.hostname) == -1) continue; if ((r = addrinfo_add(as, &sa.sa, NULL))) ar->ar_gai_errno = r; break; } if (ar->ar_gai_errno || as->as_count) { async_set_state(as, ASR_STATE_HALT); break; } if (ai->ai_flags & AI_NUMERICHOST) { ar->ar_gai_errno = EAI_NONAME; async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_NEXT_DB); break; case ASR_STATE_NEXT_DB: if (_asr_iter_db(as) == -1) { async_set_state(as, ASR_STATE_NOT_FOUND); break; } as->as_family_idx = 0; async_set_state(as, ASR_STATE_SAME_DB); break; case ASR_STATE_NEXT_FAMILY: as->as_family_idx += 1; if (as->as.ai.hints.ai_family != AF_UNSPEC || AS_FAMILY(as) == -1) { /* The family was specified, or we have tried all * families with this DB. */ if (as->as_count) { ar->ar_gai_errno = 0; async_set_state(as, ASR_STATE_HALT); } else async_set_state(as, ASR_STATE_NEXT_DOMAIN); break; } async_set_state(as, ASR_STATE_SAME_DB); break; case ASR_STATE_NEXT_DOMAIN: /* domain search is only for dns */ if (AS_DB(as) != ASR_DB_DNS) { async_set_state(as, ASR_STATE_NEXT_DB); break; } as->as_family_idx = 0; free(as->as.ai.fqdn); as->as.ai.fqdn = NULL; r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); if (r == -1) { async_set_state(as, ASR_STATE_NEXT_DB); break; } if (r == 0) { ar->ar_gai_errno = EAI_FAIL; async_set_state(as, ASR_STATE_HALT); break; } as->as.ai.fqdn = strdup(fqdn); if (as->as.ai.fqdn == NULL) { ar->ar_gai_errno = EAI_MEMORY; async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_SAME_DB); break; case ASR_STATE_SAME_DB: /* query the current DB again */ switch (AS_DB(as)) { case ASR_DB_DNS: if (as->as.ai.fqdn == NULL) { /* First try, initialize domain iteration */ as->as_dom_flags = 0; as->as_dom_step = DOM_INIT; async_set_state(as, ASR_STATE_NEXT_DOMAIN); break; } family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? AS_FAMILY(as) : as->as.ai.hints.ai_family; if (family == AF_INET && as->as_flags & ASYNC_NO_INET) { async_set_state(as, ASR_STATE_NEXT_FAMILY); break; } else if (family == AF_INET6 && as->as_flags & ASYNC_NO_INET6) { async_set_state(as, ASR_STATE_NEXT_FAMILY); break; } as->as_subq = _res_query_async_ctx(as->as.ai.fqdn, C_IN, (family == AF_INET6) ? T_AAAA : T_A, as->as_ctx); if (as->as_subq == NULL) { if (errno == ENOMEM) ar->ar_gai_errno = EAI_MEMORY; else ar->ar_gai_errno = EAI_FAIL; async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_SUBQUERY); break; case ASR_DB_FILE: f = fopen(_PATH_HOSTS, "re"); if (f == NULL) { async_set_state(as, ASR_STATE_NEXT_DB); break; } family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? AS_FAMILY(as) : as->as.ai.hints.ai_family; r = addrinfo_from_file(as, family, f); if (r == -1) { if (errno == ENOMEM) ar->ar_gai_errno = EAI_MEMORY; else ar->ar_gai_errno = EAI_FAIL; async_set_state(as, ASR_STATE_HALT); } else async_set_state(as, ASR_STATE_NEXT_FAMILY); fclose(f); break; default: async_set_state(as, ASR_STATE_NEXT_DB); } break; case ASR_STATE_SUBQUERY: if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) return (ASYNC_COND); as->as_subq = NULL; if (ar->ar_datalen == -1) { async_set_state(as, ASR_STATE_NEXT_FAMILY); break; } r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); if (r == -1) { if (errno == ENOMEM) ar->ar_gai_errno = EAI_MEMORY; else ar->ar_gai_errno = EAI_FAIL; async_set_state(as, ASR_STATE_HALT); } else async_set_state(as, ASR_STATE_NEXT_FAMILY); free(ar->ar_data); break; case ASR_STATE_NOT_FOUND: /* No result found. Maybe we can try again. */ if (as->as_flags & ASYNC_AGAIN) ar->ar_gai_errno = EAI_AGAIN; else ar->ar_gai_errno = EAI_NODATA; async_set_state(as, ASR_STATE_HALT); break; case ASR_STATE_HALT: if (ar->ar_gai_errno == 0) { ar->ar_count = as->as_count; ar->ar_addrinfo = as->as.ai.aifirst; as->as.ai.aifirst = NULL; } else { ar->ar_count = 0; ar->ar_addrinfo = NULL; } return (ASYNC_DONE); default: ar->ar_errno = EOPNOTSUPP; ar->ar_gai_errno = EAI_SYSTEM; async_set_state(as, ASR_STATE_HALT); break; } goto next; }