/** * * \file util-internal.c * @brief private library routines * * These routines are not intended to be used by applications calling into * the library. * */ /* * Copyright (c) 2013, NLnet Labs, Verisign, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names of the copyright holders nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "getdns/getdns.h" #include "dict.h" #include "list.h" #include "util-internal.h" #include "types-internal.h" #include "rr-dict.h" #if defined(WIRE_DEBUG) && WIRE_DEBUG #include "gldns/wire2str.h" #endif #include "gldns/str2wire.h" #include "gldns/gbuffer.h" #include "gldns/pkthdr.h" #include "rr-iter.h" /** * this is a comprehensive list of extensions and their data types * used by validate_extensions() * The list has to be in sorted order for bsearch lookup in function * validate_extensions. */ static getdns_extension_format extformats[] = { {"add_opt_parameters", t_dict}, {"add_warning_for_bad_dns", t_int}, {"dnssec_return_only_secure", t_int}, {"dnssec_return_status", t_int}, {"dnssec_return_validation_chain", t_int}, {"return_api_information", t_int}, {"return_both_v4_and_v6", t_int}, {"return_call_debugging", t_int}, {"specify_class", t_int}, }; static struct getdns_bindata IPv4_str_bindata = { 5, (void *)"IPv4" }; static struct getdns_bindata IPv6_str_bindata = { 5, (void *)"IPv6" }; getdns_return_t getdns_dict_util_set_string(struct getdns_dict * dict, char *name, const char *value) { /* account for the null term */ if (value == NULL) { return GETDNS_RETURN_WRONG_TYPE_REQUESTED; } struct getdns_bindata type_bin = { strlen(value) + 1, (uint8_t *) value }; return getdns_dict_set_bindata(dict, name, &type_bin); } getdns_return_t getdns_dict_util_get_string(struct getdns_dict * dict, char *name, char **result) { struct getdns_bindata *bindata = NULL; if (!result) { return GETDNS_RETURN_GENERIC_ERROR; } *result = NULL; getdns_dict_get_bindata(dict, name, &bindata); if (!bindata) { return GETDNS_RETURN_GENERIC_ERROR; } *result = (char *) bindata->data; return GETDNS_RETURN_GOOD; } getdns_return_t dict_to_sockaddr(struct getdns_dict * ns, struct sockaddr_storage * output) { char *address_type = NULL; struct getdns_bindata *address_data = NULL; uint32_t port = 53; memset(output, 0, sizeof(struct sockaddr_storage)); output->ss_family = AF_UNSPEC; uint32_t prt = 0; if (getdns_dict_get_int(ns, GETDNS_STR_PORT, &prt) == GETDNS_RETURN_GOOD) { port = prt; } getdns_dict_util_get_string(ns, GETDNS_STR_ADDRESS_TYPE, &address_type); getdns_dict_get_bindata(ns, GETDNS_STR_ADDRESS_DATA, &address_data); if (!address_type || !address_data) { return GETDNS_RETURN_GENERIC_ERROR; } if (strncmp(GETDNS_STR_IPV4, address_type, strlen(GETDNS_STR_IPV4)) == 0) { /* data is an in_addr_t */ struct sockaddr_in *addr = (struct sockaddr_in *) output; addr->sin_family = AF_INET; addr->sin_port = htons((uint16_t) port); memcpy(&(addr->sin_addr), address_data->data, address_data->size); } else { /* data is a v6 addr in host order */ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) output; addr->sin6_family = AF_INET6; addr->sin6_port = htons((uint16_t) port); memcpy(&(addr->sin6_addr), address_data->data, address_data->size); } return GETDNS_RETURN_GOOD; } getdns_return_t sockaddr_to_dict(struct getdns_context *context, struct sockaddr_storage *address, struct getdns_dict ** output) { if (!output || !address) { return GETDNS_RETURN_GENERIC_ERROR; } struct getdns_bindata addr_data; *output = NULL; struct getdns_dict *result = getdns_dict_create_with_context(context); if (address->ss_family == AF_INET) { struct sockaddr_in *addr = (struct sockaddr_in *) address; getdns_dict_util_set_string(result, GETDNS_STR_ADDRESS_TYPE, GETDNS_STR_IPV4); addr_data.size = sizeof(addr->sin_addr); addr_data.data = (uint8_t *) & (addr->sin_addr); getdns_dict_set_bindata(result, GETDNS_STR_ADDRESS_DATA, &addr_data); } else if (address->ss_family == AF_INET6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *) address; getdns_dict_util_set_string(result, GETDNS_STR_ADDRESS_TYPE, GETDNS_STR_IPV6); addr_data.size = sizeof(addr->sin6_addr); addr_data.data = (uint8_t *) & (addr->sin6_addr); getdns_dict_set_bindata(result, GETDNS_STR_ADDRESS_DATA, &addr_data); } else { // invalid getdns_dict_destroy(result); return GETDNS_RETURN_GENERIC_ERROR; } *output = result; return GETDNS_RETURN_GOOD; } /* result must be freed */ static char * convert_rdf_to_str(ldns_rdf * rdf) { if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_DNAME) { ldns_dname2canonical(rdf); } return ldns_rdf2str(rdf); } #define SET_WIRE_INT(X,Y) if (getdns_dict_set_int(result, #X , (int) \ GLDNS_ ## Y ## _WIRE(netreq->response))) break #define SET_WIRE_CNT(X,Y) if (getdns_dict_set_int(result, #X , (int) \ GLDNS_ ## Y (netreq->response))) break /* create the header dict */ static struct getdns_dict * create_reply_header_dict(getdns_context *context, getdns_network_req *netreq) { /* { "id": 23456, "qr": 1, "opcode": 0, ... }, */ struct getdns_dict *result = getdns_dict_create_with_context(context); if (!result) return NULL; do { SET_WIRE_INT(id, ID); SET_WIRE_INT(qr, QR); SET_WIRE_INT(aa, AA); SET_WIRE_INT(tc, TC); SET_WIRE_INT(rd, RD); SET_WIRE_INT(cd, CD); SET_WIRE_INT(ra, RA); SET_WIRE_INT(ad, AD); SET_WIRE_INT(opcode, OPCODE); SET_WIRE_INT(rcode, RCODE); SET_WIRE_INT(z, Z); SET_WIRE_CNT(qdcount, QDCOUNT); SET_WIRE_CNT(ancount, ANCOUNT); SET_WIRE_CNT(nscount, NSCOUNT); SET_WIRE_CNT(arcount, ARCOUNT); return result; } while (0); getdns_dict_destroy(result); return NULL; } /* helper to convert an rr_list to getdns_list. returns a list of objects where each object is a result from create_dict_from_rr */ struct getdns_list * create_list_from_rr_list(struct getdns_context *context, ldns_rr_list * rr_list) { size_t i = 0; size_t idx = 0; int r = GETDNS_RETURN_GOOD; struct getdns_list *result = getdns_list_create_with_context(context); struct getdns_dict *rrdict; for (i = 0; i < ldns_rr_list_rr_count(rr_list) && r == GETDNS_RETURN_GOOD; ++i) { ldns_rr *rr = ldns_rr_list_rr(rr_list, i); r = priv_getdns_create_dict_from_rr(context, rr, &rrdict); if (r != GETDNS_RETURN_GOOD) break; /* Could not create, do not destroy */ r = getdns_list_add_item(result, &idx); if (r == GETDNS_RETURN_GOOD) r = getdns_list_set_dict(result, idx, rrdict); getdns_dict_destroy(rrdict); } if (r != GETDNS_RETURN_GOOD) { getdns_list_destroy(result); result = NULL; } return result; } /* helper to add the ipv4 or ipv6 bin data to the list of addrs */ static getdns_return_t add_only_addresses(struct getdns_list * addrs, ldns_rr_list * rr_list) { int r = GETDNS_RETURN_GOOD; size_t i = 0; size_t item_idx = 0; r = getdns_list_get_length(addrs, &item_idx); for (i = 0; r == GETDNS_RETURN_GOOD && i < ldns_rr_list_rr_count(rr_list); ++i) { ldns_rr *rr = ldns_rr_list_rr(rr_list, i); size_t j = 0; size_t rd_count = ldns_rr_rd_count(rr); for (j = 0; r == GETDNS_RETURN_GOOD && j < rd_count; ++j) { ldns_rdf *rdf = ldns_rr_rdf(rr, j); if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_A && ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_AAAA) { continue; } struct getdns_dict *this_address = getdns_dict_create_with_extended_memory_functions( addrs->mf.mf_arg, addrs->mf.mf.ext.malloc, addrs->mf.mf.ext.realloc, addrs->mf.mf.ext.free); if (this_address == NULL) { r = GETDNS_RETURN_MEMORY_ERROR; break; } struct getdns_bindata rbin = { ldns_rdf_size(rdf), ldns_rdf_data(rdf) }; r = getdns_dict_set_bindata(this_address, GETDNS_STR_ADDRESS_TYPE, ( ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_A ? &IPv4_str_bindata : &IPv6_str_bindata)); if (r != GETDNS_RETURN_GOOD) { getdns_dict_destroy(this_address); break; } r = getdns_dict_set_bindata(this_address, GETDNS_STR_ADDRESS_DATA, &rbin); if (r != GETDNS_RETURN_GOOD) { getdns_dict_destroy(this_address); break; } r = getdns_list_set_dict(addrs, item_idx++, this_address); getdns_dict_destroy(this_address); } } return r; } static struct getdns_dict * create_reply_dict(struct getdns_context *context, getdns_network_req * req, struct getdns_list * just_addrs) { /* turn a packet into this glorious structure * * { # This is the first reply * "header": { "id": 23456, "qr": 1, "opcode": 0, ... }, * "question": { "qname": , "qtype": 1, "qclass": 1 }, * "answer": * [ * { * "name": , * "type": 1, * "class": 1, * "ttl": 33000, * "rdata": * { * "ipv4_address": * "rdata_raw": * } * } * ], * "authority": * [ * { * "name": , * "type": 1, * "class": 1, * "ttl": 600, * "rdata": * { * "ipv4_address": * "rdata_raw": * } * } * ] * "additional": [], * "canonical_name": , * "answer_type": GETDNS_NAMETYPE_DNS * } * */ getdns_return_t r = 0; ldns_pkt *reply = req->result; ldns_rr_list *rr_list = NULL; ldns_rr *question = NULL; struct getdns_dict *subdict = NULL; struct getdns_list *sublist = NULL; char *name = NULL; struct getdns_dict *result = getdns_dict_create_with_context(context); if (!result) { return NULL; } /* header */ do { subdict = create_reply_header_dict(context, req); r = getdns_dict_set_dict(result, GETDNS_STR_KEY_HEADER, subdict); getdns_dict_destroy(subdict); if (r != GETDNS_RETURN_GOOD) { break; } /* question */ r = priv_getdns_create_reply_question_dict(context, reply, &subdict); if (r != GETDNS_RETURN_GOOD) { break; } r = getdns_dict_set_dict(result, GETDNS_STR_KEY_QUESTION, subdict); getdns_dict_destroy(subdict); if (r != GETDNS_RETURN_GOOD) { break; } /* answers */ rr_list = ldns_pkt_answer(reply); sublist = create_list_from_rr_list(context, rr_list); r = getdns_dict_set_list(result, GETDNS_STR_KEY_ANSWER, sublist); getdns_list_destroy(sublist); if (r != GETDNS_RETURN_GOOD) { break; } if ((req->request_type == GETDNS_RRTYPE_A || req->request_type == GETDNS_RRTYPE_AAAA)) { /* add to just addrs */ r = add_only_addresses(just_addrs, rr_list); if (r != GETDNS_RETURN_GOOD) { break; } } /* authority */ rr_list = ldns_pkt_authority(reply); sublist = create_list_from_rr_list(context, rr_list); r = getdns_dict_set_list(result, GETDNS_STR_KEY_AUTHORITY, sublist); getdns_list_destroy(sublist); if (r != GETDNS_RETURN_GOOD) { break; } /* additional */ rr_list = ldns_pkt_additional(reply); sublist = create_list_from_rr_list(context, rr_list); if (sublist) { r |= priv_getdns_append_opt_rr(context, sublist, reply); r |= getdns_dict_set_list(result, GETDNS_STR_KEY_ADDITIONAL, sublist); getdns_list_destroy(sublist); if (r != GETDNS_RETURN_GOOD) { break; } } /* other stuff * Note that spec doesn't explicitely mention these. * They are only showcased in the response dict example */ r = getdns_dict_set_int(result, GETDNS_STR_KEY_ANSWER_TYPE, GETDNS_NAMETYPE_DNS); if (r != GETDNS_RETURN_GOOD) { break; } question = ldns_rr_list_rr(ldns_pkt_question(reply), 0); /* Some packets (like with rcode REFUSED) * don't even have RRs in the question section! */ if (question) { name = convert_rdf_to_str(ldns_rr_owner(question)); if (! name) { r = GETDNS_RETURN_MEMORY_ERROR; break; } r = getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, name); free(name); } } while (0); if (r != 0) { getdns_dict_destroy(result); result = NULL; } return result; } static char * get_canonical_name(const char *name) { ldns_rdf *rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, name); if (!rdf) { return NULL; } char *result = convert_rdf_to_str(rdf); ldns_rdf_deep_free(rdf); return result; } static int rrsigs_in_answer(ldns_pkt *pkt) { ldns_rr_list *rr_list = ldns_pkt_answer(pkt); size_t i; if (! rr_list) return 0; for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) if (LDNS_RR_TYPE_RRSIG == ldns_rr_get_type(ldns_rr_list_rr(rr_list, i))) return 1; return 0; } struct getdns_dict * create_getdns_response(getdns_dns_req *completed_request) { getdns_dict *result = getdns_dict_create_with_context( completed_request->context); getdns_list *replies_full = getdns_list_create_with_context( completed_request->context); getdns_list *just_addrs = NULL; getdns_list *replies_tree = getdns_list_create_with_context( completed_request->context); getdns_network_req *netreq, **netreq_p; char *canonical_name = NULL; getdns_return_t r = 0; int nreplies = 0, nanswers = 0, nsecure = 0, ninsecure = 0, nbogus = 0; struct getdns_bindata full_data; /* info (bools) about dns_req */ int dnssec_return_status; #if defined(WIRE_DEBUG) && WIRE_DEBUG char *str_pkt; priv_getdns_rr_iter rr_iter_storage, *rr_iter; priv_getdns_rdf_iter rdf_spc, *rdf_iter; int i; #endif for ( netreq_p = completed_request->netreqs ; ! r && (netreq = *netreq_p) ; netreq_p++) { assert(! netreq->result); if (! netreq->response_len) continue; if (ldns_wire2pkt(&(netreq->result), netreq->response, netreq->response_len)) { netreq->response_len = 0; continue; } #if defined(WIRE_DEBUG) && WIRE_DEBUG for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage , netreq->response , netreq->response_len) ; rr_iter ; rr_iter = priv_getdns_rr_iter_next(rr_iter)) { fprintf( stderr, "%d (%p): %s\n" , (int)rr_iter->n, rr_iter->pos , priv_getdns_rr_def_lookup( (int)gldns_read_uint16(rr_iter->rr_type) )->name); i = 0; for ( rdf_iter = priv_getdns_rdf_iter_init(&rdf_spc , rr_iter) ; rdf_iter ; rdf_iter = priv_getdns_rdf_iter_next(rdf_iter)) { fprintf( stderr, "\t%d (%p): " "len: %3d, left: %3d, " "name: %s\n" , i++ , rdf_iter->pos , (int)(rdf_iter->nxt - rdf_iter->pos) , (int)(rdf_iter->end - rdf_iter->nxt) , rdf_iter->rdd_pos->name ); } } if ((str_pkt = gldns_wire2str_pkt( netreq->response, netreq->response_len))) { fprintf(stderr, "%s\n", str_pkt); free(str_pkt); } #endif } dnssec_return_status = completed_request->dnssec_return_status || completed_request->dnssec_return_only_secure; if (completed_request->netreqs[0]->request_type == GETDNS_RRTYPE_A || completed_request->netreqs[0]->request_type == GETDNS_RRTYPE_AAAA) just_addrs = getdns_list_create_with_context( completed_request->context); do { canonical_name = get_canonical_name(completed_request->name); r = getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, canonical_name); free(canonical_name); if (r != GETDNS_RETURN_GOOD) { break; } r = getdns_dict_set_int(result, GETDNS_STR_KEY_ANSWER_TYPE, GETDNS_NAMETYPE_DNS); if (r != GETDNS_RETURN_GOOD) { break; } for ( netreq_p = completed_request->netreqs ; ! r && (netreq = *netreq_p) ; netreq_p++) { if (! netreq->result) continue; nreplies++; if (netreq->secure) nsecure++; else if (! netreq->bogus) ninsecure++; if (dnssec_return_status && netreq->bogus) nbogus++; else if (LDNS_RCODE_NOERROR == ldns_pkt_get_rcode(netreq->result)) nanswers++; if (! completed_request->dnssec_return_validation_chain) { if (dnssec_return_status && netreq->bogus) continue; else if (completed_request->dnssec_return_only_secure && ! netreq->secure) continue; } size_t idx = 0; /* reply tree */ struct getdns_dict *reply = create_reply_dict( completed_request->context, netreq, just_addrs); if (! reply) { r = GETDNS_RETURN_MEMORY_ERROR; break; } if (dnssec_return_status || completed_request->dnssec_return_validation_chain) { r = getdns_dict_set_int(reply, "dnssec_status", ( netreq->secure ? GETDNS_DNSSEC_SECURE : netreq->bogus ? GETDNS_DNSSEC_BOGUS : rrsigs_in_answer(netreq->result) && completed_request->context->has_ta ? GETDNS_DNSSEC_INDETERMINATE : GETDNS_DNSSEC_INSECURE )); if (r != GETDNS_RETURN_GOOD) { getdns_dict_destroy(reply); break; } } r = getdns_list_add_item(replies_tree, &idx); if (r != GETDNS_RETURN_GOOD) { getdns_dict_destroy(reply); // break inner while break; } r = getdns_list_set_dict(replies_tree, idx, reply); getdns_dict_destroy(reply); if (r != GETDNS_RETURN_GOOD) { // break inner while break; } /* buffer */ r = getdns_list_add_item(replies_full, &idx); if (r != GETDNS_RETURN_GOOD) { // break inner while break; } full_data.data = netreq->response; full_data.size = netreq->response_len; r = getdns_list_set_bindata(replies_full, idx, &full_data); if (r != GETDNS_RETURN_GOOD) { free(full_data.data); // break inner while break; } } if (r != GETDNS_RETURN_GOOD) break; r = getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_TREE, replies_tree); if (r != GETDNS_RETURN_GOOD) break; r = getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_FULL, replies_full); if (r != GETDNS_RETURN_GOOD) break; if (just_addrs) { r = getdns_dict_set_list(result, GETDNS_STR_KEY_JUST_ADDRS, just_addrs); if (r != GETDNS_RETURN_GOOD) { break; } } r = getdns_dict_set_int(result, GETDNS_STR_KEY_STATUS, nreplies == 0 ? GETDNS_RESPSTATUS_ALL_TIMEOUT : completed_request->dnssec_return_only_secure && nsecure == 0 && ninsecure > 0 ? GETDNS_RESPSTATUS_NO_SECURE_ANSWERS : completed_request->dnssec_return_only_secure && nsecure == 0 && nbogus > 0 ? GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS : nanswers == 0 ? GETDNS_RESPSTATUS_NO_NAME : GETDNS_RESPSTATUS_GOOD); } while (0); /* cleanup */ getdns_list_destroy(replies_tree); getdns_list_destroy(replies_full); getdns_list_destroy(just_addrs); if (r != 0) { getdns_dict_destroy(result); result = NULL; } return result; } /*This method can be used when e.g. a local lookup has been performed and the result is simply a list of addresses (not a DNS packet)*/ struct getdns_dict * create_getdns_response_from_rr_list(struct getdns_dns_req * completed_request, ldns_rr_list * response_list) { struct getdns_dict *result = getdns_dict_create_with_context(completed_request->context); struct getdns_list *replies_full = getdns_list_create_with_context( completed_request->context); struct getdns_list *replies_tree = getdns_list_create_with_context( completed_request->context); struct getdns_list *just_addrs = NULL; char *canonical_name = NULL; getdns_return_t r = 0; /* NOTE: With DNS packet, we ignore any DNSSEC related extensions since we don't populate the replies full or tree at all*/ just_addrs = getdns_list_create_with_context(completed_request->context); do { canonical_name = get_canonical_name(completed_request->name); r = getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, canonical_name); free(canonical_name); if (r != GETDNS_RETURN_GOOD) { break; } /* For local lookups we don't set an answer_type as there isn't a suitable one*/ r = add_only_addresses(just_addrs, response_list); if (r != GETDNS_RETURN_GOOD) { break; } if (r != GETDNS_RETURN_GOOD) break; r = getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_TREE, replies_tree); if (r != GETDNS_RETURN_GOOD) break; r = getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_FULL, replies_full); if (r != GETDNS_RETURN_GOOD) break; r = getdns_dict_set_list(result, GETDNS_STR_KEY_JUST_ADDRS, just_addrs); if (r != GETDNS_RETURN_GOOD) { break; } r = getdns_dict_set_int(result, GETDNS_STR_KEY_STATUS, GETDNS_RESPSTATUS_GOOD); } while (0); /* cleanup */ getdns_list_destroy(replies_tree); getdns_list_destroy(replies_full); getdns_list_destroy(just_addrs); if (r != 0) { getdns_dict_destroy(result); result = NULL; } return result; } /** * reverse an IP address for PTR lookup * @param address_data IP address to reverse * @return NULL on allocation failure * @return reversed string on success, caller must free storage via call to free() */ char * reverse_address(struct getdns_bindata *address_data) { ldns_rdf *addr_rdf; ldns_rdf *rev_rdf; char *rev_str; if (address_data->size == 4) addr_rdf = ldns_rdf_new(LDNS_RDF_TYPE_A, 4, address_data->data); else if (address_data->size == 16) addr_rdf = ldns_rdf_new(LDNS_RDF_TYPE_AAAA, 16, address_data->data); else return NULL; if (!addr_rdf) return NULL; rev_rdf = ldns_rdf_address_reverse(addr_rdf); ldns_rdf_free(addr_rdf); if (!rev_rdf) return NULL; rev_str = ldns_rdf2str(rev_rdf); ldns_rdf_deep_free(rev_rdf); return rev_str; } static int extformatcmp(const void *a, const void *b) { return strcmp(((getdns_extension_format *) a)->extstring, ((getdns_extension_format *) b)->extstring); } /*---------------------------------------- validate_extensions */ getdns_return_t validate_extensions(struct getdns_dict * extensions) { struct getdns_dict_item *item; getdns_extension_format *extformat; if (extensions) RBTREE_FOR(item, struct getdns_dict_item *, &(extensions->root)) { getdns_extension_format key; key.extstring = (char *) item->node.key; extformat = bsearch(&key, extformats, sizeof(extformats) / sizeof(getdns_extension_format), sizeof(getdns_extension_format), extformatcmp); if (!extformat) return GETDNS_RETURN_NO_SUCH_EXTENSION; if (item->dtype != extformat->exttype) return GETDNS_RETURN_EXTENSION_MISFORMAT; } return GETDNS_RETURN_GOOD; } /* validate_extensions */ getdns_return_t getdns_apply_network_result(getdns_network_req* netreq, struct ub_result* ub_res) { size_t dname_len; netreq->secure = ub_res->secure; netreq->bogus = ub_res->bogus; if (ub_res == NULL) /* Timeout */ return GETDNS_RETURN_GOOD; if (ub_res->answer_packet) { if (netreq->max_udp_payload_size < ub_res->answer_len) netreq->response = GETDNS_XMALLOC( netreq->owner->context->mf, uint8_t, ub_res->answer_len ); (void) memcpy(netreq->response, ub_res->answer_packet, (netreq->response_len = ub_res->answer_len)); return GETDNS_RETURN_GOOD; } if (ub_res->rcode == GETDNS_RCODE_SERVFAIL) { /* Likely to be caused by timeout from a synchronous * lookup. Don't forge a packet. */ return GETDNS_RETURN_GOOD; } /* Likely to be because libunbound refused the request * so ub_res->answer_packet=NULL, ub_res->answer_len=0 * So we need to create an answer packet. */ gldns_write_uint16(netreq->response , 0); /* query_id */ gldns_write_uint16(netreq->response + 2, 0); /* reset all flags */ gldns_write_uint16(netreq->response + GLDNS_QDCOUNT_OFF, 1); gldns_write_uint16(netreq->response + GLDNS_ANCOUNT_OFF, 0); gldns_write_uint16(netreq->response + GLDNS_NSCOUNT_OFF, 0); gldns_write_uint16(netreq->response + GLDNS_ARCOUNT_OFF, 0); GLDNS_OPCODE_SET(netreq->response, 3); GLDNS_QR_SET(netreq->response); GLDNS_RD_SET(netreq->response); GLDNS_RA_SET(netreq->response); GLDNS_RCODE_SET(netreq->response, ub_res->rcode); dname_len = netreq->max_udp_payload_size - GLDNS_HEADER_SIZE; if (gldns_str2wire_dname_buf(netreq->owner->name, netreq->response + GLDNS_HEADER_SIZE, &dname_len)) return GETDNS_RETURN_GENERIC_ERROR; gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + dname_len , netreq->request_type); gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + dname_len + 2 , netreq->request_class); netreq->response_len = GLDNS_HEADER_SIZE + dname_len + 4; return GETDNS_RETURN_GOOD; } getdns_return_t validate_dname(const char* dname) { int len; int label_len; const char* s; if (dname == NULL) { return GETDNS_RETURN_INVALID_PARAMETER; } len = strlen(dname); if (len > GETDNS_MAX_DNAME_LEN * 4 || len == 0) { return GETDNS_RETURN_BAD_DOMAIN_NAME; } if (len == 1 && dname[0] == '.') { /* root is ok */ return GETDNS_RETURN_GOOD; } /* By specification [RFC1035] the total length of a DNS label is * restricted to 63 octets and must be larger than 0 (except for the * final root-label). The total length of a domain name (i.e., label * octets and label length octets) is restricted to 255 octets or less. * With a fully qualified domain name this includes the last label * length octet for the root label. In a normalized representation the * number of labels (including the root) plus the number of octets in * each label may not be larger than 255. */ len = 0; label_len = 0; for (s = dname; *s; ++s) { switch (*s) { case '.': if (label_len > GETDNS_MAX_LABEL_LEN || label_len == 0) { return GETDNS_RETURN_BAD_DOMAIN_NAME; } label_len = 0; len += 1; break; case '\\': s += 1; if (isdigit(s[0])) { /* octet value */ if (! isdigit(s[1]) && ! isdigit(s[2])) return GETDNS_RETURN_BAD_DOMAIN_NAME; if ((s[0] - '0') * 100 + (s[1] - '0') * 10 + (s[2] - '0') > 255) return GETDNS_RETURN_BAD_DOMAIN_NAME; s += 2; } /* else literal char (1 octet) */ label_len++; len += 1; break; default: label_len++; len += 1; break; } } if (len > GETDNS_MAX_DNAME_LEN || label_len > GETDNS_MAX_LABEL_LEN) { return GETDNS_RETURN_BAD_DOMAIN_NAME; } return GETDNS_RETURN_GOOD; } /* validate_dname */ /* util-internal.c */