mirror of https://github.com/getdnsapi/getdns.git
Async stub resolver using crafted packets
This commit is contained in:
parent
8f254913f1
commit
ee316741ac
|
@ -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;
|
||||
}
|
||||
|
|
166
src/general.c
166
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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
173
src/stub.c
173
src/stub.c
|
@ -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,
|
||||
|
|
|
@ -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) \
|
||||
|
|
Loading…
Reference in New Issue