static int gethostnamadr_async_run(struct async *as, struct async_res *ar) { int r, type; FILE *f; char dname[MAXDNAME], *data; next: switch(as->as_state) { case ASR_STATE_INIT: if (as->as.hostnamadr.family != AF_INET && as->as.hostnamadr.family != AF_INET6) { ar->ar_h_errno = NETDB_INTERNAL; ar->ar_errno = EAFNOSUPPORT; async_set_state(as, ASR_STATE_HALT); break; } if ((as->as.hostnamadr.family == AF_INET && as->as.hostnamadr.addrlen != INADDRSZ) || (as->as.hostnamadr.family == AF_INET6 && as->as.hostnamadr.addrlen != IN6ADDRSZ)) { ar->ar_h_errno = NETDB_INTERNAL; ar->ar_errno = EINVAL; async_set_state(as, ASR_STATE_HALT); break; } if (as->as_type == ASR_GETHOSTBYNAME) async_set_state(as, ASR_STATE_NEXT_DOMAIN); else async_set_state(as, ASR_STATE_NEXT_DB); break; case ASR_STATE_NEXT_DOMAIN: r = asr_iter_domain(as, as->as.hostnamadr.name, dname, sizeof(dname)); if (r == -1) { async_set_state(as, ASR_STATE_NOT_FOUND); break; } if (as->as.hostnamadr.dname) free(as->as.hostnamadr.dname); if ((as->as.hostnamadr.dname = strdup(dname)) == NULL) { ar->ar_h_errno = NETDB_INTERNAL; ar->ar_errno = errno; async_set_state(as, ASR_STATE_HALT); } as->as_db_idx = 0; async_set_state(as, ASR_STATE_NEXT_DB); break; case ASR_STATE_NEXT_DB: if (asr_iter_db(as) == -1) { if (as->as_type == ASR_GETHOSTBYNAME) async_set_state(as, ASR_STATE_NEXT_DOMAIN); else async_set_state(as, ASR_STATE_NOT_FOUND); break; } switch(AS_DB(as)) { case ASR_DB_DNS: /* Create a subquery to do the DNS lookup */ if (as->as_type == ASR_GETHOSTBYNAME) { type = (as->as.hostnamadr.family == AF_INET) ? T_A : T_AAAA; as->as.hostnamadr.subq = res_query_async_ctx( as->as.hostnamadr.dname, C_IN, type, NULL, 0, as->as_ctx); } else { addr_as_fqdn(as->as.hostnamadr.addr, as->as.hostnamadr.family, dname, sizeof(dname)); as->as.hostnamadr.subq = res_query_async_ctx( dname, C_IN, T_PTR, NULL, 0, as->as_ctx); } if (as->as.hostnamadr.subq == NULL) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_SUBQUERY); break; case ASR_DB_FILE: /* Try to find a match in the host file */ if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL) break; if (as->as_type == ASR_GETHOSTBYNAME) data = as->as.hostnamadr.dname; else data = as->as.hostnamadr.addr; ar->ar_hostent = hostent_file_match(f, as->as_type, as->as.hostnamadr.family, data, as->as.hostnamadr.addrlen); fclose(f); if (ar->ar_hostent == NULL) { if (errno) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; async_set_state(as, ASR_STATE_HALT); } /* otherwise not found */ break; } ar->ar_h_errno = NETDB_SUCCESS; async_set_state(as, ASR_STATE_HALT); break; #ifdef YP case ASR_DB_YP: /* IPv4 only */ if (as->as.hostnamadr.family != AF_INET) break; if (as->as_type == ASR_GETHOSTBYNAME) data = as->as.hostnamadr.dname; else data = as->as.hostnamadr.addr; ar->ar_hostent = _yp_gethostnamadr(as->as_type, data); if (ar->ar_hostent == NULL) { if (errno) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; async_set_state(as, ASR_STATE_HALT); } /* otherwise not found */ break; } ar->ar_h_errno = NETDB_SUCCESS; async_set_state(as, ASR_STATE_HALT); break; #endif } break; case ASR_STATE_SUBQUERY: /* Run the DNS subquery. */ if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND) return (ASYNC_COND); /* Done. */ as->as.hostnamadr.subq = NULL; if (ar->ar_datalen == -1) { async_set_state(as, ASR_STATE_NEXT_DB); break; } /* If we got a packet but no anwser, use the next DB. */ if (ar->ar_count == 0) { free(ar->ar_data); async_set_state(as, ASR_STATE_NEXT_DB); break; } /* Read the hostent from the packet. */ ar->ar_hostent = hostent_from_packet(as->as_type, as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen); free(ar->ar_data); if (ar->ar_hostent == NULL) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; async_set_state(as, ASR_STATE_HALT); break; } if (as->as_type == ASR_GETHOSTBYADDR) { if (hostent_add_addr(ar->ar_hostent, as->as.hostnamadr.addr, as->as.hostnamadr.addrlen) == -1) { free(ar->ar_hostent); ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; async_set_state(as, ASR_STATE_HALT); break; } } /* * No address found in the dns packet. The blocking version * reports this as an error. */ if (as->as_type == ASR_GETHOSTBYNAME && ar->ar_hostent->h_addr_list[0] == NULL) { free(ar->ar_hostent); async_set_state(as, ASR_STATE_NEXT_DB); break; } ar->ar_h_errno = NETDB_SUCCESS; async_set_state(as, ASR_STATE_HALT); break; case ASR_STATE_NOT_FOUND: ar->ar_errno = 0; ar->ar_h_errno = HOST_NOT_FOUND; async_set_state(as, ASR_STATE_HALT); break; case ASR_STATE_HALT: if (ar->ar_h_errno) ar->ar_hostent = NULL; else ar->ar_errno = 0; 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; }
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; }