/* Consume the given number of bytes from the head of the TCP send queue. */ static void advance_tcp_send_queue(ares_channel channel, int whichserver, ares_ssize_t num_bytes) { struct send_request *sendreq; struct server_state *server = &channel->servers[whichserver]; while (num_bytes > 0) { sendreq = server->qhead; if ((size_t)num_bytes >= sendreq->len) { num_bytes -= sendreq->len; server->qhead = sendreq->next; if (sendreq->data_storage) ares_free(sendreq->data_storage); ares_free(sendreq); if (server->qhead == NULL) { SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0); server->qtail = NULL; /* qhead is NULL so we cannot continue this loop */ break; } } else { sendreq->data += num_bytes; sendreq->len -= num_bytes; num_bytes = 0; } } }
static void end_squery(struct search_query *squery, int status, unsigned char *abuf, int alen) { squery->callback(squery->arg, status, squery->timeouts, abuf, alen); ares_free(squery->name); ares_free(squery); }
static void end_hquery(struct host_query *hquery, int status, struct hostent *host) { hquery->callback(hquery->arg, status, hquery->timeouts, host); if (host) ares_free_hostent(host); ares_free(hquery->name); ares_free(hquery); }
void ares_strsplit_free(char **elms, size_t num_elm) { size_t i; if (elms == NULL) return; for (i=0; i<num_elm; i++) ares_free(elms[i]); ares_free(elms); }
void ares_query(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg) { struct qquery *qquery; unsigned char *qbuf; int qlen, rd, status; /* Compose the query. */ rd = !(channel->flags & ARES_FLAG_NORECURSE); status = ares_create_query(name, dnsclass, type, channel->next_id, rd, &qbuf, &qlen, (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0); if (status != ARES_SUCCESS) { if (qbuf != NULL) ares_free(qbuf); callback(arg, status, 0, NULL, 0); return; } channel->next_id = generate_unique_id(channel); /* Allocate and fill in the query structure. */ qquery = ares_malloc(sizeof(struct qquery)); if (!qquery) { ares_free_string(qbuf); callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } qquery->callback = callback; qquery->arg = arg; /* Send it off. qcallback will be called when we get an answer. */ ares_send(channel, qbuf, qlen, qcallback, qquery); ares_free_string(qbuf); }
ares_ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt) { char *buffer, *bp; int i; size_t bytes = 0; ares_ssize_t result; /* Validate iovcnt */ if (iovcnt <= 0) { SET_ERRNO(EINVAL); return (-1); } /* Validate and find the sum of the iov_len values in the iov array */ for (i = 0; i < iovcnt; i++) { if (iov[i].iov_len > INT_MAX - bytes) { SET_ERRNO(EINVAL); return (-1); } bytes += iov[i].iov_len; } if (bytes == 0) return (0); /* Allocate a temporary buffer to hold the data */ buffer = ares_malloc(bytes); if (!buffer) { SET_ERRNO(ENOMEM); return (-1); } /* Copy the data into buffer */ for (bp = buffer, i = 0; i < iovcnt; ++i) { memcpy (bp, iov[i].iov_base, iov[i].iov_len); bp += iov[i].iov_len; } /* Send buffer contents */ result = swrite(s, buffer, bytes); ares_free(buffer); return (result); }
void ares_gethostbyname(ares_channel channel, const char *name, int family, ares_host_callback callback, void *arg) { struct host_query *hquery; /* Right now we only know how to look up Internet addresses - and unspec means try both basically. */ switch (family) { case AF_INET: case AF_INET6: case AF_UNSPEC: break; default: callback(arg, ARES_ENOTIMP, 0, NULL); return; } if (fake_hostent(name, family, callback, arg)) return; /* Allocate and fill in the host query structure. */ hquery = ares_malloc(sizeof(struct host_query)); if (!hquery) { callback(arg, ARES_ENOMEM, 0, NULL); return; } hquery->channel = channel; hquery->name = ares_strdup(name); hquery->want_family = family; hquery->sent_family = -1; /* nothing is sent yet */ if (!hquery->name) { ares_free(hquery); callback(arg, ARES_ENOMEM, 0, NULL); return; } hquery->callback = callback; hquery->arg = arg; hquery->remaining_lookups = channel->lookups; hquery->timeouts = 0; /* Start performing lookups according to channel->lookups. */ next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */); }
static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { struct qquery *qquery = (struct qquery *) arg; unsigned int ancount; int rcode; if (status != ARES_SUCCESS) qquery->callback(qquery->arg, status, timeouts, abuf, alen); else { /* Pull the response code and answer count from the packet. */ rcode = DNS_HEADER_RCODE(abuf); ancount = DNS_HEADER_ANCOUNT(abuf); /* Convert errors. */ switch (rcode) { case NOERROR: status = (ancount > 0) ? ARES_SUCCESS : ARES_ENODATA; break; case FORMERR: status = ARES_EFORMERR; break; case SERVFAIL: status = ARES_ESERVFAIL; break; case NXDOMAIN: status = ARES_ENOTFOUND; break; case NOTIMP: status = ARES_ENOTIMP; break; case REFUSED: status = ARES_EREFUSED; break; } qquery->callback(qquery->arg, status, timeouts, abuf, alen); } ares_free(qquery); }
/* Determine if this name only yields one query. If it does, set *s to * the string we should query, in an allocated buffer. If not, set *s * to NULL. */ STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s) { size_t len = strlen(name); const char *hostaliases; FILE *fp; char *line = NULL; int status; size_t linesize; const char *p, *q; int error; /* If the name contains a trailing dot, then the single query is the name * sans the trailing dot. */ if ((len > 0) && (name[len - 1] == '.')) { *s = ares_strdup(name); return (*s) ? ARES_SUCCESS : ARES_ENOMEM; } if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.')) { /* The name might be a host alias. */ hostaliases = getenv("HOSTALIASES"); if (hostaliases) { fp = fopen(hostaliases, "r"); if (fp) { while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) { if (strncasecmp(line, name, len) != 0 || !ISSPACE(line[len])) continue; p = line + len; while (ISSPACE(*p)) p++; if (*p) { q = p + 1; while (*q && !ISSPACE(*q)) q++; *s = ares_malloc(q - p + 1); if (*s) { memcpy(*s, p, q - p); (*s)[q - p] = 0; } ares_free(line); fclose(fp); return (*s) ? ARES_SUCCESS : ARES_ENOMEM; } } ares_free(line); fclose(fp); if (status != ARES_SUCCESS && status != ARES_EOF) return status; } else { error = ERRNO; switch(error) { case ENOENT: case ESRCH: break; default: DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, strerror(error))); DEBUGF(fprintf(stderr, "Error opening file: %s\n", hostaliases)); *s = NULL; return ARES_EFILE; } } } } if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0) { /* No domain search to do; just try the name as-is. */ *s = ares_strdup(name); return (*s) ? ARES_SUCCESS : ARES_ENOMEM; } *s = NULL; return ARES_SUCCESS; }
static void search_callback(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { struct search_query *squery = (struct search_query *) arg; ares_channel channel = squery->channel; char *s; squery->timeouts += timeouts; /* Stop searching unless we got a non-fatal error. */ if (status != ARES_ENODATA && status != ARES_ESERVFAIL && status != ARES_ENOTFOUND) end_squery(squery, status, abuf, alen); else { /* Save the status if we were trying as-is. */ if (squery->trying_as_is) squery->status_as_is = status; /* * If we ever get ARES_ENODATA along the way, record that; if the search * should run to the very end and we got at least one ARES_ENODATA, * then callers like ares_gethostbyname() may want to try a T_A search * even if the last domain we queried for T_AAAA resource records * returned ARES_ENOTFOUND. */ if (status == ARES_ENODATA) squery->ever_got_nodata = 1; if (squery->next_domain < channel->ndomains) { /* Try the next domain. */ status = cat_domain(squery->name, channel->domains[squery->next_domain], &s); if (status != ARES_SUCCESS) end_squery(squery, status, NULL, 0); else { squery->trying_as_is = 0; squery->next_domain++; ares_query(channel, s, squery->dnsclass, squery->type, search_callback, squery); ares_free(s); } } else if (squery->status_as_is == -1) { /* Try the name as-is at the end. */ squery->trying_as_is = 1; ares_query(channel, squery->name, squery->dnsclass, squery->type, search_callback, squery); } else { if (squery->status_as_is == ARES_ENOTFOUND && squery->ever_got_nodata) { end_squery(squery, ARES_ENODATA, NULL, 0); } else end_squery(squery, squery->status_as_is, NULL, 0); } } }
int ares_parse_mx_reply (const unsigned char *abuf, int alen, struct ares_mx_reply **mx_out) { unsigned int qdcount, ancount, i; const unsigned char *aptr, *vptr; int status, rr_type, rr_class, rr_len; long len; char *hostname = NULL, *rr_name = NULL; struct ares_mx_reply *mx_head = NULL; struct ares_mx_reply *mx_last = NULL; struct ares_mx_reply *mx_curr; /* Set *mx_out to NULL for all failure cases. */ *mx_out = NULL; /* Give up if abuf doesn't have room for a header. */ if (alen < HFIXEDSZ) return ARES_EBADRESP; /* Fetch the question and answer count from the header. */ qdcount = DNS_HEADER_QDCOUNT (abuf); ancount = DNS_HEADER_ANCOUNT (abuf); if (qdcount != 1) return ARES_EBADRESP; if (ancount == 0) return ARES_ENODATA; /* Expand the name from the question, and skip past the question. */ aptr = abuf + HFIXEDSZ; status = ares_expand_name (aptr, abuf, alen, &hostname, &len); if (status != ARES_SUCCESS) return status; if (aptr + len + QFIXEDSZ > abuf + alen) { ares_free (hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Examine each answer resource record (RR) in turn. */ for (i = 0; i < ancount; i++) { /* Decode the RR up to the data field. */ status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); if (status != ARES_SUCCESS) { break; } aptr += len; if (aptr + RRFIXEDSZ > abuf + alen) { status = ARES_EBADRESP; break; } rr_type = DNS_RR_TYPE (aptr); rr_class = DNS_RR_CLASS (aptr); rr_len = DNS_RR_LEN (aptr); aptr += RRFIXEDSZ; if (aptr + rr_len > abuf + alen) { status = ARES_EBADRESP; break; } /* Check if we are really looking at a MX record */ if (rr_class == C_IN && rr_type == T_MX) { /* parse the MX record itself */ if (rr_len < 2) { status = ARES_EBADRESP; break; } /* Allocate storage for this MX answer appending it to the list */ mx_curr = ares_malloc_data(ARES_DATATYPE_MX_REPLY); if (!mx_curr) { status = ARES_ENOMEM; break; } if (mx_last) { mx_last->next = mx_curr; } else { mx_head = mx_curr; } mx_last = mx_curr; vptr = aptr; mx_curr->priority = DNS__16BIT(vptr); vptr += sizeof(unsigned short); status = ares_expand_name (vptr, abuf, alen, &mx_curr->host, &len); if (status != ARES_SUCCESS) break; } /* Don't lose memory in the next iteration */ ares_free (rr_name); rr_name = NULL; /* Move on to the next record */ aptr += rr_len; } if (hostname) ares_free (hostname); if (rr_name) ares_free (rr_name); /* clean up on error */ if (status != ARES_SUCCESS) { if (mx_head) ares_free_data (mx_head); return status; } /* everything looks fine, return the data */ *mx_out = mx_head; return ARES_SUCCESS; }
/* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */ static int set_servers_csv(ares_channel channel, const char* _csv, int use_port) { size_t i; char* csv = NULL; char* ptr; char* start_host; int cc = 0; int rv = ARES_SUCCESS; struct ares_addr_port_node *servers = NULL; struct ares_addr_port_node *last = NULL; if (ares_library_initialized() != ARES_SUCCESS) return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */ if (!channel) return ARES_ENODATA; ares__destroy_servers_state(channel); i = strlen(_csv); if (i == 0) return ARES_SUCCESS; /* blank all servers */ csv = ares_malloc(i + 2); if (!csv) return ARES_ENOMEM; strcpy(csv, _csv); if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */ csv[i] = ','; csv[i+1] = 0; } start_host = csv; for (ptr = csv; *ptr; ptr++) { if (*ptr == ':') { /* count colons to determine if we have an IPv6 number or IPv4 with port */ cc++; } else if (*ptr == '[') { /* move start_host if an open square bracket is found wrapping an IPv6 address */ start_host = ptr + 1; } else if (*ptr == ',') { char* pp = ptr - 1; char* p = ptr; int port = 0; struct in_addr in4; struct ares_in6_addr in6; struct ares_addr_port_node *s = NULL; *ptr = 0; /* null terminate host:port string */ /* Got an entry..see if the port was specified. */ if (cc > 0) { while (pp > start_host) { /* a single close square bracket followed by a colon, ']:' indicates an IPv6 address with port */ if ((*pp == ']') && (*p == ':')) break; /* found port */ /* a single colon, ':' indicates an IPv4 address with port */ if ((*pp == ':') && (cc == 1)) break; /* found port */ if (!(ISDIGIT(*pp) || (*pp == ':'))) { /* Found end of digits before we found :, so wasn't a port */ /* must allow ':' for IPv6 case of ']:' indicates we found a port */ pp = p = ptr; break; } pp--; p--; } if ((pp != start_host) && ((pp + 1) < ptr)) { /* Found it. Parse over the port number */ /* when an IPv6 address is wrapped with square brackets the port starts at pp + 2 */ if (*pp == ']') p++; /* move p before ':' */ /* p will point to the start of the port */ port = (int)strtol(p, NULL, 10); *pp = 0; /* null terminate host */ } } /* resolve host, try ipv4 first, rslt is in network byte order */ rv = ares_inet_pton(AF_INET, start_host, &in4); if (!rv) { /* Ok, try IPv6 then */ rv = ares_inet_pton(AF_INET6, start_host, &in6); if (!rv) { rv = ARES_EBADSTR; goto out; } /* was ipv6, add new server */ s = ares_malloc(sizeof(*s)); if (!s) { rv = ARES_ENOMEM; goto out; } s->family = AF_INET6; memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr)); } else { /* was ipv4, add new server */ s = ares_malloc(sizeof(*s)); if (!s) { rv = ARES_ENOMEM; goto out; } s->family = AF_INET; memcpy(&s->addr, &in4, sizeof(struct in_addr)); } if (s) { s->udp_port = use_port ? port: 0; s->tcp_port = s->udp_port; s->next = NULL; if (last) { last->next = s; /* need to move last to maintain the linked list */ last = last->next; } else { servers = s; last = s; } } /* Set up for next one */ start_host = ptr + 1; cc = 0; } } rv = ares_set_servers_ports(channel, servers); out: if (csv) ares_free(csv); while (servers) { struct ares_addr_port_node *s = servers; servers = servers->next; ares_free(s); } return rv; }
/* If the name looks like an IP address, fake up a host entry, end the * query immediately, and return true. Otherwise return false. */ static int fake_hostent(const char *name, int family, ares_host_callback callback, void *arg) { struct hostent hostent; char *aliases[1] = { NULL }; char *addrs[2]; int result = 0; struct in_addr in; struct ares_in6_addr in6; if (family == AF_INET || family == AF_INET6) { /* It only looks like an IP address if it's all numbers and dots. */ int numdots = 0, valid = 1; const char *p; for (p = name; *p; p++) { if (!ISDIGIT(*p) && *p != '.') { valid = 0; break; } else if (*p == '.') { numdots++; } } /* if we don't have 3 dots, it is illegal * (although inet_addr doesn't think so). */ if (numdots != 3 || !valid) result = 0; else result = ((in.s_addr = inet_addr(name)) == INADDR_NONE ? 0 : 1); if (result) family = AF_INET; } if (family == AF_INET6) result = (ares_inet_pton(AF_INET6, name, &in6) < 1 ? 0 : 1); if (!result) return 0; if (family == AF_INET) { hostent.h_length = (int)sizeof(struct in_addr); addrs[0] = (char *)∈ } else if (family == AF_INET6) { hostent.h_length = (int)sizeof(struct ares_in6_addr); addrs[0] = (char *)&in6; } /* Duplicate the name, to avoid a constness violation. */ hostent.h_name = ares_strdup(name); if (!hostent.h_name) { callback(arg, ARES_ENOMEM, 0, NULL); return 1; } /* Fill in the rest of the host structure and terminate the query. */ addrs[1] = NULL; hostent.h_aliases = aliases; hostent.h_addrtype = aresx_sitoss(family); hostent.h_addr_list = addrs; callback(arg, ARES_SUCCESS, 0, &hostent); ares_free((char *)(hostent.h_name)); return 1; }
void ares_search(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg) { struct search_query *squery; char *s; const char *p; int status, ndots; /* If name only yields one domain to search, then we don't have * to keep extra state, so just do an ares_query(). */ status = single_domain(channel, name, &s); if (status != ARES_SUCCESS) { callback(arg, status, 0, NULL, 0); return; } if (s) { ares_query(channel, s, dnsclass, type, callback, arg); ares_free(s); return; } /* Allocate a search_query structure to hold the state necessary for * doing multiple lookups. */ squery = ares_malloc(sizeof(struct search_query)); if (!squery) { callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } squery->channel = channel; squery->name = ares_strdup(name); if (!squery->name) { ares_free(squery); callback(arg, ARES_ENOMEM, 0, NULL, 0); return; } squery->dnsclass = dnsclass; squery->type = type; squery->status_as_is = -1; squery->callback = callback; squery->arg = arg; squery->timeouts = 0; squery->ever_got_nodata = 0; /* Count the number of dots in name. */ ndots = 0; for (p = name; *p; p++) { if (*p == '.') ndots++; } /* If ndots is at least the channel ndots threshold (usually 1), * then we try the name as-is first. Otherwise, we try the name * as-is last. */ if (ndots >= channel->ndots) { /* Try the name as-is first. */ squery->next_domain = 0; squery->trying_as_is = 1; ares_query(channel, name, dnsclass, type, search_callback, squery); } else { /* Try the name as-is last; start with the first search domain. */ squery->next_domain = 1; squery->trying_as_is = 0; status = cat_domain(name, channel->domains[0], &s); if (status == ARES_SUCCESS) { ares_query(channel, s, dnsclass, type, search_callback, squery); ares_free(s); } else { /* failed, free the malloc()ed memory */ ares_free(squery->name); ares_free(squery); callback(arg, status, 0, NULL, 0); } } }
/* If any TCP sockets select true for writing, write out queued data * we have for them. */ static void write_tcp_data(ares_channel channel, fd_set *write_fds, ares_socket_t write_fd, struct timeval *now) { struct server_state *server; struct send_request *sendreq; struct iovec *vec; int i; ares_ssize_t scount; ares_ssize_t wcount; size_t n; if(!write_fds && (write_fd == ARES_SOCKET_BAD)) /* no possible action */ return; for (i = 0; i < channel->nservers; i++) { /* Make sure server has data to send and is selected in write_fds or write_fd. */ server = &channel->servers[i]; if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD || server->is_broken) continue; if(write_fds) { if(!(FD_ISSET(server->tcp_socket, write_fds))) continue; } else { if(server->tcp_socket != write_fd) continue; } if(write_fds) /* If there's an error and we close this socket, then open * another with the same fd to talk to another server, then we * don't want to think that it was the new socket that was * ready. This is not disastrous, but is likely to result in * extra system calls and confusion. */ FD_CLR(server->tcp_socket, write_fds); /* Count the number of send queue items. */ n = 0; for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) n++; /* Allocate iovecs so we can send all our data at once. */ vec = ares_malloc(n * sizeof(struct iovec)); if (vec) { /* Fill in the iovecs and send. */ n = 0; for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) { vec[n].iov_base = (char *) sendreq->data; vec[n].iov_len = sendreq->len; n++; } wcount = socket_writev(channel, server->tcp_socket, vec, (int)n); ares_free(vec); if (wcount < 0) { if (!try_again(SOCKERRNO)) handle_error(channel, i, now); continue; } /* Advance the send queue by as many bytes as we sent. */ advance_tcp_send_queue(channel, i, wcount); } else { /* Can't allocate iovecs; just send the first request. */ sendreq = server->qhead; scount = socket_write(channel, server->tcp_socket, sendreq->data, sendreq->len); if (scount < 0) { if (!try_again(SOCKERRNO)) handle_error(channel, i, now); continue; } /* Advance the send queue by as many bytes as we sent. */ advance_tcp_send_queue(channel, i, scount); } } }
int ares_parse_soa_reply(const unsigned char *abuf, int alen, struct ares_soa_reply **soa_out) { const unsigned char *aptr; long len; char *qname = NULL, *rr_name = NULL; struct ares_soa_reply *soa = NULL; int qdcount, ancount; int status; if (alen < HFIXEDSZ) return ARES_EBADRESP; /* parse message header */ qdcount = DNS_HEADER_QDCOUNT(abuf); ancount = DNS_HEADER_ANCOUNT(abuf); if (qdcount != 1 || ancount != 1) return ARES_EBADRESP; aptr = abuf + HFIXEDSZ; /* query name */ status = ares__expand_name_for_response(aptr, abuf, alen, &qname, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* skip qtype & qclass */ if (aptr + QFIXEDSZ > abuf + alen) goto failed; aptr += QFIXEDSZ; /* rr_name */ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* allocate result struct */ soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY); if (!soa) { status = ARES_ENOMEM; goto failed_stat; } /* skip rr_type, rr_class, rr_ttl, rr_rdlen */ if (aptr + RRFIXEDSZ > abuf + alen) goto failed; soa->ttl = DNS_RR_TTL(aptr); aptr += RRFIXEDSZ; /* nsname */ status = ares__expand_name_for_response(aptr, abuf, alen, &soa->nsname, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* hostmaster */ status = ares__expand_name_for_response(aptr, abuf, alen, &soa->hostmaster, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* integer fields */ if (aptr + 5 * 4 > abuf + alen) goto failed; soa->serial = DNS__32BIT(aptr + 0 * 4); soa->refresh = DNS__32BIT(aptr + 1 * 4); soa->retry = DNS__32BIT(aptr + 2 * 4); soa->expire = DNS__32BIT(aptr + 3 * 4); soa->minttl = DNS__32BIT(aptr + 4 * 4); ares_free(qname); ares_free(rr_name); *soa_out = soa; return ARES_SUCCESS; failed: status = ARES_EBADRESP; failed_stat: ares_free_data(soa); if (qname) ares_free(qname); if (rr_name) ares_free(rr_name); return status; }
/* If any TCP socket selects true for reading, read some data, * allocate a buffer if we finish reading the length word, and process * a packet if we finish reading one. */ static void read_tcp_data(ares_channel channel, fd_set *read_fds, ares_socket_t read_fd, struct timeval *now) { struct server_state *server; int i; ares_ssize_t count; if(!read_fds && (read_fd == ARES_SOCKET_BAD)) /* no possible action */ return; for (i = 0; i < channel->nservers; i++) { /* Make sure the server has a socket and is selected in read_fds. */ server = &channel->servers[i]; if (server->tcp_socket == ARES_SOCKET_BAD || server->is_broken) continue; if(read_fds) { if(!(FD_ISSET(server->tcp_socket, read_fds))) continue; } else { if(server->tcp_socket != read_fd) continue; } if(read_fds) /* If there's an error and we close this socket, then open another * with the same fd to talk to another server, then we don't want to * think that it was the new socket that was ready. This is not * disastrous, but is likely to result in extra system calls and * confusion. */ FD_CLR(server->tcp_socket, read_fds); if (server->tcp_lenbuf_pos != 2) { /* We haven't yet read a length word, so read that (or * what's left to read of it). */ count = socket_recv(channel, server->tcp_socket, server->tcp_lenbuf + server->tcp_lenbuf_pos, 2 - server->tcp_lenbuf_pos); if (count <= 0) { if (!(count == -1 && try_again(SOCKERRNO))) handle_error(channel, i, now); continue; } server->tcp_lenbuf_pos += (int)count; if (server->tcp_lenbuf_pos == 2) { /* We finished reading the length word. Decode the * length and allocate a buffer for the data. */ server->tcp_length = server->tcp_lenbuf[0] << 8 | server->tcp_lenbuf[1]; server->tcp_buffer = ares_malloc(server->tcp_length); if (!server->tcp_buffer) { handle_error(channel, i, now); return; /* bail out on malloc failure. TODO: make this function return error codes */ } server->tcp_buffer_pos = 0; } } else { /* Read data into the allocated buffer. */ count = socket_recv(channel, server->tcp_socket, server->tcp_buffer + server->tcp_buffer_pos, server->tcp_length - server->tcp_buffer_pos); if (count <= 0) { if (!(count == -1 && try_again(SOCKERRNO))) handle_error(channel, i, now); continue; } server->tcp_buffer_pos += (int)count; if (server->tcp_buffer_pos == server->tcp_length) { /* We finished reading this answer; process it and * prepare to read another length word. */ process_answer(channel, server->tcp_buffer, server->tcp_length, i, 1, now); ares_free(server->tcp_buffer); server->tcp_buffer = NULL; server->tcp_lenbuf_pos = 0; server->tcp_buffer_pos = 0; } } } }
int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, int addrlen, int family, struct hostent **host) { unsigned int qdcount, ancount; int status, i, rr_type, rr_class, rr_len; long len; const unsigned char *aptr; char *ptrname, *hostname, *rr_name, *rr_data; struct hostent *hostent; int aliascnt = 0; int alias_alloc = 8; char ** aliases; /* Set *host to NULL for all failure cases. */ *host = NULL; /* Give up if abuf doesn't have room for a header. */ if (alen < HFIXEDSZ) return ARES_EBADRESP; /* Fetch the question and answer count from the header. */ qdcount = DNS_HEADER_QDCOUNT(abuf); ancount = DNS_HEADER_ANCOUNT(abuf); if (qdcount != 1) return ARES_EBADRESP; /* Expand the name from the question, and skip past the question. */ aptr = abuf + HFIXEDSZ; status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len); if (status != ARES_SUCCESS) return status; if (aptr + len + QFIXEDSZ > abuf + alen) { ares_free(ptrname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Examine each answer resource record (RR) in turn. */ hostname = NULL; aliases = ares_malloc(alias_alloc * sizeof(char *)); if (!aliases) { ares_free(ptrname); return ARES_ENOMEM; } for (i = 0; i < (int)ancount; i++) { /* Decode the RR up to the data field. */ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len); if (status != ARES_SUCCESS) break; aptr += len; if (aptr + RRFIXEDSZ > abuf + alen) { ares_free(rr_name); status = ARES_EBADRESP; break; } rr_type = DNS_RR_TYPE(aptr); rr_class = DNS_RR_CLASS(aptr); rr_len = DNS_RR_LEN(aptr); aptr += RRFIXEDSZ; if (aptr + rr_len > abuf + alen) { ares_free(rr_name); status = ARES_EBADRESP; break; } if (rr_class == C_IN && rr_type == T_PTR && strcasecmp(rr_name, ptrname) == 0) { /* Decode the RR data and set hostname to it. */ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, &len); if (status != ARES_SUCCESS) { ares_free(rr_name); break; } if (hostname) ares_free(hostname); hostname = rr_data; aliases[aliascnt] = ares_malloc((strlen(rr_data)+1) * sizeof(char)); if (!aliases[aliascnt]) { ares_free(rr_name); status = ARES_ENOMEM; break; } strncpy(aliases[aliascnt], rr_data, strlen(rr_data)+1); aliascnt++; if (aliascnt >= alias_alloc) { char **ptr; alias_alloc *= 2; ptr = ares_realloc(aliases, alias_alloc * sizeof(char *)); if(!ptr) { ares_free(rr_name); status = ARES_ENOMEM; break; } aliases = ptr; } } if (rr_class == C_IN && rr_type == T_CNAME) { /* Decode the RR data and replace ptrname with it. */ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, &len); if (status != ARES_SUCCESS) { ares_free(rr_name); break; } ares_free(ptrname); ptrname = rr_data; } ares_free(rr_name); aptr += rr_len; if (aptr > abuf + alen) { /* LCOV_EXCL_START: already checked above */ status = ARES_EBADRESP; break; } /* LCOV_EXCL_STOP */ } if (status == ARES_SUCCESS && !hostname) status = ARES_ENODATA; if (status == ARES_SUCCESS) { /* We got our answer. Allocate memory to build the host entry. */ hostent = ares_malloc(sizeof(struct hostent)); if (hostent) { hostent->h_addr_list = ares_malloc(2 * sizeof(char *)); if (hostent->h_addr_list) { hostent->h_addr_list[0] = ares_malloc(addrlen); if (hostent->h_addr_list[0]) { hostent->h_aliases = ares_malloc((aliascnt+1) * sizeof (char *)); if (hostent->h_aliases) { /* Fill in the hostent and return successfully. */ hostent->h_name = hostname; for (i=0 ; i<aliascnt ; i++) hostent->h_aliases[i] = aliases[i]; hostent->h_aliases[aliascnt] = NULL; hostent->h_addrtype = aresx_sitoss(family); hostent->h_length = aresx_sitoss(addrlen); memcpy(hostent->h_addr_list[0], addr, addrlen); hostent->h_addr_list[1] = NULL; *host = hostent; ares_free(aliases); ares_free(ptrname); return ARES_SUCCESS; } ares_free(hostent->h_addr_list[0]); } ares_free(hostent->h_addr_list); } ares_free(hostent); } status = ARES_ENOMEM; } for (i=0 ; i<aliascnt ; i++) if (aliases[i]) ares_free(aliases[i]); ares_free(aliases); if (hostname) ares_free(hostname); ares_free(ptrname); return status; }
int ares__get_hostent(FILE *fp, int family, struct hostent **host) { char *line = NULL, *p, *q, **alias; char *txtaddr, *txthost, *txtalias; int status; size_t addrlen, linesize, naliases; struct ares_addr addr; struct hostent *hostent = NULL; *host = NULL; /* Assume failure */ /* Validate family */ switch (family) { case AF_INET: case AF_INET6: case AF_UNSPEC: break; default: return ARES_EBADFAMILY; } while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) { /* Trim line comment. */ p = line; while (*p && (*p != '#')) p++; *p = '\0'; /* Trim trailing whitespace. */ q = p - 1; while ((q >= line) && ISSPACE(*q)) q--; *++q = '\0'; /* Skip leading whitespace. */ p = line; while (*p && ISSPACE(*p)) p++; if (!*p) /* Ignore line if empty. */ continue; /* Pointer to start of IPv4 or IPv6 address part. */ txtaddr = p; /* Advance past address part. */ while (*p && !ISSPACE(*p)) p++; if (!*p) /* Ignore line if reached end of line. */ continue; /* Null terminate address part. */ *p = '\0'; /* Advance to host name */ p++; while (*p && ISSPACE(*p)) p++; if (!*p) /* Ignore line if reached end of line. */ continue; /* LCOV_EXCL_LINE: trailing whitespace already stripped */ /* Pointer to start of host name. */ txthost = p; /* Advance past host name. */ while (*p && !ISSPACE(*p)) p++; /* Pointer to start of first alias. */ txtalias = NULL; if (*p) { q = p + 1; while (*q && ISSPACE(*q)) q++; if (*q) txtalias = q; } /* Null terminate host name. */ *p = '\0'; /* find out number of aliases. */ naliases = 0; if (txtalias) { p = txtalias; while (*p) { while (*p && !ISSPACE(*p)) p++; while (*p && ISSPACE(*p)) p++; naliases++; } } /* Convert address string to network address for the requested family. */ addrlen = 0; addr.family = AF_UNSPEC; addr.addrV4.s_addr = INADDR_NONE; if ((family == AF_INET) || (family == AF_UNSPEC)) { addr.addrV4.s_addr = inet_addr(txtaddr); if (addr.addrV4.s_addr != INADDR_NONE) { /* Actual network address family and length. */ addr.family = AF_INET; addrlen = sizeof(addr.addrV4); } } if ((family == AF_INET6) || ((family == AF_UNSPEC) && (!addrlen))) { if (ares_inet_pton(AF_INET6, txtaddr, &addr.addrV6) > 0) { /* Actual network address family and length. */ addr.family = AF_INET6; addrlen = sizeof(addr.addrV6); } } if (!addrlen) /* Ignore line if invalid address string for the requested family. */ continue; /* ** Actual address family possible values are AF_INET and AF_INET6 only. */ /* Allocate memory for the hostent structure. */ hostent = ares_malloc(sizeof(struct hostent)); if (!hostent) break; /* Initialize fields for out of memory condition. */ hostent->h_aliases = NULL; hostent->h_addr_list = NULL; /* Copy official host name. */ hostent->h_name = ares_strdup(txthost); if (!hostent->h_name) break; /* Copy network address. */ hostent->h_addr_list = ares_malloc(2 * sizeof(char *)); if (!hostent->h_addr_list) break; hostent->h_addr_list[1] = NULL; hostent->h_addr_list[0] = ares_malloc(addrlen); if (!hostent->h_addr_list[0]) break; if (addr.family == AF_INET) memcpy(hostent->h_addr_list[0], &addr.addrV4, sizeof(addr.addrV4)); else memcpy(hostent->h_addr_list[0], &addr.addrV6, sizeof(addr.addrV6)); /* Copy aliases. */ hostent->h_aliases = ares_malloc((naliases + 1) * sizeof(char *)); if (!hostent->h_aliases) break; alias = hostent->h_aliases; while (naliases) *(alias + naliases--) = NULL; *alias = NULL; while (txtalias) { p = txtalias; while (*p && !ISSPACE(*p)) p++; q = p; while (*q && ISSPACE(*q)) q++; *p = '\0'; if ((*alias = ares_strdup(txtalias)) == NULL) break; alias++; txtalias = *q ? q : NULL; } if (txtalias) /* Alias memory allocation failure. */ break; /* Copy actual network address family and length. */ hostent->h_addrtype = aresx_sitoss(addr.family); hostent->h_length = aresx_uztoss(addrlen); /* Free line buffer. */ ares_free(line); /* Return hostent successfully */ *host = hostent; return ARES_SUCCESS; } /* If allocated, free line buffer. */ if (line) ares_free(line); if (status == ARES_SUCCESS) { /* Memory allocation failure; clean up. */ if (hostent) { if (hostent->h_name) ares_free((char *) hostent->h_name); if (hostent->h_aliases) { for (alias = hostent->h_aliases; *alias; alias++) ares_free(*alias); ares_free(hostent->h_aliases); } if (hostent->h_addr_list) { if (hostent->h_addr_list[0]) ares_free(hostent->h_addr_list[0]); ares_free(hostent->h_addr_list); } ares_free(hostent); } return ARES_ENOMEM; } return status; }