Async stub resolver using crafted packets

This commit is contained in:
Willem Toorop 2014-10-15 15:12:16 +02:00
parent 8f254913f1
commit ee316741ac
5 changed files with 163 additions and 205 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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) {

View File

@ -39,179 +39,6 @@
#include "context.h"
#include <ldns/util.h>
#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,

View File

@ -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) \