From ee316741accfa169dff7bf0324caf2bb6323f070 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 15 Oct 2014 15:12:16 +0200 Subject: [PATCH] Async stub resolver using crafted packets --- src/context.c | 2 + src/general.c | 166 ++++++++++++++++++++++++++++++++------- src/request-internal.c | 15 +++- src/stub.c | 173 ----------------------------------------- src/types-internal.h | 12 +++ 5 files changed, 163 insertions(+), 205 deletions(-) diff --git a/src/context.c b/src/context.c index fafa180b..bce53f5e 100755 --- a/src/context.c +++ b/src/context.c @@ -1471,6 +1471,8 @@ cancel_dns_req(getdns_dns_req * req) ub_cancel(req->context->unbound_ctx, netreq->unbound_id); netreq->unbound_id = -1; + } else if (netreq->event.ev) { + req->loop->vmt->clear(req->loop, &netreq->event); } netreq = netreq->next; } diff --git a/src/general.c b/src/general.c index 720de265..548abfe5 100644 --- a/src/general.c +++ b/src/general.c @@ -44,6 +44,10 @@ #include "types-internal.h" #include "util-internal.h" #include "dnssec.h" +#include "stub.h" +#include "gldns/str2wire.h" +#include "gldns/pkthdr.h" + /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) @@ -54,14 +58,6 @@ static void ub_resolve_timeout(void *arg); static void handle_network_request_error(getdns_network_req * netreq, int err); static void handle_dns_request_complete(getdns_dns_req * dns_req); -static int submit_network_request(getdns_network_req * netreq); - -typedef struct netreq_cb_data -{ - getdns_network_req *netreq; - int err; - struct ub_result* ub_res; -} netreq_cb_data; /* cancel, cleanup and send timeout to callback */ static void @@ -106,18 +102,140 @@ handle_dns_request_complete(getdns_dns_req * dns_req) dns_req, create_getdns_response(dns_req)); } -static int -submit_network_request(getdns_network_req * netreq) +static void +stub_resolve_timeout_cb(void *userarg) +{ + getdns_network_req *netreq = (getdns_network_req *)userarg; + getdns_dns_req *dns_req = netreq->owner; + + (void) getdns_context_request_timed_out(dns_req); +} + +static void +stub_resolve_read_cb(void *userarg) +{ + getdns_network_req *netreq = (getdns_network_req *)userarg; + getdns_dns_req *dns_req = netreq->owner; + + static size_t pkt_buf_len = 4096; + size_t pkt_len = pkt_buf_len; + uint8_t pkt_buf[pkt_buf_len]; + uint8_t *pkt = pkt_buf; + + size_t read; + + dns_req->loop->vmt->clear(dns_req->loop, &netreq->event); + + read = recvfrom(netreq->udp_fd, pkt, pkt_len, 0, NULL, NULL); + if (read < GLDNS_HEADER_SIZE) + return; /* Not DNS */ + + if (GLDNS_ID_WIRE(pkt) != netreq->query_id) + return; /* Cache poisoning attempt ;) */ + + close(netreq->udp_fd); + netreq->state = NET_REQ_FINISHED; + ldns_wire2pkt(&(netreq->result), pkt, read); + + /* Do the dnssec here */ + netreq->secure = 0; + netreq->bogus = 0; + + netreq = dns_req->first_req; + while (netreq) { + if (netreq->state != NET_REQ_FINISHED && + netreq->state != NET_REQ_CANCELED) + return; + netreq = netreq->next; + } + handle_dns_request_complete(dns_req); +} + +static getdns_return_t +submit_stub_request(getdns_network_req *netreq) { getdns_dns_req *dns_req = netreq->owner; - int r = ub_resolve_async(dns_req->context->unbound_ctx, - dns_req->name, - netreq->request_type, - netreq->request_class, - netreq, - ub_resolve_callback, - &(netreq->unbound_id)); - return r; + static size_t pkt_buf_len = 4096; + size_t pkt_len = pkt_buf_len; + uint8_t pkt_buf[pkt_buf_len]; + uint8_t *pkt = pkt_buf; + int s; + struct getdns_upstream *upstream; + ssize_t sent; + + s = getdns_make_query_pkt_buf(dns_req->context, dns_req->name, + netreq->request_type, dns_req->extensions, pkt_buf, &pkt_len); + if (s == GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL) { + /* TODO: Allocate 64K and retry */ + return GETDNS_RETURN_GENERIC_ERROR; + } else if (s) + return GETDNS_RETURN_GENERIC_ERROR; + + netreq->query_id = ldns_get_random(); + GLDNS_ID_SET(pkt, netreq->query_id); + + upstream = &dns_req->upstreams->upstreams[dns_req->ns_index]; + + /* TODO: TCP */ + if (dns_req->context->dns_transport != GETDNS_TRANSPORT_UDP_ONLY && + dns_req->context->dns_transport != + GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP) + return GETDNS_RETURN_GENERIC_ERROR; + + if ((netreq->udp_fd = socket( + upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return GETDNS_RETURN_GENERIC_ERROR; + + sent = sendto(netreq->udp_fd, pkt, pkt_len, 0, + (struct sockaddr *)&upstream->addr, upstream->addr_len); + if (sent != pkt_len) { + close(netreq->udp_fd); + return GETDNS_RETURN_GENERIC_ERROR; + } + + netreq->event.userarg = netreq; + netreq->event.read_cb = stub_resolve_read_cb; + netreq->event.write_cb = NULL; + netreq->event.timeout_cb = stub_resolve_timeout_cb; + netreq->event.ev = NULL; + dns_req->loop->vmt->schedule(dns_req->loop, + netreq->udp_fd, dns_req->context->timeout, &netreq->event); + + if (s == GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL) { + /* TODO: Free the 64K allocated buffer */ + } + return GETDNS_RETURN_GOOD; +} + +static getdns_return_t +submit_network_request(getdns_network_req *netreq) +{ + getdns_return_t r; + getdns_dns_req *dns_req = netreq->owner; + + if (dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING || + dns_req->context->dns_transport == GETDNS_TRANSPORT_TCP_ONLY || + dns_req->context->dns_transport == GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN) { + + /* schedule the timeout */ + if (! dns_req->timeout.timeout_cb) { + dns_req->timeout.userarg = dns_req; + dns_req->timeout.read_cb = NULL; + dns_req->timeout.write_cb = NULL; + dns_req->timeout.timeout_cb = ub_resolve_timeout; + dns_req->timeout.ev = NULL; + if ((r = dns_req->loop->vmt->schedule(dns_req->loop, -1, + dns_req->context->timeout, &dns_req->timeout))) + return r; + } + + return ub_resolve_async(dns_req->context->unbound_ctx, + dns_req->name, netreq->request_type, netreq->request_class, + netreq, ub_resolve_callback, &(netreq->unbound_id)) ? + GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; + } + /* Submit with stub resolver */ + return submit_stub_request(netreq); } static void @@ -189,18 +307,6 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, getdns_context_track_outbound_request(req); - if (1 || context->resolution_type == GETDNS_RESOLUTION_RECURSING) { - /* schedule the timeout */ - req->timeout.userarg = req; - req->timeout.read_cb = NULL; - req->timeout.write_cb = NULL; - req->timeout.timeout_cb = ub_resolve_timeout; - req->timeout.ev = NULL; - if ((r = loop->vmt->schedule( - loop, -1, context->timeout, &req->timeout))) - return r; - } - if (!usenamespaces) /* issue all network requests */ for (netreq = req->first_req; !r && netreq; netreq = netreq->next) diff --git a/src/request-internal.c b/src/request-internal.c index 1e41a5c6..c7588b18 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -72,6 +72,10 @@ network_req_new(getdns_dns_req * owner, /* TODO: records and other extensions */ + net_req->query_id = -1; + net_req->udp_fd = -1; + memset(&net_req->event, 0, sizeof(net_req->event)); + return net_req; } @@ -86,6 +90,9 @@ dns_req_free(getdns_dns_req * req) /* free extensions */ getdns_dict_destroy(req->extensions); + if (req->upstreams && --req->upstreams->referenced == 0) + GETDNS_FREE(req->upstreams->mf, req->upstreams); + /* free network requests */ net_req = req->first_req; while (net_req) { @@ -125,8 +132,7 @@ dns_req_new(struct getdns_context *context, getdns_eventloop *loop, result->canceled = 0; result->current_req = NULL; result->first_req = NULL; - result->trans_id = ((uint64_t) ldns_get_random()) - ^ ((intptr_t) result); + result->trans_id = (uint64_t)(((intptr_t) result) ^ ldns_get_random()); getdns_dict_copy(extensions, &result->extensions); result->return_dnssec_status = context->return_dnssec_status; @@ -139,6 +145,11 @@ dns_req_new(struct getdns_context *context, getdns_eventloop *loop, /* check the specify_class extension */ (void) getdns_dict_get_int(extensions, "specify_class", &klass); + result->upstreams = context->upstreams; + if (result->upstreams) + result->upstreams->referenced++; + result->ns_index = 0; + /* create the requests */ req = network_req_new(result, request_type, klass, extensions); if (!req) { diff --git a/src/stub.c b/src/stub.c index 90ac5cab..4def08fe 100644 --- a/src/stub.c +++ b/src/stub.c @@ -39,179 +39,6 @@ #include "context.h" #include #include "util-internal.h" -#include "gldns/gbuffer.h" -#include "gldns/wire2str.h" -#include "extension/libmini_event.h" - -#define STUBDEBUG 1 - -typedef struct stub_resolver { - getdns_eventloop *ext; - getdns_context *context; - getdns_upstreams *upstreams; - const char *name; - uint16_t request_type; - getdns_dict *extensions; - gldns_buffer *response; - - - size_t request_pkt_len; - uint8_t *request_pkt; - - size_t ns_index; - size_t to_retry; - int udp_fd; - - getdns_eventloop_event event; -} stub_resolver; - -static void -udp_request_timeout_cb(void *arg) -{ - stub_resolver *resolver = (stub_resolver *)arg; - resolver->ext->vmt->clear(resolver->ext, &resolver->event); - fprintf(stderr, "TIMEOUT!\n"); -} - -static void -udp_request_read_cb(void *arg) -{ - stub_resolver *resolver = (stub_resolver *)arg; - ssize_t read; - - resolver->ext->vmt->clear(resolver->ext, &resolver->event); - - read = recvfrom(resolver->udp_fd, - gldns_buffer_current(resolver->response), - gldns_buffer_remaining(resolver->response), - 0, NULL, NULL); - - if (read == -1 || read == 0) - return; - - gldns_buffer_skip(resolver->response, read); - gldns_buffer_flip(resolver->response); -#if STUBDEBUG - do { - char *str = gldns_wire2str_pkt( - gldns_buffer_current(resolver->response), - gldns_buffer_limit(resolver->response)); - fprintf(stderr, "%s\n", str); - free(str); - } while(0); -#endif -} - -static getdns_return_t -query_ns(stub_resolver *resolver) -{ - struct getdns_upstream *upstream; - ssize_t sent; - - assert(resolver); - - if (resolver->ns_index >= resolver->upstreams->count) - return GETDNS_RETURN_GENERIC_ERROR; - - upstream = &resolver->upstreams->upstreams[resolver->ns_index]; - /* TODO: Try next upstream if something is not right with this one - */ - - /* TODO: Check how to connect first (udp or tcp) */ - if (resolver->context->dns_transport != GETDNS_TRANSPORT_UDP_ONLY && - resolver->context->dns_transport != - GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP) - return GETDNS_RETURN_GENERIC_ERROR; - - resolver->udp_fd = socket(upstream->addr.ss_family, SOCK_DGRAM, - IPPROTO_UDP); - if (resolver->udp_fd == -1) { - /* Retry with tcp? */ - return GETDNS_RETURN_GENERIC_ERROR; - } - - sent = sendto(resolver->udp_fd, - resolver->request_pkt, resolver->request_pkt_len, 0, - (struct sockaddr *)&upstream->addr, upstream->addr_len); - if (sent == -1 || sent != resolver->request_pkt_len) - return GETDNS_RETURN_GENERIC_ERROR; - - resolver->event.userarg = resolver; - resolver->event.read_cb = udp_request_read_cb; - resolver->event.write_cb = NULL; - resolver->event.timeout_cb = udp_request_timeout_cb; - resolver->ext->vmt->schedule(resolver->ext, resolver->udp_fd, - resolver->context->timeout, &resolver->event); - - return GETDNS_RETURN_GOOD; -} - -getdns_return_t -getdns_stub_dns_query_async(struct getdns_eventloop *ext, - getdns_context *context, const char *name, uint16_t request_type, - getdns_dict *extensions, gldns_buffer *response) -{ - getdns_return_t r; - stub_resolver *resolver; - - resolver = GETDNS_MALLOC(context->mf, stub_resolver); - if (! resolver) - return GETDNS_RETURN_MEMORY_ERROR; - - resolver->ext = ext; - resolver->context = context; - resolver->upstreams = context->upstreams; - resolver->upstreams->referenced++; - resolver->name = name; - resolver->request_type = request_type; - resolver->extensions = extensions; - resolver->response = response; - resolver->request_pkt = getdns_make_query_pkt(context, - name, request_type, extensions, &resolver->request_pkt_len); - if (! resolver->request_pkt) { - GETDNS_FREE(context->mf, resolver); - return GETDNS_RETURN_GENERIC_ERROR; - } -#if STUBDEBUG - do { - char *str = gldns_wire2str_pkt( - resolver->request_pkt, resolver->request_pkt_len); - fprintf(stderr, "%s\n", str); - free(str); - } while(0); -#endif - resolver->ns_index = 0; - resolver->to_retry = 2; - r = query_ns(resolver); - if (r) { - if (--resolver->upstreams->referenced == 0) - GETDNS_FREE(resolver->upstreams->mf, resolver->upstreams); - GETDNS_FREE(context->mf, resolver); - } - return r; -} - -getdns_return_t -getdns_stub_dns_query_sync( - getdns_context *context, const char *name, uint16_t request_type, - getdns_dict *extensions, gldns_buffer *response) -{ - getdns_return_t r; - getdns_mini_event mini_event; - getdns_eventloop *ext = &mini_event.loop; - - r = getdns_mini_event_init(context, &mini_event); - if (r) - return r; - - r = getdns_stub_dns_query_async(ext, context, name, request_type, - extensions, response); - if (r) - return r; - - ext->vmt->run(ext); - return GETDNS_RETURN_GOOD; -} int getdns_make_query_pkt_buf(getdns_context *context, const char *name, diff --git a/src/types-internal.h b/src/types-internal.h index 3ec30c75..d18b7ebc 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -41,6 +41,8 @@ #include "getdns/getdns.h" #include "getdns/getdns_extra.h" struct getdns_context; +typedef struct getdns_upstreams getdns_upstreams; + /** * \defgroup strings String Constants @@ -170,6 +172,12 @@ typedef struct getdns_network_req /* next request to issue after this one */ struct getdns_network_req *next; + + /* For stub resolving */ + int udp_fd; + uint16_t query_id; + getdns_eventloop_event event; + } getdns_network_req; /** @@ -215,6 +223,10 @@ typedef struct getdns_dns_req /* mem funcs */ struct mem_funcs my_mf; + /* Stuff for stub resolving */ + getdns_upstreams *upstreams; + size_t ns_index; + } getdns_dns_req; #define GETDNS_XMALLOC(obj, type, count) \