diff --git a/src/context.c b/src/context.c index 80e81130..6021d54b 100644 --- a/src/context.c +++ b/src/context.c @@ -822,6 +822,23 @@ getdns_extension_set_libevent_base( return GETDNS_RETURN_GOOD; } /* getdns_extension_set_libevent_base */ +/* cancel the request */ +static void cancel_dns_req(getdns_dns_req* req) { + getdns_network_req* netreq = req->first_req; + while (netreq) { + if (netreq->state == NET_REQ_IN_FLIGHT) { + /* for ev based ub, this should always prevent + * the callback from firing */ + ub_cancel(req->unbound, netreq->unbound_id); + netreq->state = NET_REQ_CANCELED; + } else if (netreq->state == NET_REQ_NOT_SENT) { + netreq->state = NET_REQ_CANCELED; + } + netreq = netreq->next; + } + req->canceled = 1; +} + /* * getdns_cancel_callback * @@ -832,8 +849,35 @@ getdns_cancel_callback( getdns_transaction_t transaction_id ) { - UNUSED_PARAM(context); - UNUSED_PARAM(transaction_id); + getdns_dns_req *req = NULL; + getdns_callback_t cb = NULL; + void* user_pointer = NULL; + + /* delete the node from the tree */ + ldns_rbnode_t* node = ldns_rbtree_delete(context->outbound_requests, + &transaction_id); + + if (!node) { + return GETDNS_RETURN_UNKNOWN_TRANSACTION; + } + req = (getdns_dns_req*) node->data; + /* do the cancel */ + + cancel_dns_req(req); + cb = req->user_callback; + user_pointer = req->user_pointer; + + /* clean up */ + context->memory_deallocator(node); + dns_req_free(req); + + /* fire callback */ + cb(context, + GETDNS_CALLBACK_CANCEL, + NULL, + user_pointer, + transaction_id); + return GETDNS_RETURN_GOOD; } /* getdns_cancel_callback */ diff --git a/src/convert.c b/src/convert.c index e3182b56..2e73e301 100644 --- a/src/convert.c +++ b/src/convert.c @@ -1,7 +1,7 @@ /** * * /brief getdns core functions - * + * * This is the meat of the API * Originally taken from the getdns API description pseudo implementation. * @@ -15,10 +15,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,6 +30,8 @@ #include #include +#include +#include /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) @@ -62,7 +64,32 @@ char * getdns_display_ip_address( struct getdns_bindata *bindata_of_ipv4_or_ipv6_address ) -{ UNUSED_PARAM(bindata_of_ipv4_or_ipv6_address); return NULL; } +{ + char buff[256]; + if (!bindata_of_ipv4_or_ipv6_address || + bindata_of_ipv4_or_ipv6_address->size == 0 || + !bindata_of_ipv4_or_ipv6_address->data) { + return NULL; + } + if (bindata_of_ipv4_or_ipv6_address->size == 4) { + const char* ipStr = inet_ntop(AF_INET, + bindata_of_ipv4_or_ipv6_address->data, + buff, + 256); + if (ipStr) { + return strdup(ipStr); + } + } else if (bindata_of_ipv4_or_ipv6_address->size == 16) { + const char* ipStr = inet_ntop(AF_INET6, + bindata_of_ipv4_or_ipv6_address->data, + buff, + 256); + if (ipStr) { + return strdup(ipStr); + } + } + return NULL; +} getdns_return_t getdns_strerror(getdns_return_t err, char *buf, size_t buflen) diff --git a/src/general.c b/src/general.c index 647def62..2865e6b5 100644 --- a/src/general.c +++ b/src/general.c @@ -106,28 +106,41 @@ static void handle_dns_request_complete(getdns_dns_req* dns_req) { /* clean up the request */ getdns_context_clear_outbound_request(dns_req); dns_req_free(dns_req); + if (response) { + cb(context, + GETDNS_CALLBACK_COMPLETE, + response, + user_arg, + trans_id); + } else { + cb(context, + GETDNS_CALLBACK_ERROR, + NULL, + user_arg, + trans_id); + } - cb(context, - GETDNS_CALLBACK_COMPLETE, - response, - user_arg, - trans_id); } static int submit_network_request(getdns_network_req* netreq) { getdns_dns_req *dns_req = netreq->owner; - netreq->state = NET_REQ_IN_FLIGHT; - return ub_resolve_event(dns_req->unbound, + int r = ub_resolve_event(dns_req->unbound, dns_req->name, netreq->request_type, netreq->request_class, netreq, ub_resolve_callback, &(netreq->unbound_id)); + netreq->state = NET_REQ_IN_FLIGHT; + return r; } static void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec, char* bogus) { getdns_network_req* netreq = (getdns_network_req*) arg; + /* if netreq->state == NET_REQ_NOT_SENT here, that implies + * that ub called us back immediately - probably from a local file. + * This most likely means that getdns_general has not returned + */ netreq->state = NET_REQ_FINISHED; if (err) { handle_network_request_error(netreq, err); @@ -153,13 +166,13 @@ static void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec getdns_return_t getdns_general_ub(struct ub_ctx* unbound, - getdns_context_t context, - const char *name, - uint16_t request_type, - struct getdns_dict *extensions, - void *userarg, - getdns_transaction_t *transaction_id, - getdns_callback_t callbackfn) { + getdns_context_t context, + const char *name, + uint16_t request_type, + struct getdns_dict *extensions, + void *userarg, + getdns_transaction_t *transaction_id, + getdns_callback_t callbackfn) { getdns_return_t gr; int r; @@ -182,10 +195,6 @@ getdns_general_ub(struct ub_ctx* unbound, req->user_pointer = userarg; req->user_callback = callbackfn; - /* TODO: - handle immediate callback - */ - if (transaction_id) { *transaction_id = req->trans_id; } @@ -221,7 +230,7 @@ getdns_general_ub(struct ub_ctx* unbound, /* Can't do async without an event loop * or callback */ - return GETDNS_RETURN_BAD_CONTEXT; + return GETDNS_RETURN_BAD_CONTEXT; } return getdns_general_ub(context->unbound_async, diff --git a/src/getdns/getdns.h b/src/getdns/getdns.h index 1ff2acb6..8965b4b8 100644 --- a/src/getdns/getdns.h +++ b/src/getdns/getdns.h @@ -252,6 +252,38 @@ struct event_base; #define GETDNS_STR_PORT "port" #define GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6 "return_both_v4_and_v6" +#define GETDNS_STR_KEY_STATUS "status" +#define GETDNS_STR_KEY_REPLIES_TREE "replies_tree" +#define GETDNS_STR_KEY_REPLIES_FULL "replies_full" +#define GETDNS_STR_KEY_JUST_ADDRS "just_address_answers" +#define GETDNS_STR_KEY_CANONICAL_NM "canonical_name" +#define GETDNS_STR_KEY_ANSWER_TYPE "answer_type" +#define GETDNS_STR_KEY_INTERM_ALIASES "intermediate_aliases" +#define GETDNS_STR_KEY_NAME "name" +#define GETDNS_STR_KEY_HEADER "header" +#define GETDNS_STR_KEY_QUESTION "question" +#define GETDNS_STR_KEY_ANSWER "answer" +#define GETDNS_STR_KEY_ID "id" +#define GETDNS_STR_KEY_QR "qr" +#define GETDNS_STR_KEY_OPC "opcode" +#define GETDNS_STR_KEY_TYPE "type" +#define GETDNS_STR_KEY_CLASS "class" +#define GETDNS_STR_KEY_TTL "ttl" +#define GETDNS_STR_KEY_RDATA "rdata" +#define GETDNS_STR_KEY_V4_ADDR "ipv4_address" +#define GETDNS_STR_KEY_V6_ADDR "ipv6_address" +#define GETDNS_STR_KEY_RDATA_RAW "rdata_raw" +#define GETDNS_STR_KEY_AUTHORITY "authority" +#define GETDNS_STR_KEY_ADDITIONAL "additional" +#define GETDNS_STR_KEY_QTYPE "qtype" +#define GETDNS_STR_KEY_QCLASS "qclass" +#define GETDNS_STR_KEY_QNAME "qname" + + + + + + /** @} */ diff --git a/src/request-internal.c b/src/request-internal.c index 8d0ed758..604b6bac 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -30,6 +30,7 @@ #include "types-internal.h" #include "util-internal.h" +#include /* useful macros */ #define gd_malloc(sz) context->memory_allocator(sz) @@ -111,6 +112,7 @@ getdns_dns_req* dns_req_new(getdns_context_t context, result->name = strdup(name); result->context = context; result->unbound = unbound; + result->canceled = 0; result->current_req = NULL; result->first_req = NULL; result->trans_id = ldns_get_random(); diff --git a/src/sync.c b/src/sync.c index 041de7f6..fbdef668 100644 --- a/src/sync.c +++ b/src/sync.c @@ -1,7 +1,7 @@ /** * * /brief getdns core functions for synchronous use - * + * * Originally taken from the getdns API description pseudo implementation. * */ @@ -14,10 +14,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -58,7 +58,7 @@ static void sync_callback_func(getdns_context_t context, static void * request_thread_start(void *arg) { struct sync_request_data *req_data = arg; - + req_data->response_status = getdns_general_ub(req_data->context->unbound_sync, req_data->context, req_data->name, @@ -67,7 +67,7 @@ static void * request_thread_start(void *arg) { req_data, NULL, sync_callback_func); - + event_base_dispatch(req_data->context->event_base_sync); return NULL; } @@ -94,7 +94,7 @@ getdns_general_sync( GETDNS_RETURN_GOOD, response }; - + /* create the thread */ int ret = pthread_attr_init(&attr); if (ret != 0) { @@ -133,7 +133,7 @@ getdns_address_sync( getdns_dict_set_int(extensions, GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6, GETDNS_EXTENSION_TRUE); - + getdns_return_t result = getdns_general_sync(context, name, GETDNS_RRTYPE_A, extensions, response_length, response); @@ -163,7 +163,7 @@ getdns_service_sync( struct getdns_dict **response ) { - + return getdns_general_sync(context, name, GETDNS_RRTYPE_SRV, extensions, response_length, response); diff --git a/src/test/tests_stub_async.c b/src/test/tests_stub_async.c index 70bf8f4d..8c6139ad 100644 --- a/src/test/tests_stub_async.c +++ b/src/test/tests_stub_async.c @@ -42,14 +42,10 @@ void this_callbackfn(struct getdns_context_t *this_context, { if (this_callback_type == GETDNS_CALLBACK_COMPLETE) /* This is a callback with data */ { - getdns_bindata* bindata = NULL; - getdns_dict_get_bindata(this_response, "pkt", &bindata); - if (bindata) { - char* data = (char*) bindata->data; - data[bindata->size] = 0; - memcpy(data, bindata->data, bindata->size); - fprintf(stdout, "The packet %s\n", data); - } + char* res = getdns_pretty_print_dict(this_response); + fprintf(stdout, "%s", res); + getdns_dict_destroy(this_response); + } else if (this_callback_type == GETDNS_CALLBACK_CANCEL) fprintf(stderr, "The callback with ID %lld was cancelled. Exiting.", this_transaction_id); @@ -82,7 +78,7 @@ main() const char * this_name = "www.google.com"; char* this_userarg = "somestring"; // Could add things here to help identify this call getdns_transaction_t this_transaction_id = 0; - + /* Make the call */ getdns_return_t dns_request_return = getdns_address(this_context, this_name, NULL, this_userarg, &this_transaction_id, this_callbackfn); diff --git a/src/types-internal.h b/src/types-internal.h index 94fd9108..15475a5d 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -42,7 +42,8 @@ struct ub_ctx; typedef enum network_req_state_enum { NET_REQ_NOT_SENT, NET_REQ_IN_FLIGHT, - NET_REQ_FINISHED + NET_REQ_FINISHED, + NET_REQ_CANCELED } network_req_state; /** @@ -77,6 +78,9 @@ typedef struct getdns_dns_req { /* name */ char *name; + /* canceled flag */ + int canceled; + /* current network request */ struct getdns_network_req *current_req; @@ -96,7 +100,8 @@ typedef struct getdns_dns_req { getdns_callback_t user_callback; void *user_pointer; - getdns_transaction_t trans_id; /* the transaction id */ + /* the transaction id */ + getdns_transaction_t trans_id; } getdns_dns_req; @@ -121,7 +126,4 @@ getdns_dns_req* dns_req_new(getdns_context_t context, void dns_req_free(getdns_dns_req* req); -/* cancel the request */ -getdns_return_t dns_req_cancel(getdns_dns_req* req); - #endif diff --git a/src/util-internal.c b/src/util-internal.c index f198d817..fe367c5f 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -34,6 +34,9 @@ getdns_return_t getdns_dict_util_set_string(getdns_dict* dict, char* name, const char* value) { /* account for the null term */ + if (value == NULL) { + return GETDNS_RETURN_WRONG_TYPE_REQUESTED; + } getdns_bindata type_bin = { strlen(value) + 1, (uint8_t*) value }; return getdns_dict_set_bindata(dict, name, &type_bin); } @@ -114,15 +117,336 @@ getdns_return_t sockaddr_to_dict(struct sockaddr_storage* address, getdns_dict** return GETDNS_RETURN_GOOD; } -getdns_dict *create_getdns_response(struct getdns_dns_req* completed_request) { - ldns_pkt* pkt = completed_request->first_req->result; - char* data = ldns_pkt2str(pkt); +/* 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); +} + +/* create the header dict */ +static getdns_dict *create_reply_header_dict(ldns_pkt* reply) { + /* { "id": 23456, "qr": 1, "opcode": 0, ... }, */ + int r = 0; getdns_dict* result = getdns_dict_create(); - getdns_bindata bindata = { - strlen(data), (uint8_t*) data - }; - getdns_dict_set_bindata(result, "pkt", &bindata); - free(data); + if (!result) { + return NULL; + } + /* cheat since we know GETDNS_RETURN_GOOD == 0 */ + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_ID, ldns_pkt_id(reply)); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_QR, ldns_pkt_qr(reply)); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_OPC, (int) ldns_pkt_get_opcode(reply)); + + if (r != 0) { + getdns_dict_destroy(result); + result = NULL; + } + return result; +} + +static getdns_dict *create_reply_question_dict(ldns_pkt* reply) { + /* { "qname": , "qtype": 1, "qclass": 1 } */ + int r = 0; + ldns_rr *question = NULL; + char* qname; + getdns_dict* result = getdns_dict_create(); + if (!result) { + return NULL; + } + question = ldns_rr_list_rr(ldns_pkt_question(reply), 0); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_QTYPE, (int) ldns_rr_get_type(question)); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_QCLASS, (int) ldns_rr_get_class(question)); + qname = convert_rdf_to_str(ldns_rr_owner(question)); + if (qname) { + r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_QNAME, qname); + free(qname); + } else { + r = 1; + } + if (r != 0) { + getdns_dict_destroy(result); + result = NULL; + } + return result; +} + +static getdns_dict *create_dict_from_rdf(ldns_rdf* rdf) { + /* + create a dict w/ rdata_raw and special fields if needed + i.e. + { + "ipv4_address": + "rdata_raw": + } + */ + int r = 0; + getdns_bindata rbin = { ldns_rdf_size(rdf), ldns_rdf_data(rdf) }; + getdns_dict* result = getdns_dict_create(); + r |= getdns_dict_set_bindata(result, GETDNS_STR_KEY_RDATA_RAW, &rbin); + if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_AAAA) { + r |= getdns_dict_set_bindata(result, GETDNS_STR_KEY_V6_ADDR, &rbin); + } else if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_A) { + r |= getdns_dict_set_bindata(result, GETDNS_STR_KEY_V4_ADDR, &rbin); + } + if (r != 0) { + getdns_dict_destroy(result); + result = NULL; + } + return result; +} + +static getdns_dict *create_dict_from_rr(ldns_rr* rr) { + /* + { + "name": , + "type": 1, + "class": 1, + "ttl": 33000, + "rdata": + { + "ipv4_address": + "rdata_raw": + } + } + */ + int r = 0; + char * name = NULL; + getdns_dict *result = getdns_dict_create(); + size_t rd_count = ldns_rr_rd_count(rr); + ldns_rdf* owner = ldns_rr_owner(rr); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_TYPE, (int) ldns_rr_get_type(rr)); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_CLASS, (int) ldns_rr_get_class(rr)); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_TTL, ldns_rr_ttl(rr)); + if (owner) { + name = convert_rdf_to_str(owner); + if (name) { + r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_NAME, name); + free(name); + } else { + r = 1; + } + } + /* create rdatas */ + if (rd_count >= 1) { + getdns_dict* rdata = create_dict_from_rdf(ldns_rr_rdf(rr, 0)); + r |= getdns_dict_set_dict(result, GETDNS_STR_KEY_RDATA, rdata); + } + /* TODO - if more than one, is rdata a list? */ + + if (r != 0) { + getdns_dict_destroy(result); + result = NULL; + } + return result; +} + +/* 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 */ +static getdns_list *create_list_from_rr_list(ldns_rr_list* rr_list) { + size_t i = 0; + size_t idx = 0; + int r = 0; + getdns_list *result = getdns_list_create(); + for (i = 0; i < ldns_rr_list_rr_count(rr_list); ++i) { + ldns_rr *rr = ldns_rr_list_rr(rr_list, i); + r |= getdns_list_add_item(result, &idx); + r |= getdns_list_set_dict(result, idx, create_dict_from_rr(rr)); + } + if (r != 0) { + 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(getdns_list* addrs, ldns_rr_list* rr_list) { + int r = 0; + size_t i = 0; + for (i = 0; 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; j < rd_count; ++j) { + size_t item_idx = 0; + ldns_rdf* rdf = ldns_rr_rdf(rr, j); + getdns_bindata rbin = { ldns_rdf_size(rdf), ldns_rdf_data(rdf) }; + r |= getdns_list_add_item(addrs, &item_idx); + r |= getdns_list_set_bindata(addrs, item_idx, &rbin); + } + } + return r; +} + +static getdns_dict *create_reply_dict(getdns_network_req* req, + 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 + } + + */ + int r = 0; + ldns_pkt *reply = req->result; + ldns_rr_list *rr_list = NULL; + ldns_rr* question = NULL; + getdns_dict *subdict = NULL; + getdns_list *sublist = NULL; + char* name = NULL; + + getdns_dict *result = getdns_dict_create(); + if (!result) { + return NULL; + } + /* header */ + subdict = create_reply_header_dict(reply); + r |= getdns_dict_set_dict(result, GETDNS_STR_KEY_HEADER, subdict); + getdns_dict_destroy(subdict); + + /* question */ + subdict = create_reply_question_dict(reply); + r |= getdns_dict_set_dict(result, GETDNS_STR_KEY_QUESTION, subdict); + getdns_dict_destroy(subdict); + + /* answers */ + rr_list = ldns_pkt_answer(reply); + sublist = create_list_from_rr_list(rr_list); + r |= getdns_dict_set_list(result, GETDNS_STR_KEY_ANSWER, sublist); + getdns_list_destroy(sublist); + if ((req->request_type == GETDNS_RRTYPE_A || + req->request_type == GETDNS_RRTYPE_AAAA) && + just_addrs != NULL) { + /* add to just addrs */ + r |= add_only_addresses(just_addrs, rr_list); + } + + /* authority */ + rr_list = ldns_pkt_authority(reply); + sublist = create_list_from_rr_list(rr_list); + r |= getdns_dict_set_list(result, GETDNS_STR_KEY_AUTHORITY, sublist); + getdns_list_destroy(sublist); + + /* additional */ + rr_list = ldns_pkt_additional(reply); + sublist = create_list_from_rr_list(rr_list); + r |= getdns_dict_set_list(result, GETDNS_STR_KEY_ADDITIONAL, sublist); + getdns_list_destroy(sublist); + + /* other stuff */ + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_ANSWER_TYPE, GETDNS_NAMETYPE_DNS); + question = ldns_rr_list_rr(ldns_pkt_question(reply), 0); + name = convert_rdf_to_str(ldns_rr_owner(question)); + if (name) { + r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, name); + free(name); + } else { + r |= 1; + } + 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_free(rdf); + return result; +} + +getdns_dict *create_getdns_response(struct getdns_dns_req* completed_request) { + getdns_dict* result = getdns_dict_create(); + getdns_list* replies_full = getdns_list_create(); + getdns_list* just_addrs = NULL; + getdns_list* replies_tree = getdns_list_create(); + getdns_network_req *netreq = completed_request->first_req; + + + int r = 0; + + if (completed_request->first_req->request_class == GETDNS_RRTYPE_A || + completed_request->first_req->request_class == GETDNS_RRTYPE_AAAA) { + just_addrs = getdns_list_create(); + } + + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_STATUS, GETDNS_RESPSTATUS_GOOD); + r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, get_canonical_name(completed_request->name)); + r |= getdns_dict_set_int(result, GETDNS_STR_KEY_ANSWER_TYPE, GETDNS_NAMETYPE_DNS); + + while (netreq) { + getdns_bindata full_data; + full_data.data = NULL; + full_data.size = 0; + ldns_pkt *pkt = netreq->result; + ldns_status s = ldns_pkt2wire(&(full_data.data), pkt, &(full_data.size)); + size_t idx = 0; + /* reply tree */ + getdns_dict *reply = create_reply_dict(netreq, just_addrs); + r |= getdns_list_add_item(replies_tree, &idx); + r |= getdns_list_set_dict(replies_tree, idx, reply); + /* buffer */ + if (s == LDNS_STATUS_OK) { + r |= getdns_list_add_item(replies_full, &idx); + r |= getdns_list_set_bindata(replies_full, idx, &full_data); + free(full_data.data); + } else { + r = 1; + break; + } + netreq = netreq->next; + } + + r |= getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_TREE, replies_tree); + r |= getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_FULL, replies_full); + if (just_addrs) { + r |= getdns_dict_set_list(result, GETDNS_STR_KEY_JUST_ADDRS, just_addrs); + } + + if (r != 0) { + getdns_dict_destroy(result); + result = NULL; + } + return result; }