/* * Same as above, but run in a loop that handles the fd conditions result. */ int asr_run_sync(struct asr_query *as, struct asr_result *ar) { struct pollfd fds[1]; struct timespec pollstart, pollend, elapsed; int timeout, r, p, saved_errno = errno; while ((r = asr_run(as, ar)) == ASYNC_COND) { fds[0].fd = ar->ar_fd; fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT; timeout = ar->ar_timeout; again: if (clock_gettime(CLOCK_MONOTONIC, &pollstart)) break; p = poll(fds, 1, timeout); if (p == -1 && errno == EINTR) { if (clock_gettime(CLOCK_MONOTONIC, &pollend)) break; timespecsub(&pollend, &pollstart, &elapsed); timeout -= (elapsed.tv_sec * 1000) + (elapsed.tv_nsec / 1000000); if (timeout < 1) break; goto again; } /* * Otherwise, just ignore the error and let asr_run() * catch the failure. */ } errno = saved_errno; return (r); }
static int getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) { #ifdef YP static char *domain = NULL; char *res; int len; char *name; #endif 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; 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; } 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) != 0) { ar->ar_gai_errno = EAI_FAIL; 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) { ar->ar_gai_errno = EAI_BADHINTS; 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 = 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); } 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.ai.flags & ASYNC_NO_INET) { async_set_state(as, ASR_STATE_NEXT_FAMILY); break; } else if (family == AF_INET6 && as->as.ai.flags & ASYNC_NO_INET6) { async_set_state(as, ASR_STATE_NEXT_FAMILY); break; } as->as.ai.subq = res_query_async_ctx(as->as.ai.fqdn, C_IN, (family == AF_INET6) ? T_AAAA : T_A, as->as_ctx); if (as->as.ai.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; #ifdef YP case ASR_DB_YP: if (!domain && _yp_check(&domain) == 0) { 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; name = as->as.ai.hostname; /* XXX * ipnodes.byname could also contain IPv4 address */ r = yp_match(domain, (family == AF_INET6) ? "ipnodes.byname" : "hosts.byname", name, strlen(name), &res, &len); if (r == 0) { r = addrinfo_from_yp(as, family, res); free(res); 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); break; } } async_set_state(as, ASR_STATE_NEXT_FAMILY); break; #endif default: async_set_state(as, ASR_STATE_NEXT_DB); } break; case ASR_STATE_SUBQUERY: if ((r = asr_run(as->as.ai.subq, ar)) == ASYNC_COND) return (ASYNC_COND); as->as.ai.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.ai.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; }
static int getnameinfo_async_run(struct asr_query *as, struct asr_result *ar) { void *addr; socklen_t addrlen; int r; next: switch (as->as_state) { case ASR_STATE_INIT: /* Make sure the parameters are all valid. */ if (as->as.ni.sa.sa.sa_family != AF_INET && as->as.ni.sa.sa.sa_family != AF_INET6) { ar->ar_gai_errno = EAI_FAMILY; async_set_state(as, ASR_STATE_HALT); break; } if ((as->as.ni.sa.sa.sa_family == AF_INET && (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain))) || (as->as.ni.sa.sa.sa_family == AF_INET6 && (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain6)))) { ar->ar_gai_errno = EAI_FAIL; async_set_state(as, ASR_STATE_HALT); break; } /* Set the service name first, if needed. */ if (_servname(as) == -1) { ar->ar_gai_errno = EAI_OVERFLOW; async_set_state(as, ASR_STATE_HALT); break; } if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) { ar->ar_gai_errno = 0; async_set_state(as, ASR_STATE_HALT); break; } if (as->as.ni.flags & NI_NUMERICHOST) { if (_numerichost(as) == -1) { if (errno == ENOMEM) ar->ar_gai_errno = EAI_MEMORY; else if (errno == ENOSPC) ar->ar_gai_errno = EAI_OVERFLOW; else { ar->ar_errno = errno; ar->ar_gai_errno = EAI_SYSTEM; } } else ar->ar_gai_errno = 0; async_set_state(as, ASR_STATE_HALT); break; } if (as->as.ni.sa.sa.sa_family == AF_INET) { addrlen = sizeof(as->as.ni.sa.sain.sin_addr); addr = &as->as.ni.sa.sain.sin_addr; } else { addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr); addr = &as->as.ni.sa.sain6.sin6_addr; } /* * Create a subquery to lookup the address. */ as->as.ni.subq = gethostbyaddr_async_ctx(addr, addrlen, as->as.ni.sa.sa.sa_family, as->as_ctx); if (as->as.ni.subq == NULL) { ar->ar_gai_errno = EAI_MEMORY; async_set_state(as, ASR_STATE_HALT); break; } async_set_state(as, ASR_STATE_SUBQUERY); break; case ASR_STATE_SUBQUERY: if ((r = asr_run(as->as.ni.subq, ar)) == ASYNC_COND) return (ASYNC_COND); /* * Request done. */ as->as.ni.subq = NULL; if (ar->ar_hostent == NULL) { if (as->as.ni.flags & NI_NAMEREQD) { ar->ar_gai_errno = EAI_NONAME; } else if (_numerichost(as) == -1) { if (errno == ENOMEM) ar->ar_gai_errno = EAI_MEMORY; else if (errno == ENOSPC) ar->ar_gai_errno = EAI_OVERFLOW; else { ar->ar_errno = errno; ar->ar_gai_errno = EAI_SYSTEM; } } else ar->ar_gai_errno = 0; } else { if (strlcpy(as->as.ni.hostname, ar->ar_hostent->h_name, as->as.ni.hostnamelen) >= as->as.ni.hostnamelen) ar->ar_gai_errno = EAI_OVERFLOW; else ar->ar_gai_errno = 0; free(ar->ar_hostent); } async_set_state(as, ASR_STATE_HALT); break; case ASR_STATE_HALT: return (ASYNC_DONE); default: ar->ar_errno = EOPNOTSUPP; ar->ar_gai_errno = EAI_SYSTEM; async_set_state(as, ASR_STATE_HALT); break; } goto next; }
static int gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar) { struct hostent_ext *h; int r, type, saved_errno; FILE *f; char name[MAXDNAME], *data, addr[16], *c; 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) { if (as->as.hostnamadr.name[0] == '\0') { ar->ar_h_errno = NO_DATA; async_set_state(as, ASR_STATE_HALT); break; } /* Name might be an IP address string */ for (c = as->as.hostnamadr.name; *c; c++) if (!isdigit((unsigned char)*c) && *c != '.' && *c != ':') break; if (*c == 0 && inet_pton(as->as.hostnamadr.family, as->as.hostnamadr.name, addr) == 1) { h = hostent_from_addr(as->as.hostnamadr.family, as->as.hostnamadr.name, addr); if (h == NULL) { ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; } else { ar->ar_hostent = &h->h; ar->ar_h_errno = NETDB_SUCCESS; } 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; } 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_search_async_ctx( as->as.hostnamadr.name, C_IN, type, as->as_ctx); } else { _asr_addr_as_fqdn(as->as.hostnamadr.addr, as->as.hostnamadr.family, name, sizeof(name)); as->as.hostnamadr.subq = _res_query_async_ctx( name, C_IN, T_PTR, 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(_PATH_HOSTS, "re")) == NULL) break; if (as->as_type == ASR_GETHOSTBYNAME) data = as->as.hostnamadr.name; else data = as->as.hostnamadr.addr; h = hostent_file_match(f, as->as_type, as->as.hostnamadr.family, data, as->as.hostnamadr.addrlen); saved_errno = errno; fclose(f); errno = saved_errno; if (h == 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_hostent = &h->h; ar->ar_h_errno = NETDB_SUCCESS; async_set_state(as, ASR_STATE_HALT); break; } break; case ASR_STATE_SUBQUERY: /* Run the DNS subquery. */ if ((r = asr_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); as->as.hostnamadr.subq_h_errno = ar->ar_h_errno; async_set_state(as, ASR_STATE_NEXT_DB); break; } /* Read the hostent from the packet. */ h = hostent_from_packet(as->as_type, as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen); free(ar->ar_data); if (h == 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(h, as->as.hostnamadr.addr, as->as.hostnamadr.addrlen) == -1) { free(h); ar->ar_errno = errno; ar->ar_h_errno = NETDB_INTERNAL; async_set_state(as, ASR_STATE_HALT); break; } } /* * No valid hostname or address found in the dns packet. * Ignore it. */ if ((as->as_type == ASR_GETHOSTBYNAME && h->h.h_addr_list[0] == NULL) || h->h.h_name == NULL) { free(h); async_set_state(as, ASR_STATE_NEXT_DB); break; } ar->ar_hostent = &h->h; ar->ar_h_errno = NETDB_SUCCESS; async_set_state(as, ASR_STATE_HALT); break; case ASR_STATE_NOT_FOUND: ar->ar_errno = 0; if (as->as.hostnamadr.subq_h_errno) ar->ar_h_errno = as->as.hostnamadr.subq_h_errno; else 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; }