/* * Use the sockaddr at "sa" to extend the result list on the "as" context, * with the specified canonical name "cname". This function adds one * entry per protocol/socktype match. */ static int add_sockaddr(struct async *as, struct sockaddr *sa, const char *cname) { struct addrinfo *ai; int i, port; for(i = 0; matches[i].family != -1; i++) { if (matches[i].family != sa->sa_family || !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) continue; if (matches[i].protocol == IPPROTO_TCP) port = as->as.ai.port_tcp; else if (matches[i].protocol == IPPROTO_UDP) port = as->as.ai.port_udp; else port = 0; ai = calloc(1, sizeof(*ai) + sa->sa_len); if (ai == NULL) return (EAI_MEMORY); ai->ai_family = sa->sa_family; ai->ai_socktype = matches[i].socktype; ai->ai_protocol = matches[i].protocol; ai->ai_addrlen = sa->sa_len; ai->ai_addr = (void*)(ai + 1); if (cname && as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { if ((ai->ai_canonname = strdup(cname)) == NULL) { free(ai); return (EAI_MEMORY); } } memmove(ai->ai_addr, sa, sa->sa_len); if (sa->sa_family == PF_INET) ((struct sockaddr_in *)ai->ai_addr)->sin_port = htons(port); else if (sa->sa_family == PF_INET6) ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(port); if (as->as.ai.aifirst == NULL) as->as.ai.aifirst = ai; if (as->as.ai.ailast) as->as.ai.ailast->ai_next = ai; as->as.ai.ailast = ai; as->as_count += 1; } return (0); }
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; }
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; }
/* * Use the sockaddr at "sa" to extend the result list on the "as" context, * with the specified canonical name "cname". This function adds one * entry per protocol/socktype match. */ static int addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) { struct addrinfo *ai; int i, port, proto; for (i = 0; matches[i].family != -1; i++) { if (matches[i].family != sa->sa_family || !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) continue; proto = as->as.ai.hints.ai_protocol; if (!proto) proto = matches[i].protocol; if (proto == IPPROTO_TCP) port = as->as.ai.port_tcp; else if (proto == IPPROTO_UDP) port = as->as.ai.port_udp; else port = 0; /* servname specified, but not defined for this protocol */ if (port == -1) continue; ai = calloc(1, sizeof(*ai) + SA_LEN(sa)); if (ai == NULL) return (EAI_MEMORY); ai->ai_family = sa->sa_family; ai->ai_socktype = matches[i].socktype; ai->ai_protocol = proto; ai->ai_flags = as->as.ai.hints.ai_flags; ai->ai_addrlen = SA_LEN(sa); ai->ai_addr = (void *)(ai + 1); if (cname && as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { if ((ai->ai_canonname = strdup(cname)) == NULL) { free(ai); return (EAI_MEMORY); } } memmove(ai->ai_addr, sa, SA_LEN(sa)); if (sa->sa_family == PF_INET) ((struct sockaddr_in *)ai->ai_addr)->sin_port = htons(port); else if (sa->sa_family == PF_INET6) ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(port); if (as->as.ai.aifirst == NULL) as->as.ai.aifirst = ai; if (as->as.ai.ailast) as->as.ai.ailast->ai_next = ai; as->as.ai.ailast = ai; as->as_count += 1; } return (0); }