int sockaddr_from_str(struct sockaddr *sa, int family, const char *str) { struct in_addr ina; struct in6_addr in6a; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; switch (family) { case PF_UNSPEC: if (sockaddr_from_str(sa, PF_INET, str) == 0) return (0); return sockaddr_from_str(sa, PF_INET6, str); case PF_INET: if (inet_pton(PF_INET, str, &ina) != 1) return (-1); sin = (struct sockaddr_in *)sa; memset(sin, 0, sizeof *sin); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = PF_INET; sin->sin_addr.s_addr = ina.s_addr; return (0); case PF_INET6: if (inet_pton(PF_INET6, str, &in6a) != 1) return (-1); sin6 = (struct sockaddr_in6 *)sa; memset(sin6, 0, sizeof *sin6); sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = PF_INET6; sin6->sin6_addr = in6a; return (0); default: break; } return (-1); }
static int getaddrinfo_async_run(struct async *as, struct async_res *ar) { const char *str; struct addrinfo *ai; int i, family, r; char fqdn[MAXDNAME]; 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; async_set_state(as, ASR_STATE_HALT); ar->ar_errno = 0; ar->ar_h_errno = NETDB_SUCCESS; ar->ar_gai_errno = 0; if (as->as.ai.hostname == NULL && as->as.ai.servname == NULL) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_NONAME; break; } ai = &as->as.ai.hints; if (ai->ai_addrlen || ai->ai_canonname || ai->ai_addr || ai->ai_next) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_BADHINTS; break; } if (ai->ai_flags & ~AI_MASK || (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_BADFLAGS; break; } if (ai->ai_family != PF_UNSPEC && ai->ai_family != PF_INET && ai->ai_family != PF_INET6) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_FAMILY; break; } if (ai->ai_socktype && ai->ai_socktype != SOCK_DGRAM && ai->ai_socktype != SOCK_STREAM && ai->ai_socktype != SOCK_RAW) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_SOCKTYPE; break; } if (ai->ai_protocol && ai->ai_protocol != IPPROTO_UDP && ai->ai_protocol != IPPROTO_TCP) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_PROTOCOL; break; } if (ai->ai_socktype == SOCK_RAW && as->as.ai.servname != NULL) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_SERVICE; 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) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_BADHINTS; break; } if (as->as.ai.servname) { as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", as->as.ai.hints.ai_flags & AI_NUMERICSERV); 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 < 0 || as->as.ai.port_udp < 0) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_SERVICE; break; } } /* 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 */ sockaddr_from_str(&sa.sa, family, str); if ((r = add_sockaddr(as, &sa.sa, NULL))) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; ar->ar_gai_errno = r; async_set_state(as, ASR_STATE_HALT); break; } } if (ar->ar_gai_errno == 0 && as->as_count == 0) { ar->ar_h_errno = NO_DATA; ar->ar_gai_errno = EAI_NODATA; } break; } /* Try numeric addresses first */ for(family = iter_family(as, 1); family != -1; family = iter_family(as, 0)) { if (sockaddr_from_str(&sa.sa, family, as->as.ai.hostname) == -1) continue; if ((r = add_sockaddr(as, &sa.sa, NULL))) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; ar->ar_gai_errno = r; async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_HALT); break; } if (ar->ar_gai_errno || as->as_count) break; if (ai->ai_flags & AI_NUMERICHOST) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_FAIL; async_set_state(as, ASR_STATE_HALT); break; } /* Starting domain lookup */ async_set_state(as, ASR_STATE_SEARCH_DOMAIN); break; case ASR_STATE_SEARCH_DOMAIN: r = asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); if (r == -1) { async_set_state(as, ASR_STATE_NOT_FOUND); break; } if (r > (int)sizeof(fqdn)) { ar->ar_errno = EINVAL; ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_OVERFLOW; async_set_state(as, ASR_STATE_HALT); break; } /* * Create a subquery to lookup the host addresses. * We use the special hostaddr_async() API, which has the * nice property of honoring the "lookup" and "family" keyword * in the configuration, thus returning the right address * families in the right order, and thus fixing the current * getaddrinfo() feature documented in the BUGS section of * resolver.conf(5). */ as->as.ai.subq = hostaddr_async_ctx(fqdn, as->as.ai.hints.ai_family, as->as.ai.hints.ai_flags, as->as_ctx); if (as->as.ai.subq == NULL) { ar->ar_errno = errno; if (errno == EINVAL) { ar->ar_h_errno = NO_RECOVERY; ar->ar_gai_errno = EAI_FAIL; } else { ar->ar_h_errno = NETDB_INTERNAL; ar->ar_gai_errno = EAI_MEMORY; } async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_LOOKUP_DOMAIN); break; case ASR_STATE_LOOKUP_DOMAIN: /* Run the subquery */ if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND) return (ASYNC_COND); /* Got one more address, use it to extend the result list. */ if (r == ASYNC_YIELD) { if ((r = add_sockaddr(as, &ar->ar_sa.sa, ar->ar_cname))) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; ar->ar_gai_errno = r; async_set_state(as, ASR_STATE_HALT); } if (ar->ar_cname) free(ar->ar_cname); break; } /* * The subquery is done. Stop there if we have at least one * answer. */ as->as.ai.subq = NULL; if (ar->ar_count) { async_set_state(as, ASR_STATE_HALT); break; } /* * No anwser for this domain, but we might be suggested to * try again later, so remember this. Then search the next * domain. */ if (ar->ar_gai_errno == EAI_AGAIN) as->as.ai.flags |= ASYNC_AGAIN; async_set_state(as, ASR_STATE_SEARCH_DOMAIN); break; case ASR_STATE_NOT_FOUND: /* * No result found. Maybe we can try again. */ ar->ar_errno = 0; if (as->as.ai.flags & ASYNC_AGAIN) { ar->ar_h_errno = TRY_AGAIN; ar->ar_gai_errno = EAI_AGAIN; } else { ar->ar_h_errno = NO_DATA; ar->ar_gai_errno = EAI_NODATA; } async_set_state(as, ASR_STATE_HALT); break; case ASR_STATE_HALT: /* Set the results. */ 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_h_errno = NETDB_INTERNAL; ar->ar_gai_errno = EAI_SYSTEM; async_set_state(as, ASR_STATE_HALT); break; } goto next; }