/* * Fill the hostent from the given DNS packet. */ static int hostent_from_packet(struct hostent *h, int action, char *pkt, size_t pktlen) { struct packed p; struct header hdr; struct query q; struct rr rr; int r; packed_init(&p, pkt, pktlen); unpack_header(&p, &hdr); for(; hdr.qdcount; hdr.qdcount--) unpack_query(&p, &q); for(; hdr.ancount; hdr.ancount--) { unpack_rr(&p, &rr); if (rr.rr_class != C_IN) continue; switch (rr.rr_type) { case T_CNAME: if (action == ASR_GETHOSTBYNAME) r = hostent_add_alias(h, rr.rr_dname, 1); else r = hostent_set_cname(h, rr.rr_dname, 1); break; case T_PTR: if (action != ASR_GETHOSTBYADDR) continue; r = hostent_set_cname(h, rr.rr.ptr.ptrname, 1); /* XXX See if we need MULTI_PTRS_ARE_ALIASES */ break; case T_A: if (h->h_addrtype != AF_INET) break; r = hostent_set_cname(h, rr.rr_dname, 1); r = hostent_add_addr(h, &rr.rr.in_a.addr, 4); break; case T_AAAA: if (h->h_addrtype != AF_INET6) break; r = hostent_set_cname(h, rr.rr_dname, 1); r = hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16); break; } } return (0); }
struct hostent * gethostbyaddr(const void *addr, socklen_t len, int type) { int ret; struct hostent *he; struct dns_result result; struct dns_rr *rr; struct in_addr in; char hostaddr[] = "xxx.xxx.xxx.xxx.in-addr.arpa"; size_t i; if ((type != AF_INET) || (len != sizeof in)) { return NULL; /* error: IPv4 supported only */ } memcpy(&in, addr, sizeof in); in.s_addr = swab32(in.s_addr); if (0 > snprintf(&hostaddr[0], ARRAY_SIZE(hostaddr), "%s.in-addr.arpa", inet_ntoa(in))) { return NULL; } ret = dns_query(&hostaddr[0], DNS_RR_TYPE_PTR, DNS_RR_CLASS_IN, &result); if (ret != 0) { h_errno = HOST_NOT_FOUND; return NULL; } if (((he = hostent_create()) == NULL) || (hostent_set_addr_info(he, AF_INET, len) != 0) || (hostent_add_addr(he, addr) != 0)) { dns_result_free(&result); return NULL; } for (i = 0, rr = result.an; i < result.ancount; ++i, ++rr) { switch (rr->rtype) { default: ret = 0; break; case DNS_RR_TYPE_PTR: ret = hostent_set_name(he, &rr->rdata.ptr.ptrdname[0]); break; } if (ret != 0) { dns_result_free(&result); return NULL; } } dns_result_free(&result); return he; }
/* * Lookup the first matching entry in the hostfile, either by address or by * name depending on reqtype, and build a hostent from the line. */ static struct hostent_ext * hostent_file_match(FILE *f, int reqtype, int family, const char *data, int datalen) { char *tokens[MAXTOKEN], addr[16], buf[ASR_BUFSIZ + 1]; struct hostent_ext *h; int n, i; for (;;) { n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); if (n == -1) { errno = 0; /* ignore errors reading the file */ return (NULL); } /* there must be an address and at least one name */ if (n < 2) continue; if (reqtype == ASR_GETHOSTBYNAME) { for (i = 1; i < n; i++) { if (strcasecmp(data, tokens[i])) continue; if (inet_pton(family, tokens[0], addr) == 1) goto found; } } else { if (inet_pton(family, tokens[0], addr) == 1 && memcmp(addr, data, datalen) == 0) goto found; } } found: if ((h = hostent_alloc(family)) == NULL) return (NULL); if (hostent_set_cname(h, tokens[1], 0) == -1) goto fail; for (i = 2; i < n; i ++) if (hostent_add_alias(h, tokens[i], 0) == -1) goto fail; if (hostent_add_addr(h, addr, h->h.h_length) == -1) goto fail; return (h); fail: free(h); return (NULL); }
/* * Create a hostent from a numeric address string. */ static struct hostent_ext * hostent_from_addr(int family, const char *name, const char *addr) { struct hostent_ext *h; if ((h = hostent_alloc(family)) == NULL) return (NULL); if (hostent_set_cname(h, name, 0) == -1) goto fail; if (hostent_add_addr(h, addr, h->h.h_length) == -1) goto fail; return (h); fail: free(h); return (NULL); }
static struct hostent * hostent_from_yp(int family, char *line) { struct hostent *h; char *next, *tokens[10], addr[IN6ADDRSZ]; int i, ntok; if ((h = hostent_alloc(family)) == NULL) return (NULL); for(next = line; line; line = next) { if ((next = strchr(line, '\n'))) { *next = '\0'; next += 1; } ntok = strsplit(line, tokens, 10); if (ntok < 2) continue; if (inet_pton(family, tokens[0], addr) == 1) hostent_add_addr(h, addr, family == AF_INET ? INADDRSZ : IN6ADDRSZ); i = 2; if (!h->h_name) hostent_set_cname(h, tokens[1], 0); else if (strcmp(h->h_name, tokens[1])) i = 1; for (; i < ntok; i++) hostent_add_alias(h, tokens[i], 0); } if (h->h_name == NULL) { free(h); return (NULL); } return (h); }
/* * Fill the hostent from the given DNS packet. */ static struct hostent * hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen) { struct hostent *h; struct packed p; struct header hdr; struct query q; struct rr rr; if ((h = hostent_alloc(family)) == NULL) return (NULL); packed_init(&p, pkt, pktlen); unpack_header(&p, &hdr); for(; hdr.qdcount; hdr.qdcount--) unpack_query(&p, &q); for(; hdr.ancount; hdr.ancount--) { unpack_rr(&p, &rr); if (rr.rr_class != C_IN) continue; switch (rr.rr_type) { case T_CNAME: if (reqtype == ASR_GETHOSTBYNAME) { if (hostent_add_alias(h, rr.rr_dname, 1) == -1) goto fail; } else { if (hostent_set_cname(h, rr.rr_dname, 1) == -1) goto fail; } break; case T_PTR: if (reqtype != ASR_GETHOSTBYADDR) break; if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) goto fail; /* XXX See if we need MULTI_PTRS_ARE_ALIASES */ break; case T_A: if (reqtype != ASR_GETHOSTBYNAME) break; if (family != AF_INET) break; if (hostent_set_cname(h, rr.rr_dname, 1) == -1) goto fail; if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1) goto fail; break; case T_AAAA: if (reqtype != ASR_GETHOSTBYNAME) break; if (family != AF_INET6) break; if (hostent_set_cname(h, rr.rr_dname, 1) == -1) goto fail; if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1) goto fail; break; } } return (h); fail: free(h); return (NULL); }
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 gethostnamadr_async_run(struct async *as, struct async_res *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; } /* Name might be an IP address string */ if (as->as_type == ASR_GETHOSTBYNAME) { for (c = as->as.hostnamadr.name; *c; c++) if (!isdigit(*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(as->as_ctx->ac_hostfile, "r")) == NULL) break; if (as->as_type == ASR_GETHOSTBYNAME) { data = asr_hostalias(as->as_ctx, as->as.hostnamadr.name, name, sizeof(name)); if (data == NULL) 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; #ifdef YP case ASR_DB_YP: /* IPv4 only */ if (as->as.hostnamadr.family != AF_INET) break; if (as->as_type == ASR_GETHOSTBYNAME) { data = asr_hostalias(as->as_ctx, as->as.hostnamadr.name, name, sizeof(name)); if (data == NULL) data = as->as.hostnamadr.name; } else data = as->as.hostnamadr.addr; h = _yp_gethostnamadr(as->as_type, data); 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; #endif } break; case ASR_STATE_SUBQUERY: /* Run the DNS subquery. */ if ((r = asr_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); 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 address found in the dns packet. The blocking version * reports this as an error. */ if ((as->as_type == ASR_GETHOSTBYNAME && h->h.h_addr_list[0] == NULL) || (as->as_type == ASR_GETHOSTBYADDR && 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; }
/* * Fill the hostent from the given DNS packet. */ static struct hostent_ext * hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen) { struct hostent_ext *h; struct asr_unpack p; struct asr_dns_header hdr; struct asr_dns_query q; struct asr_dns_rr rr; char dname[MAXDNAME]; if ((h = hostent_alloc(family)) == NULL) return (NULL); _asr_unpack_init(&p, pkt, pktlen); _asr_unpack_header(&p, &hdr); for (; hdr.qdcount; hdr.qdcount--) _asr_unpack_query(&p, &q); strlcpy(dname, q.q_dname, sizeof(dname)); for (; hdr.ancount; hdr.ancount--) { _asr_unpack_rr(&p, &rr); if (rr.rr_class != C_IN) continue; switch (rr.rr_type) { case T_CNAME: if (reqtype == ASR_GETHOSTBYNAME) { if (hostent_add_alias(h, rr.rr_dname, 1) == -1) goto fail; } else { if (strcasecmp(rr.rr_dname, dname) == 0) strlcpy(dname, rr.rr.cname.cname, sizeof(dname)); } break; case T_PTR: if (reqtype != ASR_GETHOSTBYADDR) break; if (strcasecmp(rr.rr_dname, dname) != 0) continue; if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) hostent_add_alias(h, rr.rr.ptr.ptrname, 1); break; case T_A: if (reqtype != ASR_GETHOSTBYNAME) break; if (family != AF_INET) break; if (hostent_set_cname(h, rr.rr_dname, 1) == -1) ; if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1) goto fail; break; case T_AAAA: if (reqtype != ASR_GETHOSTBYNAME) break; if (family != AF_INET6) break; if (hostent_set_cname(h, rr.rr_dname, 1) == -1) ; if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1) goto fail; break; } } return (h); fail: free(h); return (NULL); }