/* * Copyright (c) 2013, 2015, 2016, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * netdb/getaddrinfo.c * Network address and service translation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DNS_SIZE 512 #define DNS_NAME_MAX 255 #define DNS_LABEL_MAX 64 struct dns_header { uint16_t id; uint16_t flags; uint16_t qdcount; uint16_t ancount; uint16_t nscount; uint16_t arcount; }; struct dns_question { uint16_t qtype; uint16_t qclass; }; struct dns_record { uint16_t type; uint16_t class; uint16_t ttl_high; uint16_t ttl_low; uint16_t rdlength; }; #define DNS_HEADER_FLAGS_RCODE_MASK (0xF << 0) #define DNS_HEADER_FLAGS_RCODE_NO (0 << 0) #define DNS_HEADER_FLAGS_RCODE_FORMAT (1 << 0) #define DNS_HEADER_FLAGS_RCODE_SERVER (2 << 0) #define DNS_HEADER_FLAGS_RCODE_NAME (3 << 0) #define DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED (4 << 0) #define DNS_HEADER_FLAGS_RCODE_REFUSED (5 << 0) #define DNS_HEADER_FLAGS_RA (1 << 7) #define DNS_HEADER_FLAGS_RD (1 << 8) #define DNS_HEADER_FLAGS_TC (1 << 9) #define DNS_HEADER_FLAGS_AA (1 << 10) #define DNS_HEADER_FLAGS_OPCODE_MASK (0xF << 11) #define DNS_HEADER_FLAGS_OPCODE_QUERY (0 << 11) #define DNS_HEADER_FLAGS_OPCODE_IQUERY (1 << 11) #define DNS_HEADER_FLAGS_OPCODE_STATUS (2 << 11) #define DNS_HEADER_FLAGS_QR (1 << 15) #define DNS_TYPE_A 1 #define DNS_TYPE_NS 2 #define DNS_TYPE_MD 3 #define DNS_TYPE_MF 4 #define DNS_TYPE_CNAME 5 #define DNS_TYPE_SOA 6 #define DNS_TYPE_MB 7 #define DNS_TYPE_MG 8 #define DNS_TYPE_MR 9 #define DNS_TYPE_NULL 10 #define DNS_TYPE_WKS 11 #define DNS_TYPE_PTR 12 #define DNS_TYPE_HINFO 13 #define DNS_TYPE_MINFO 14 #define DNS_TYPE_MX 15 #define DNS_TYPE_TXT 16 #define DNS_TYPE_AAAA 28 #define DNS_QTYPE_AXFR 252 #define DNS_QTYPE_MAILB 253 #define DNS_QTYPE_MAILA 254 #define DNS_QTYPE_ANY 255 #define DNS_CLASS_IN 1 #define DNS_CLASS_CS 2 #define DNS_CLASS_CH 3 #define DNS_CLASS_HS 4 #define DNS_QCLASS_ANY 255 static bool encode_dns_header(unsigned char* msg, size_t* offset_ptr, const struct dns_header* hdrin) { size_t offset = *offset_ptr; struct dns_header hdr; if ( DNS_SIZE - offset < sizeof(hdr) ) return false; hdr.id = htobe16(hdrin->id); hdr.flags = htobe16(hdrin->flags); hdr.qdcount = htobe16(hdrin->qdcount); hdr.ancount = htobe16(hdrin->ancount); hdr.nscount = htobe16(hdrin->nscount); hdr.arcount = htobe16(hdrin->arcount); memcpy(msg + offset, &hdr, sizeof(hdr)); *offset_ptr = offset + sizeof(hdr); return true; } static bool encode_dns_byte(unsigned char* msg, size_t* offset_ptr, unsigned char byte) { size_t offset = *offset_ptr; if ( DNS_SIZE - offset < 1 ) return false; msg[offset] = byte; *offset_ptr = offset + 1; return true; } // TODO: Enforce things like proper use of dashes and restrict to allowed ASCII // sequences (but see internationalized domain names). // TODO: Simplify this further. static bool is_valid_name(const char* name) { size_t index = 0; size_t namelen = 0; if ( !name[0] ) return false; /* unexpected end of input */ while ( name[index] ) { if ( !strcmp(name + index, ".") ) break; if ( name[index] == '.' ) return false; /* empty label */ size_t length = strcspn(name + index, "."); if ( DNS_LABEL_MAX <= length ) return false; /* label too long */ if ( namelen++ == DNS_NAME_MAX ) return false; /* name is too long */ for ( size_t i = 0; i < length; i++ ) { if ( namelen++ == DNS_NAME_MAX ) return false; /* name is too long */ } index += length; if ( name[index] == '.' ) index++; } if ( namelen++ == DNS_NAME_MAX ) return false; return true; } static bool encode_dns_name(unsigned char* msg, size_t* offset_ptr, const char* name) { size_t index = 0; size_t namelen = 0; if ( !name[0] ) return false; while ( name[index] ) { if ( !strcmp(name + index, ".") ) break; if ( name[index] == '.' ) return false; size_t length = strcspn(name + index, "."); if ( DNS_LABEL_MAX <= length ) return false; if ( namelen++ == DNS_NAME_MAX ) return false; if ( !encode_dns_byte(msg, offset_ptr, length & 0xFF) ) return false; for ( size_t i = 0; i < length; i++ ) { if ( namelen++ == DNS_NAME_MAX ) return false; if ( !encode_dns_byte(msg, offset_ptr, name[index + i]) ) return false; } index += length; if ( name[index] == '.' ) index++; } if ( namelen++ == DNS_NAME_MAX ) return false; if ( !encode_dns_byte(msg, offset_ptr, 0) ) return false; return true; } static bool encode_dns_question(unsigned char* msg, size_t* offset_ptr, const char* name, const struct dns_question* qsin) { if ( !encode_dns_name(msg, offset_ptr, name) ) return false; size_t offset = *offset_ptr; struct dns_question qs; if ( DNS_SIZE - offset < sizeof(qs) ) return false; qs.qtype = htobe16(qsin->qtype); qs.qclass = htobe16(qsin->qclass); memcpy(msg + offset, &qs, sizeof(qs)); *offset_ptr = offset + sizeof(qs); return true; } static bool decode_dns_header(const unsigned char* msg, size_t* offset_ptr, size_t msg_size, struct dns_header* hdrout) { size_t offset = *offset_ptr; struct dns_header hdr; if ( msg_size - offset < sizeof(hdr) ) return false; memcpy(&hdr, msg + offset, sizeof(hdr)); hdrout->id = be16toh(hdr.id); hdrout->flags = be16toh(hdr.flags); hdrout->qdcount = be16toh(hdr.qdcount); hdrout->ancount = be16toh(hdr.ancount); hdrout->nscount = be16toh(hdr.nscount); hdrout->arcount = be16toh(hdr.arcount); *offset_ptr = offset + sizeof(hdr); return true; } static bool decode_dns_byte(const unsigned char* msg, size_t* offset_ptr, size_t msg_size, unsigned char* byte) { size_t offset = *offset_ptr; if ( msg_size <= offset || msg_size - offset < 1 ) return false; *byte = msg[offset]; *offset_ptr = offset + 1; return true; } static bool decode_dns_name(const unsigned char* msg, size_t* offset_ptr, size_t msg_size, char* name) { bool real_offset_set = false; size_t real_offset = 0; size_t index = 0; size_t namelen = 0; uint8_t b; while ( true ) { if ( namelen++ == DNS_NAME_MAX ) return false; if ( !decode_dns_byte(msg, offset_ptr, msg_size, &b) ) return false; if ( 0xC0 & b ) { namelen--; size_t ptr = (b & 0x3F) << 8; if ( !decode_dns_byte(msg, offset_ptr, msg_size, &b) ) return false; ptr |= b; if ( !real_offset_set ) { real_offset = *offset_ptr; real_offset_set = true; } *offset_ptr = ptr; continue; } size_t length = b; if ( DNS_LABEL_MAX <= length ) return false; if ( !length ) break; if ( index ) name[index++] = '.'; for ( size_t i = 0; i < length; i++ ) { if ( namelen++ == DNS_NAME_MAX ) return false; if ( !decode_dns_byte(msg, offset_ptr, msg_size, &b) ) return false; // TODO: Handle if b == '.'. name[index++] = b; } } name[index++] = '.'; name[index] = '\0'; if ( real_offset_set ) *offset_ptr = real_offset; return true; } static bool decode_dns_question(const unsigned char* msg, size_t* offset_ptr, size_t msg_size, char* name, struct dns_question* qsout) { if ( !decode_dns_name(msg, offset_ptr, msg_size, name) ) return false; size_t offset = *offset_ptr; struct dns_question qs; if ( msg_size <= offset || msg_size - offset < sizeof(qs) ) return false; memcpy(&qs, msg + offset, sizeof(qs)); qsout->qtype = be16toh(qs.qtype); qsout->qclass = be16toh(qs.qclass); *offset_ptr = offset + sizeof(qs); return true; } static bool decode_dns_record(const unsigned char* msg, size_t* offset_ptr, size_t msg_size, char* name, struct dns_record* rrout) { if ( !decode_dns_name(msg, offset_ptr, msg_size, name) ) return false; size_t offset = *offset_ptr; struct dns_record rr; if ( msg_size <= offset || msg_size - offset < sizeof(rr) ) return false; memcpy(&rr, msg + offset, sizeof(rr)); rrout->type = be16toh(rr.type); rrout->class = be16toh(rr.class); rrout->ttl_high = be16toh(rr.ttl_high); rrout->ttl_low = be16toh(rr.ttl_low); rrout->rdlength = be16toh(rr.rdlength); *offset_ptr = offset + sizeof(rr); return true; } static bool linkaddrinfo(struct addrinfo** restrict* res_ptr, const struct addrinfo* restrict templ) { struct addrinfo* link = (struct addrinfo*) calloc(1, sizeof(struct addrinfo)); if ( !link ) return false; link->ai_flags = templ->ai_flags; link->ai_family = templ->ai_family; link->ai_socktype = templ->ai_socktype; link->ai_protocol = templ->ai_protocol; link->ai_addrlen = templ->ai_addrlen; link->ai_addr = (struct sockaddr*) malloc(templ->ai_addrlen); if ( !link->ai_addr ) return free(link), false; memcpy(link->ai_addr, templ->ai_addr, templ->ai_addrlen); link->ai_canonname = NULL; if ( templ->ai_canonname ) { link->ai_canonname = strdup(templ->ai_canonname); if ( !link->ai_canonname ) return free(link->ai_addr), free(link), false; } **res_ptr = link; *res_ptr = &link->ai_next; return true; } int getaddrinfo(const char* restrict node, const char* restrict servname, const struct addrinfo* restrict hints, struct addrinfo** restrict res) { int flags = 0; int family = AF_UNSPEC; int socktype = 0; int protocol = 0; if ( hints ) { flags = hints->ai_flags; family = hints->ai_family; socktype = hints->ai_socktype; protocol = hints->ai_protocol; } // TODO: Implement missing flags. int supported = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_CANONNAME; if ( flags & ~supported ) return EAI_BADFLAGS; // TODO: IPv6 support. if ( family != AF_UNSPEC && family != AF_INET ) return EAI_FAMILY; family = AF_INET; if ( socktype == 0 ) socktype = SOCK_STREAM; in_port_t port = 0; if ( servname ) { int errval = flags & AI_NUMERICSERV ? EAI_NONAME : EAI_SERVICE; if ( isspace((unsigned char) servname[0]) ) return errval; const char* end; long portl = strtol(servname, (char**) &end, 10); if ( end[0] ) return errval; if ( (in_port_t) portl != portl ) return errval; port = portl; } struct addrinfo** res_orig = res; *res = NULL; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); if ( !node || !strcasecmp(node, "localhost") ) { if ( !node && !servname ) return EAI_NONAME; bool any = false; if ( family == AF_UNSPEC || family == AF_INET ) { sin.sin_family = AF_INET; sin.sin_port = htobe16(port); if ( flags & AI_PASSIVE ) sin.sin_addr.s_addr = htobe32(INADDR_ANY); else sin.sin_addr.s_addr = htobe32(INADDR_LOOPBACK); struct addrinfo templ; memset(&templ, 0, sizeof(templ)); templ.ai_family = sin.sin_family; templ.ai_socktype = socktype; templ.ai_protocol = protocol; templ.ai_addrlen = sizeof(sin); templ.ai_addr = (struct sockaddr*) &sin; if ( flags & AI_CANONNAME ) templ.ai_canonname = "localhost"; if ( !linkaddrinfo(&res, &templ) ) return freeaddrinfo(*res_orig), EAI_MEMORY; any = true; } if ( family == AF_UNSPEC || family == AF_INET6 ) { sin6.sin6_family = AF_INET6; sin6.sin6_port = htobe16(port); sin6.sin6_flowinfo = 0; if ( flags & AI_PASSIVE ) sin6.sin6_addr = in6addr_any; else sin6.sin6_addr = in6addr_loopback; sin6.sin6_scope_id = 0; struct addrinfo templ; memset(&templ, 0, sizeof(templ)); templ.ai_family = sin6.sin6_family; templ.ai_socktype = socktype; templ.ai_protocol = protocol; templ.ai_addrlen = sizeof(sin6); templ.ai_addr = (struct sockaddr*) &sin6; if ( flags & AI_CANONNAME ) templ.ai_canonname = "localhost"; if ( !linkaddrinfo(&res, &templ) ) return freeaddrinfo(*res_orig), EAI_MEMORY; any = true; } if ( any ) return 0; return EAI_NONAME; } if ( (family == AF_UNSPEC || family == AF_INET) ) { // POSIX divergence: Use inet_pton instead of inet_addr format. if ( inet_pton(AF_INET, node, &sin.sin_addr) == 1 ) { sin.sin_family = AF_INET; sin.sin_port = htobe16(port); struct addrinfo templ; memset(&templ, 0, sizeof(templ)); templ.ai_family = sin.sin_family; templ.ai_socktype = socktype; templ.ai_protocol = protocol; templ.ai_addrlen = sizeof(sin); templ.ai_addr = (struct sockaddr*) &sin; if ( !linkaddrinfo(&res, &templ) ) return freeaddrinfo(*res_orig), EAI_MEMORY; return 0; } } if ( (family == AF_UNSPEC || family == AF_INET6) ) { if ( inet_pton(AF_INET6, node, &sin6.sin6_addr) == 1 ) { sin6.sin6_family = AF_INET6; sin6.sin6_port = htobe16(port); sin6.sin6_flowinfo = 0; sin6.sin6_scope_id = 0; struct addrinfo templ; memset(&templ, 0, sizeof(templ)); templ.ai_family = sin6.sin6_family; templ.ai_socktype = socktype; templ.ai_protocol = protocol; templ.ai_addrlen = sizeof(sin6); templ.ai_addr = (struct sockaddr*) &sin6; if ( !linkaddrinfo(&res, &templ) ) return freeaddrinfo(*res_orig), EAI_MEMORY; return 0; } } if ( flags & AI_NUMERICHOST ) return EAI_NONAME; if ( !is_valid_name(node) ) return EAI_NONAME; size_t encoded_size = 0; unsigned char encoded[DNS_SIZE]; if ( !encode_dns_name(encoded, &encoded_size, node) ) return EAI_NONAME; size_t decoded_size = 0; char target[DNS_NAME_MAX + 1]; if ( !decode_dns_name(encoded, &decoded_size, encoded_size, target) ) return EAI_NONAME; int cname_retries = 0; retry_cname: if ( 5 < cname_retries++ ) return EAI_NONAME; struct dnsconfig dnsconfig; if ( getdnsconfig(&dnsconfig) < 0 ) return EAI_SYSTEM; // TODO: Potentially do a blocking wait for DNS configuration to come up (or // for the automatic configuration to time out) if it isn't ready yet. if ( dnsconfig.servers_count == 0 ) return EAI_NONAME; // TODO: Send requests to all the servers rather than picking the first one // a socket can be made for. int fd = -1; size_t server_index = 0; struct dnsconfig_server* server = NULL; for ( ; server_index < dnsconfig.servers_count; server_index++ ) { server = &dnsconfig.servers[server_index]; if ( 0 <= (fd = socket(server->family, SOCK_DGRAM, 0)) ) break; } if ( server_index == dnsconfig.servers_count ) return EAI_SYSTEM; struct sockaddr* addr; size_t addr_size; if ( server->family == AF_INET ) { memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htobe16(53); memcpy(&sin.sin_addr, &server->addr, sizeof(sin.sin_addr)); addr = (struct sockaddr*) &sin; addr_size = sizeof(sin); } else if ( server->family == AF_INET6 ) { memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htobe16(53); memcpy(&sin6.sin6_addr, &server->addr, sizeof(sin6.sin6_addr)); addr = (struct sockaddr*) &sin6; addr_size = sizeof(sin6); } else return EAI_FAMILY; if ( connect(fd, addr, addr_size) < 0 ) return close(fd), EAI_SYSTEM; uint16_t id = arc4random() & 0xFFFF; unsigned char req[DNS_SIZE]; size_t req_size = 0; struct dns_header hdr; hdr.id = id; hdr.flags = DNS_HEADER_FLAGS_RD; hdr.qdcount = 1; hdr.ancount = 0; hdr.nscount = 0; hdr.arcount = 0; if ( !encode_dns_header(req, &req_size, &hdr) ) return close(fd), EAI_OVERFLOW; struct dns_question qs; qs.qtype = 0; if ( family == AF_INET ) qs.qtype = DNS_TYPE_A; else if ( family == AF_INET6 ) qs.qtype = DNS_TYPE_AAAA; qs.qclass = DNS_CLASS_IN; if ( !encode_dns_question(req, &req_size, node, &qs) ) return close(fd), EAI_OVERFLOW; struct timespec last_sent = timespec_nul(); struct timespec timeout = timespec_nul(); unsigned char resp[DNS_SIZE]; ssize_t resp_size; size_t offset; unsigned int retransmissions = 0; while ( true ) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct timespec since_sent = timespec_sub(now, last_sent); if ( timespec_le(timeout, since_sent) ) { if ( 2 <= retransmissions ) return close(fd), EAI_AGAIN; ssize_t amount = send(fd, req, req_size, 0); if ( amount < 0 ) return close(fd), EAI_SYSTEM; timeout = timespec_make(2, 500000000); last_sent = now; retransmissions++; } struct timespec left = timespec_sub(timespec_add(last_sent, timeout), now); struct pollfd pfd = { 0 }; pfd.fd = fd; pfd.events = POLLIN; int num_events = ppoll(&pfd, 1, &left, NULL); if ( num_events < 0 ) return close(fd), EAI_SYSTEM; if ( num_events == 0 ) continue; resp_size = recv(fd, resp, sizeof(resp), 0); if ( resp_size < 0 ) return close(fd), EAI_SYSTEM; offset = 0; if ( !decode_dns_header(resp, &offset, resp_size, &hdr) ) continue; if ( hdr.id != id ) continue; break; } // TODO: Return the correct errors below. It may be the best behavior to // simply drop any responses with errors. uint16_t rcode = hdr.flags & DNS_HEADER_FLAGS_RCODE_MASK; if ( rcode == DNS_HEADER_FLAGS_RCODE_FORMAT ) return close(fd), EAI_FAIL; else if ( rcode == DNS_HEADER_FLAGS_RCODE_SERVER ) return close(fd), EAI_FAIL; else if ( rcode == DNS_HEADER_FLAGS_RCODE_NAME ) return close(fd), EAI_NONAME; else if ( rcode == DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED ) return close(fd), EAI_FAIL; else if ( rcode == DNS_HEADER_FLAGS_RCODE_REFUSED ) return close(fd), EAI_FAIL; else if ( rcode != DNS_HEADER_FLAGS_RCODE_NO ) return close(fd), EAI_FAIL; // TODO: Reconnect to server using TCP to get full response. if ( hdr.flags & DNS_HEADER_FLAGS_TC ) return close(fd), EAI_FAIL; // TODO: Check query bit. for ( uint16_t i = 0; i < hdr.qdcount; i++ ) { char name[DNS_NAME_MAX + 1]; if ( !decode_dns_question(resp, &offset, resp_size, name, &qs) ) return close(fd), EAI_OVERFLOW; } bool any = false; bool found_cname = false; for ( uint16_t i = 0; i < hdr.ancount; i++ ) { char name[DNS_NAME_MAX + 1]; struct dns_record rr; if ( !decode_dns_record(resp, &offset, resp_size, name, &rr) ) return close(fd), freeaddrinfo(*res_orig), EAI_OVERFLOW; bool match = strcmp(name, target) == 0; // TODO: Support aliases. if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_A ) { unsigned char ip[4]; for ( size_t i = 0; i < 4; i++ ) if ( !decode_dns_byte(resp, &offset, resp_size, &ip[i]) ) return close(fd), freeaddrinfo(*res_orig), EAI_OVERFLOW; if ( match && (family == AF_UNSPEC || family == AF_INET) ) { memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htobe16(port); memcpy(&sin.sin_addr, ip, sizeof(sin.sin_addr)); struct addrinfo templ; memset(&templ, 0, sizeof(templ)); templ.ai_family = sin.sin_family; templ.ai_socktype = socktype; templ.ai_protocol = protocol; templ.ai_addrlen = sizeof(sin); templ.ai_addr = (struct sockaddr*) &sin; if ( !linkaddrinfo(&res, &templ) ) return close(fd), freeaddrinfo(*res_orig), EAI_MEMORY; any = true; } } else if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_AAAA ) { unsigned char ip[16]; for ( size_t i = 0; i < 16; i++ ) if ( !decode_dns_byte(resp, &offset, resp_size, &ip[i]) ) return close(fd), freeaddrinfo(*res_orig), EAI_OVERFLOW; if ( match && (family == AF_UNSPEC || family == AF_INET6) ) { sin6.sin6_family = AF_INET6; sin6.sin6_port = htobe16(port); sin6.sin6_flowinfo = 0; memcpy(&sin6.sin6_addr, ip, sizeof(sin6.sin6_addr)); sin6.sin6_scope_id = 0; struct addrinfo templ; memset(&templ, 0, sizeof(templ)); templ.ai_family = sin6.sin6_family; templ.ai_socktype = socktype; templ.ai_protocol = protocol; templ.ai_addrlen = sizeof(sin6); templ.ai_addr = (struct sockaddr*) &sin6; if ( !linkaddrinfo(&res, &templ) ) return close(fd), freeaddrinfo(*res_orig), EAI_MEMORY; any = true; } } else if ( rr.type == DNS_TYPE_CNAME ) { char cname[DNS_NAME_MAX + 1]; if ( !decode_dns_name(resp, &offset, resp_size, cname) ) return close(fd), freeaddrinfo(*res_orig), EAI_OVERFLOW; if ( match ) { // TODO: Report CNAME to caller. memcpy(target, cname, sizeof(target)); found_cname = true; } } else { for ( size_t i = 0; i < rr.rdlength; i++ ) { unsigned char b; if ( !decode_dns_byte(resp, &offset, resp_size, &b) ) return close(fd), freeaddrinfo(*res_orig), EAI_OVERFLOW; } } } close(fd); if ( !any ) { if ( found_cname ) { freeaddrinfo(*res_orig); res = res_orig; *res = NULL; goto retry_cname; } return EAI_NONAME; } return 0; }