diff --git a/src/context.c b/src/context.c index a32b6752..55492176 100644 --- a/src/context.c +++ b/src/context.c @@ -30,6 +30,7 @@ #include #include "context.h" +#include "util-internal.h" #include /* stuff to make it compile pedantically */ @@ -60,13 +61,9 @@ static struct getdns_dict* create_ipaddr_dict_from_rdf(ldns_rdf* rdf) { getdns_dict *result = getdns_dict_create(); /* set type */ if (rt == LDNS_RDF_TYPE_A) { - getdns_bindata type_bin = { (size_t) strlen(GETDNS_STR_IPV4), - (uint8_t*) GETDNS_STR_IPV4 }; - getdns_dict_set_bindata(result, GETDNS_STR_ADDRESS_TYPE, &type_bin); + getdns_dict_util_set_string(result, GETDNS_STR_ADDRESS_TYPE, GETDNS_STR_IPV4); } else { - getdns_bindata type_bin = { (size_t) strlen(GETDNS_STR_IPV6), - (uint8_t*) GETDNS_STR_IPV6 }; - getdns_dict_set_bindata(result, GETDNS_STR_ADDRESS_TYPE, &type_bin); + getdns_dict_util_set_string(result, GETDNS_STR_ADDRESS_TYPE, GETDNS_STR_IPV6); } /* set data */ getdns_bindata data_bin = { sz, ldns_rdf_data(rdf) }; diff --git a/src/dict.c b/src/dict.c index 8f502383..24771e1f 100644 --- a/src/dict.c +++ b/src/dict.c @@ -508,7 +508,7 @@ getdns_dict_set_bindata(struct getdns_dict *dict, char *name, struct getdns_bind item->data.bindata = (struct getdns_bindata *) malloc(sizeof(struct getdns_bindata)); if(item->data.bindata != NULL) { - item->data.bindata->data = (void *) malloc(item->data.bindata->size); + item->data.bindata->data = (void *) malloc(child_bindata->size); if(item->data.bindata->data != NULL) { item->data.bindata->size = child_bindata->size; diff --git a/src/general.c b/src/general.c index 22c96b56..f0c3eae0 100644 --- a/src/general.c +++ b/src/general.c @@ -64,25 +64,83 @@ /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) +/* libevent callback for a network request */ +static void dns_req_callback(int fd, short events, void *arg) { + getdns_dns_req *request = (getdns_dns_req*) arg; + uint8_t data[1500]; + if (events & EV_READ) { + while (1) { + ssize_t r = recv(fd, data, sizeof(data), MSG_DONTWAIT); + if (r < 0) { + if (errno == EAGAIN) return; + /* otherwise failed */ + request->user_callback(request->context, + GETDNS_CALLBACK_ERROR, + NULL, request->user_pointer, + request->trans_id); + } + /* parse a packet */ + ldns_pkt* pkt = NULL; + ldns_wire2pkt(&pkt, data, r); + if (pkt == NULL) { + /* otherwise failed */ + request->user_callback(request->context, + GETDNS_CALLBACK_ERROR, + NULL, request->user_pointer, + request->trans_id); + } else { + /* success */ + getdns_dict* response = create_getdns_response(pkt); + ldns_pkt_free(pkt); + request->user_callback(request->context, GETDNS_CALLBACK_COMPLETE, + response, request->user_pointer, + request->trans_id); + } + } + } else if (events & EV_TIMEOUT) { + request->user_callback(request->context, GETDNS_CALLBACK_TIMEOUT, + NULL, request->user_pointer, request->trans_id); + } + /* clean up ns since right now it's 1:1 with the request */ + nameserver_free(request->current_req->ns); + /* cleanup the request */ + dns_req_free(request); +} + /* submit a new request to the event loop */ static getdns_return_t submit_new_dns_req(getdns_dns_req *request) { - getdns_dict *nameserver = NULL; + getdns_dict *ip_dict = NULL; getdns_context_t context = request->context; - struct sockaddr_storage sockdata; + uint8_t* data = NULL; + size_t data_len = 0; + struct timeval timeout = { 5, 0 }; /* get first upstream server */ - getdns_list_get_dict(context->upstream_list, 0, &nameserver); - if (!nameserver) { + getdns_list_get_dict(context->upstream_list, 0, &ip_dict); + if (!ip_dict) { return GETDNS_RETURN_GENERIC_ERROR; } - /* setup socket */ - if (dict_to_sockaddr(nameserver, &sockdata) != GETDNS_RETURN_GOOD) { + /* get the nameserver */ + getdns_nameserver *ns = nameserver_new_from_ip_dict(context, ip_dict); + if (!ns) { return GETDNS_RETURN_GENERIC_ERROR; } - evutil_socket_t sock = socket(sockdata.ss_family, SOCK_DGRAM, 0); - evutil_make_socket_closeonexec(sock); - evutil_make_socket_nonblocking(sock); + + request->current_req->ns = ns; + + /* schedule on the loop */ + ns->event = event_new(context->event_base, request->current_req->ns->socket, + EV_READ | EV_TIMEOUT, + dns_req_callback, request); + + event_add(ns->event, &timeout); + + /* send data */ + ldns_pkt *pkt = request->current_req->pkt; + ldns_pkt2wire(&data, pkt, &data_len); + send(ns->socket, data, data_len, MSG_DONTWAIT); + free(data); return GETDNS_RETURN_GOOD; } diff --git a/src/nameserver-internal.c b/src/nameserver-internal.c index 2504e847..2286b7b3 100644 --- a/src/nameserver-internal.c +++ b/src/nameserver-internal.c @@ -28,4 +28,70 @@ * THE SOFTWARE. */ +#include "types-internal.h" +#include "util-internal.h" + +/* useful macros */ +#define gd_malloc(sz) context->memory_allocator(sz) +#define gd_free(ptr) context->memory_deallocator(ptr) + +getdns_nameserver* nameserver_new_from_ip_dict(getdns_context_t context, + getdns_dict* ip_dict) { + if (!context || !ip_dict) { + return NULL; + } + struct sockaddr_storage sockdata; + /* setup socket */ + if (dict_to_sockaddr(ip_dict, &sockdata) != GETDNS_RETURN_GOOD) { + return NULL; + } + getdns_nameserver *result = gd_malloc(sizeof(getdns_nameserver)); + if (!result) { + return NULL; + } + memset(result, 0, sizeof(getdns_nameserver)); + result->context = context; + + /* create socket */ + evutil_socket_t sock = socket(sockdata.ss_family, SOCK_DGRAM, 0); + evutil_make_socket_closeonexec(sock); + evutil_make_socket_nonblocking(sock); + + result->address = sockdata; + result->socket = sock; + + int connected = -1; + if (sockdata.ss_family == AF_INET) { + connected = connect(sock, (struct sockaddr *) &sockdata, sizeof(struct sockaddr_in)); + } else if (sockdata.ss_family == AF_INET6) { + connected = connect(sock, (struct sockaddr *) &sockdata, sizeof(struct sockaddr_in6)); + } + if (connected != 0) { + // sad + nameserver_free(result); + result= NULL; + } + + + return result; +} + +void nameserver_free(getdns_nameserver* nameserver) { + if (!nameserver) { + return; + } + if (nameserver->event) { + event_del(nameserver->event); + event_free(nameserver->event); + } + getdns_context_t context = nameserver->context; + evutil_closesocket(nameserver->socket); + gd_free(nameserver); + +} + +/* TODO */ +getdns_dict* nameserver_to_dict(getdns_nameserver* nameserver) { + return NULL; +} diff --git a/src/request-internal.c b/src/request-internal.c index fa1f73da..6299d534 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -49,8 +49,7 @@ void network_req_free(getdns_network_req* net_req) { getdns_network_req* network_req_new(getdns_context_t context, const char* name, uint16_t request_type, - struct getdns_dict* extensions, - getdns_transaction_t *transaction_id) { + struct getdns_dict* extensions) { getdns_network_req *net_req = NULL; ldns_pkt *pkt = NULL; net_req = gd_malloc(sizeof(getdns_network_req)); @@ -70,10 +69,6 @@ getdns_network_req* network_req_new(getdns_context_t context, return NULL; } net_req->pkt = pkt; - net_req->trans_id = ldns_pkt_id(pkt); - if (transaction_id) { - *transaction_id = net_req->trans_id; - } return net_req; } @@ -106,12 +101,17 @@ getdns_dns_req* dns_req_new(getdns_context_t context, /* create the initial network request */ net_req = network_req_new(context, name, request_type, - extensions, transaction_id); + extensions); if (!net_req) { dns_req_free(result); result = NULL; } + result->trans_id = ldns_pkt_id(net_req->pkt); + if (transaction_id) { + *transaction_id = result->trans_id; + } + result->current_req = net_req; net_req->owner = result; diff --git a/src/types-internal.h b/src/types-internal.h index e2a29cc0..22eda146 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -46,9 +46,9 @@ typedef struct getdns_nameserver { int failed_times; /* number of times which we have given this server a chance */ int timedout; /* number of times in a row a request has timed out */ - struct event event; + + struct event* event; - struct event timeout_event; /* used to keep the timeout for */ /* when we next probe this server. */ /* Valid if state == 0 */ /* Outstanding probe request for this nameserver, if any */ @@ -76,12 +76,7 @@ typedef struct getdns_network_req { int tx_count; /* the number of times that this packet has been sent */ /* not owned */ - struct nameserver *ns; /* the server which we last sent it (unused) */ - getdns_dict *upstream_server; - - struct event timeout_event; - - getdns_transaction_t trans_id; /* the transaction id */ + getdns_nameserver *ns; /* the server which we sent to */ unsigned transmit_me :1; /* needs to be transmitted */ @@ -102,6 +97,8 @@ typedef struct getdns_dns_req { /* callback data */ getdns_callback_t user_callback; void *user_pointer; + + getdns_transaction_t trans_id; /* the transaction id */ int pending_cb; /* Waiting for its callback to be invoked; not @@ -119,8 +116,7 @@ void network_req_free(getdns_network_req* net_req); getdns_network_req* network_req_new(getdns_context_t context, const char* name, uint16_t request_type, - struct getdns_dict* extensions, - getdns_transaction_t *transaction_id); + struct getdns_dict* extensions); /* dns request utils */ @@ -134,9 +130,12 @@ getdns_dns_req* dns_req_new(getdns_context_t context, void dns_req_free(getdns_dns_req* req); /* nameserver utils */ -getdns_nameserver* nameserver_new_from_ip_dict(getdns_dict* ip_dict); +getdns_nameserver* nameserver_new_from_ip_dict(getdns_context_t context, + getdns_dict* ip_dict); void nameserver_free(getdns_nameserver* nameserver); +getdns_dict* nameserver_to_dict(getdns_nameserver* nameserver); + #endif diff --git a/src/util-internal.c b/src/util-internal.c index a9a25a92..4e0ae0b6 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -30,6 +30,15 @@ #include "util-internal.h" +getdns_return_t getdns_dict_util_set_string(getdns_dict* dict, char* name, + char* value) { + char data[strlen(value) + 1]; + data[strlen(value)] = 0; + memcpy(data, value, strlen(value)); + getdns_bindata type_bin = { strlen(value) + 1, (uint8_t*) data }; + return getdns_dict_set_bindata(dict, name, &type_bin); +} + getdns_return_t dict_to_sockaddr(getdns_dict* ns, struct sockaddr_storage* output) { struct getdns_bindata *address_type = NULL; struct getdns_bindata *address_data = NULL; @@ -42,7 +51,7 @@ getdns_return_t dict_to_sockaddr(getdns_dict* ns, struct sockaddr_storage* outpu if (!address_type || !address_data) { return GETDNS_RETURN_GENERIC_ERROR; } - if (strcmp((char*) address_type->data, GETDNS_STR_IPV4)) { + if (strcmp((char*) address_type->data, GETDNS_STR_IPV4) == 0) { /* data is an in_addr_t */ struct sockaddr_in* addr = (struct sockaddr_in*) output; addr->sin_family = AF_INET; @@ -75,5 +84,14 @@ ldns_pkt *create_new_pkt(getdns_context_t context, return pkt; } - +getdns_dict *create_getdns_response(ldns_pkt* pkt) { + char* data = ldns_pkt2str(pkt); + getdns_dict* result = getdns_dict_create(); + getdns_bindata bindata = { + strlen(data), (uint8_t*) data + }; + getdns_dict_set_bindata(result, "pkt", &bindata); + free(data); + return result; +} diff --git a/src/util-internal.h b/src/util-internal.h index ecfd0b81..acf198dc 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -38,3 +38,10 @@ ldns_pkt *create_new_pkt(getdns_context_t context, const char* name, uint16_t request_type, struct getdns_dict* extensions); + +getdns_dict *create_getdns_response(ldns_pkt* pkt); + +/* dict util */ +getdns_return_t getdns_dict_util_set_string(getdns_dict* dict, char* name, + char* value); +