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; /* skip rr_type, rr_class, rr_ttl, rr_rdlen */ if (aptr + RRFIXEDSZ > abuf + alen) goto failed; aptr += RRFIXEDSZ; /* allocate result struct */ soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY); if (!soa) { status = ARES_ENOMEM; goto failed_stat; } /* 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); free(qname); free(rr_name); *soa_out = soa; return ARES_SUCCESS; failed: status = ARES_EBADRESP; failed_stat: ares_free_data(soa); if (qname) free(qname); if (rr_name) free(rr_name); return status; }
int ares_parse_ns_reply( const unsigned char* abuf, int alen, struct hostent** host ) { unsigned int qdcount, ancount; int status, i, rr_type, rr_class, rr_len; int nameservers_num; long len; const unsigned char *aptr; char* hostname, *rr_name, *rr_data, **nameservers; struct hostent *hostent; /* 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, &hostname, &len); if ( status != ARES_SUCCESS ) return status; if ( aptr + len + QFIXEDSZ > abuf + alen ) { free( hostname ); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Allocate nameservers array; ancount gives an upper bound */ nameservers = malloc( ( ancount + 1 ) * sizeof( char * ) ); if ( !nameservers ) { free( hostname ); return ARES_ENOMEM; } nameservers_num = 0; /* Examine each answer resource record (RR) in turn. */ 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 ) { status = ARES_EBADRESP; free(rr_name); break; } rr_type = DNS_RR_TYPE( aptr ); rr_class = DNS_RR_CLASS( aptr ); rr_len = DNS_RR_LEN( aptr ); aptr += RRFIXEDSZ; if ( rr_class == C_IN && rr_type == T_NS ) { /* Decode the RR data and add it to the nameservers list */ status = ares__expand_name_for_response( aptr, abuf, alen, &rr_data, &len); if ( status != ARES_SUCCESS ) { free(rr_name); break; } nameservers[nameservers_num] = malloc(strlen(rr_data)+1); if (nameservers[nameservers_num]==NULL) { free(rr_name); free(rr_data); status=ARES_ENOMEM; break; } strcpy(nameservers[nameservers_num],rr_data); free(rr_data); nameservers_num++; } free( rr_name ); aptr += rr_len; if ( aptr > abuf + alen ) { status = ARES_EBADRESP; break; } } if ( status == ARES_SUCCESS && nameservers_num == 0 ) { status = ARES_ENODATA; } if ( status == ARES_SUCCESS ) { /* We got our answer. Allocate memory to build the host entry. */ nameservers[nameservers_num] = NULL; hostent = malloc( sizeof( struct hostent ) ); if ( hostent ) { hostent->h_addr_list = malloc( 1 * sizeof( char * ) ); if ( hostent->h_addr_list ) { /* Fill in the hostent and return successfully. */ hostent->h_name = hostname; hostent->h_aliases = nameservers; hostent->h_addrtype = AF_INET; hostent->h_length = sizeof( struct in_addr ); hostent->h_addr_list[0] = NULL; *host = hostent; return ARES_SUCCESS; } free( hostent ); } status = ARES_ENOMEM; } for ( i = 0; i < nameservers_num; i++ ) free( nameservers[i] ); free( nameservers ); free( hostname ); return status; }
int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, struct hostent **host, struct ares_addr6ttl *addrttls, int *naddrttls) { unsigned int qdcount, ancount; int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ int naliases; long len; const unsigned char *aptr; char *hostname, *rr_name, *rr_data, **aliases; struct ares_in6_addr *addrs; struct hostent *hostent; const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; /* Set *host to NULL for all failure cases. */ if (host) *host = NULL; /* Same with *naddrttls. */ if (naddrttls) *naddrttls = 0; /* 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, &hostname, &len); if (status != ARES_SUCCESS) return status; if (aptr + len + QFIXEDSZ > abuf + alen) { free(hostname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Allocate addresses and aliases; ancount gives an upper bound for both. */ if (host) { addrs = malloc(ancount * sizeof(struct ares_in6_addr)); if (!addrs) { free(hostname); return ARES_ENOMEM; } aliases = malloc((ancount + 1) * sizeof(char *)); if (!aliases) { free(hostname); free(addrs); return ARES_ENOMEM; } } else { addrs = NULL; aliases = NULL; } naddrs = 0; naliases = 0; /* Examine each answer resource record (RR) in turn. */ 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) { status = ARES_EBADRESP; break; } rr_type = DNS_RR_TYPE(aptr); rr_class = DNS_RR_CLASS(aptr); rr_len = DNS_RR_LEN(aptr); rr_ttl = DNS_RR_TTL(aptr); aptr += RRFIXEDSZ; if (rr_class == C_IN && rr_type == T_AAAA && rr_len == sizeof(struct ares_in6_addr) && strcasecmp(rr_name, hostname) == 0) { if (addrs) { if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) { status = ARES_EBADRESP; break; } memcpy(&addrs[naddrs], aptr, sizeof(struct ares_in6_addr)); } if (naddrs < max_addr_ttls) { struct ares_addr6ttl * const at = &addrttls[naddrs]; if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) { status = ARES_EBADRESP; break; } memcpy(&at->ip6addr, aptr, sizeof(struct ares_in6_addr)); at->ttl = rr_ttl; } naddrs++; status = ARES_SUCCESS; } if (rr_class == C_IN && rr_type == T_CNAME) { /* Record the RR name as an alias. */ if (aliases) aliases[naliases] = rr_name; else free(rr_name); naliases++; /* Decode the RR data and replace the hostname with it. */ status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, &len); if (status != ARES_SUCCESS) break; free(hostname); hostname = rr_data; /* Take the min of the TTLs we see in the CNAME chain. */ if (cname_ttl > rr_ttl) cname_ttl = rr_ttl; } else free(rr_name); aptr += rr_len; if (aptr > abuf + alen) { status = ARES_EBADRESP; break; } } if (status == ARES_SUCCESS && naddrs == 0) status = ARES_ENODATA; if (status == ARES_SUCCESS) { /* We got our answer. */ if (naddrttls) { const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; for (i = 0; i < n; i++) { /* Ensure that each A TTL is no larger than the CNAME TTL. */ if (addrttls[i].ttl > cname_ttl) addrttls[i].ttl = cname_ttl; } *naddrttls = n; } if (aliases) aliases[naliases] = NULL; if (host) { /* Allocate memory to build the host entry. */ hostent = malloc(sizeof(struct hostent)); if (hostent) { hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); if (hostent->h_addr_list) { /* Fill in the hostent and return successfully. */ hostent->h_name = hostname; hostent->h_aliases = aliases; hostent->h_addrtype = AF_INET6; hostent->h_length = sizeof(struct ares_in6_addr); for (i = 0; i < naddrs; i++) hostent->h_addr_list[i] = (char *) &addrs[i]; hostent->h_addr_list[naddrs] = NULL; *host = hostent; return ARES_SUCCESS; } free(hostent); } status = ARES_ENOMEM; } } if (aliases) { for (i = 0; i < naliases; i++) free(aliases[i]); free(aliases); } free(addrs); free(hostname); return status; }
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) { free(ptrname); return ARES_EBADRESP; } aptr += len + QFIXEDSZ; /* Examine each answer resource record (RR) in turn. */ hostname = NULL; aliases = malloc(alias_alloc * sizeof(char *)); if (!aliases) { 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) { 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 (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) { free(rr_name); break; } if (hostname) free(hostname); hostname = rr_data; aliases[aliascnt] = malloc((strlen(rr_data)+1) * sizeof(char)); if (!aliases[aliascnt]) { 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 = realloc(aliases, alias_alloc * sizeof(char *)); if(!ptr) { 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) { free(rr_name); break; } free(ptrname); ptrname = rr_data; } free(rr_name); aptr += rr_len; if (aptr > abuf + alen) { status = ARES_EBADRESP; break; } } if (status == ARES_SUCCESS && !hostname) status = ARES_ENODATA; if (status == ARES_SUCCESS) { /* We got our answer. Allocate memory to build the host entry. */ hostent = malloc(sizeof(struct hostent)); if (hostent) { hostent->h_addr_list = malloc(2 * sizeof(char *)); if (hostent->h_addr_list) { hostent->h_addr_list[0] = malloc(addrlen); if (hostent->h_addr_list[0]) { hostent->h_aliases = 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; free(aliases); free(ptrname); return ARES_SUCCESS; } free(hostent->h_addr_list[0]); } free(hostent->h_addr_list); } free(hostent); } status = ARES_ENOMEM; } for (i=0 ; i<aliascnt ; i++) if (aliases[i]) free(aliases[i]); free(aliases); if (hostname) free(hostname); free(ptrname); return status; }