stub tcp lookups

And the foundation for tcp keep connections open
This commit is contained in:
Willem Toorop 2014-10-18 00:25:41 +02:00
parent c017e75f5a
commit 181d8cd3f4
8 changed files with 484 additions and 116 deletions

View File

@ -50,6 +50,7 @@
#include "types-internal.h"
#include "util-internal.h"
#include "dnssec.h"
#include "stub.h"
void *plain_mem_funcs_user_arg = MF_PLAIN;
@ -342,20 +343,32 @@ upstream_dict(getdns_context *context, getdns_upstream *upstream)
static int
net_req_query_id_cmp(const void *id1, const void *id2)
{
return (int)((struct getdns_network_req *)id1)->query_id -
(int)((struct getdns_network_req *)id1)->query_id;
return (intptr_t)id1 - (intptr_t)id2;
}
static void
upstream_init(getdns_upstream *upstream, struct addrinfo *ai)
upstream_init(getdns_upstream *upstream,
getdns_upstreams *parent, struct addrinfo *ai)
{
assert(upstream && ai);
upstream->upstreams = parent;
upstream->addr_len = ai->ai_addrlen;
(void) memcpy(&upstream->addr, ai->ai_addr, ai->ai_addrlen);
/* How is this upstream doing? */
upstream->to_retry = 2;
upstream->back_off = 1;
upstream->tcp_fd = -1;
(void) memset(&upstream->tcp_event, 0, sizeof(upstream->tcp_event));
/* For sharing a socket to this upstream with TCP */
upstream->fd = -1;
(void) memset(&upstream->event, 0, sizeof(upstream->event));
upstream->loop = NULL;
(void) memset(&upstream->tcp, 0, sizeof(upstream->tcp));
upstream->write_queue = NULL;
upstream->write_queue_tail = NULL;
/* Tracking of network requests on this socket */
getdns_rbtree_init(&upstream->netreq_by_query_id,
net_req_query_id_cmp);
}
@ -469,7 +482,7 @@ set_os_defaults(struct getdns_context *context)
upstream = &context->upstreams->
upstreams[context->upstreams->count++];
upstream_init(upstream, result);
upstream_init(upstream, context->upstreams, result);
freeaddrinfo(result);
}
fclose(in);
@ -973,6 +986,7 @@ set_ub_dns_transport(struct getdns_context* context,
set_ub_string_opt(context, "do-tcp:", "no");
break;
case GETDNS_TRANSPORT_TCP_ONLY:
case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN:
set_ub_string_opt(context, "do-udp:", "no");
set_ub_string_opt(context, "do-tcp:", "yes");
break;
@ -996,6 +1010,7 @@ set_ldns_dns_transport(struct getdns_context* context,
ldns_resolver_set_fallback(context->ldns_res, false);
break;
case GETDNS_TRANSPORT_TCP_ONLY:
case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN:
ldns_resolver_set_usevc(context->ldns_res, 1);
break;
default:
@ -1324,7 +1339,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
if (getaddrinfo(addrstr, portstr, &hints, &ai))
goto invalid_parameter;
upstream_init(upstream, ai);
upstream_init(upstream, upstreams, ai);
upstreams->count++;
freeaddrinfo(ai);
}
@ -1479,19 +1494,18 @@ getdns_context_set_memory_functions(struct getdns_context *context,
/* cancel the request */
static void
cancel_dns_req(getdns_dns_req * req)
cancel_dns_req(getdns_dns_req *req)
{
getdns_network_req *netreq = req->first_req;
while (netreq) {
getdns_network_req *netreq;
for (netreq = req->first_req; netreq; netreq = netreq->next)
if (netreq->unbound_id != -1) {
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;
}
} else
priv_getdns_cancel_stub_request(netreq);
req->canceled = 1;
}

View File

@ -70,13 +70,25 @@ struct filechg {
};
typedef struct getdns_upstream {
socklen_t addr_len;
struct sockaddr_storage addr;
int to_retry;
int back_off;
int tcp_fd;
getdns_eventloop_event tcp_event;
getdns_rbtree_t netreq_by_query_id;
struct getdns_upstreams *upstreams;
socklen_t addr_len;
struct sockaddr_storage addr;
/* How is this upstream doing? */
int to_retry;
int back_off;
/* For sharing a TCP socket to this upstream */
int fd;
getdns_eventloop_event event;
getdns_eventloop *loop;
getdns_tcp_state tcp;
/* Pipelining of TCP network requests */
getdns_network_req *write_queue;
getdns_network_req *write_queue_tail;
getdns_rbtree_t netreq_by_query_id;
} getdns_upstream;
typedef struct getdns_upstreams {

View File

@ -82,13 +82,18 @@ void
priv_getdns_check_dns_req_complete(getdns_dns_req *dns_req)
{
getdns_network_req *netreq;
int results_found = 0;
for (netreq = dns_req->first_req; netreq; netreq = netreq->next)
if (netreq->state != NET_REQ_FINISHED &&
netreq->state != NET_REQ_CANCELED)
return;
else if (netreq->result)
results_found = 1;
if (is_extension_set(dns_req->extensions,
if (! results_found)
priv_getdns_call_user_callback(dns_req, NULL);
else if (is_extension_set(dns_req->extensions,
"dnssec_return_validation_chain"))
priv_getdns_get_validation_chain(dns_req);
else
@ -126,9 +131,7 @@ 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) {
if (dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING) {
/* schedule the timeout */
if (! dns_req->timeout.timeout_cb) {

View File

@ -82,11 +82,11 @@ typedef struct getdns_eventloop_event {
void *ev;
} getdns_eventloop_event;
inline void getdns_eventloop_init_event(getdns_eventloop_event *el,
void *userarg, getdns_eventloop_callback read_cb,
inline getdns_eventloop_event *getdns_eventloop_event_init(
getdns_eventloop_event *ev,void *userarg, getdns_eventloop_callback read_cb,
getdns_eventloop_callback write_cb, getdns_eventloop_callback timeout_cb)
{ el->userarg = userarg; el->read_cb = read_cb; el->write_cb = write_cb;
el->timeout_cb = timeout_cb; el->ev = NULL; }
{ ev->userarg = userarg; ev->read_cb = read_cb; ev->write_cb = write_cb;
ev->timeout_cb = timeout_cb; ev->ev = NULL; return ev; }
typedef struct getdns_eventloop_vmt getdns_eventloop_vmt;
typedef struct getdns_eventloop {

View File

@ -72,10 +72,11 @@ 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));
net_req->upstream = NULL;
net_req->fd = -1;
memset(&net_req->event, 0, sizeof(net_req->event));
memset(&net_req->tcp, 0, sizeof(net_req->tcp));
net_req->query_id = -1;
return net_req;
}

View File

@ -160,7 +160,8 @@ getdns_make_query_pkt_buf(getdns_context *context, const char *name,
buf[0] = 0; /* dname for . */
gldns_write_uint16(buf + 1, GLDNS_RR_TYPE_OPT);
gldns_write_uint16(buf + 3, (uint16_t) edns_maximum_udp_payload_size);
gldns_write_uint16(buf + 3,
(uint16_t) edns_maximum_udp_payload_size);
buf[5] = (uint8_t) edns_extended_rcode;
buf[6] = (uint8_t) edns_version;
buf[7] = edns_do_bit ? 0x80 : 0;
@ -264,25 +265,64 @@ getdns_sock_nonblock(int sockfd)
}
static void
stub_resolve_timeout_cb(void *userarg)
stub_next_upstream(getdns_network_req *netreq)
{
getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dns_req = netreq->owner;
getdns_dns_req *dnsreq = netreq->owner;
if (! --netreq->upstream->to_retry)
netreq->upstream->to_retry = -(netreq->upstream->back_off *= 2);
if (++dns_req->upstreams->current > dns_req->upstreams->count)
dns_req->upstreams->current = 0;
(void) getdns_context_request_timed_out(dns_req);
if (++dnsreq->upstreams->current > dnsreq->upstreams->count)
dnsreq->upstreams->current = 0;
}
static void
stub_resolve_read_cb(void *userarg)
stub_cleanup(getdns_network_req *netreq)
{
getdns_dns_req *dnsreq = netreq->owner;
if (netreq->event.ev)
dnsreq->loop->vmt->clear(dnsreq->loop, &netreq->event);
GETDNS_NULL_FREE(dnsreq->context->mf, netreq->tcp.write_buf);
GETDNS_NULL_FREE(dnsreq->context->mf, netreq->tcp.read_buf);
/* TODO: Delete from write_queue */
/* TODO: Delete from netreq_by_query_id */
}
void
priv_getdns_cancel_stub_request(getdns_network_req *netreq)
{
stub_cleanup(netreq);
if (netreq->fd >= 0) close(netreq->fd);
}
static void
stub_erred(getdns_network_req *netreq)
{
stub_next_upstream(netreq);
stub_cleanup(netreq);
if (netreq->fd >= 0) close(netreq->fd);
priv_getdns_check_dns_req_complete(netreq->owner);
}
static void
stub_timeout_cb(void *userarg)
{
getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dns_req = netreq->owner;
stub_next_upstream(netreq);
stub_cleanup(netreq);
if (netreq->fd >= 0) close(netreq->fd);
(void) getdns_context_request_timed_out(netreq->owner);
}
static void
stub_udp_read_cb(void *userarg)
{
getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dnsreq = netreq->owner;
static size_t pkt_buf_len = 4096;
size_t pkt_len = pkt_buf_len;
@ -291,9 +331,9 @@ stub_resolve_read_cb(void *userarg)
size_t read;
dns_req->loop->vmt->clear(dns_req->loop, &netreq->event);
dnsreq->loop->vmt->clear(dnsreq->loop, &netreq->event);
read = recvfrom(netreq->udp_fd, pkt, pkt_len, 0, NULL, NULL);
read = recvfrom(netreq->fd, pkt, pkt_len, 0, NULL, NULL);
if (read == -1 && (errno = EAGAIN || errno == EWOULDBLOCK))
return;
@ -303,23 +343,23 @@ stub_resolve_read_cb(void *userarg)
if (GLDNS_ID_WIRE(pkt) != netreq->query_id)
return; /* Cache poisoning attempt ;) */
close(netreq->udp_fd);
close(netreq->fd);
netreq->state = NET_REQ_FINISHED;
ldns_wire2pkt(&(netreq->result), pkt, read);
dns_req->upstreams->current = 0;
dnsreq->upstreams->current = 0;
/* Do the dnssec here */
/* TODO: DNSSEC */
netreq->secure = 0;
netreq->bogus = 0;
priv_getdns_check_dns_req_complete(dns_req);
priv_getdns_check_dns_req_complete(dnsreq);
}
static void
stub_resolve_write_cb(void *userarg)
stub_udp_write_cb(void *userarg)
{
getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dns_req = netreq->owner;
getdns_dns_req *dnsreq = netreq->owner;
static size_t pkt_buf_len = 4096;
uint8_t pkt_buf[pkt_buf_len];
@ -327,114 +367,381 @@ stub_resolve_write_cb(void *userarg)
size_t pkt_len;
size_t pkt_size_needed;
dns_req->loop->vmt->clear(dns_req->loop, &netreq->event);
dnsreq->loop->vmt->clear(dnsreq->loop, &netreq->event);
pkt_size_needed = getdns_get_query_pkt_size(dns_req->context,
dns_req->name, netreq->request_type, dns_req->extensions);
pkt_size_needed = getdns_get_query_pkt_size(dnsreq->context,
dnsreq->name, netreq->request_type, dnsreq->extensions);
if (pkt_size_needed > pkt_buf_len) {
pkt = GETDNS_XMALLOC(
dns_req->context->mf, uint8_t, pkt_size_needed);
dnsreq->context->mf, uint8_t, pkt_size_needed);
pkt_len = pkt_size_needed;
} else
pkt_len = pkt_buf_len;
if (getdns_make_query_pkt_buf(dns_req->context, dns_req->name,
netreq->request_type, dns_req->extensions, pkt_buf, &pkt_len))
if (getdns_make_query_pkt_buf(dnsreq->context, dnsreq->name,
netreq->request_type, dnsreq->extensions, pkt_buf, &pkt_len))
goto done;
netreq->query_id = ldns_get_random();
GLDNS_ID_SET(pkt, netreq->query_id);
if (pkt_len != sendto(netreq->udp_fd, pkt, pkt_len, 0,
if (pkt_len != sendto(netreq->fd, pkt, pkt_len, 0,
(struct sockaddr *)&netreq->upstream->addr,
netreq->upstream->addr_len)) {
close(netreq->udp_fd);
close(netreq->fd);
goto done;
}
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);
dnsreq->loop->vmt->schedule(
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
getdns_eventloop_event_init(&netreq->event, netreq,
stub_udp_read_cb, NULL, stub_timeout_cb));
done:
if (pkt_size_needed > pkt_buf_len)
GETDNS_FREE(dns_req->context->mf, pkt);
GETDNS_FREE(dnsreq->context->mf, pkt);
return;
}
static getdns_upstream *
pick_upstream(getdns_dns_req *dns_req)
pick_upstream(getdns_dns_req *dnsreq)
{
getdns_upstream *upstream;
size_t i;
if (!dns_req->upstreams->count)
if (!dnsreq->upstreams->count)
return NULL;
for (i = 0; i < dns_req->upstreams->count; i++)
if (dns_req->upstreams->upstreams[i].to_retry <= 0)
dns_req->upstreams->upstreams[i].to_retry++;
for (i = 0; i < dnsreq->upstreams->count; i++)
if (dnsreq->upstreams->upstreams[i].to_retry <= 0)
dnsreq->upstreams->upstreams[i].to_retry++;
i = dns_req->upstreams->current;
i = dnsreq->upstreams->current;
do {
if (dns_req->upstreams->upstreams[i].to_retry > 0) {
dns_req->upstreams->current = i;
return &dns_req->upstreams->upstreams[i];
if (dnsreq->upstreams->upstreams[i].to_retry > 0) {
dnsreq->upstreams->current = i;
return &dnsreq->upstreams->upstreams[i];
}
if (++i > dns_req->upstreams->count)
if (++i > dnsreq->upstreams->count)
i = 0;
} while (i != dns_req->upstreams->current);
} while (i != dnsreq->upstreams->current);
upstream = dns_req->upstreams->upstreams;
for (i = 1; i < dns_req->upstreams->count; i++)
if (dns_req->upstreams->upstreams[i].back_off <
upstream = dnsreq->upstreams->upstreams;
for (i = 1; i < dnsreq->upstreams->count; i++)
if (dnsreq->upstreams->upstreams[i].back_off <
upstream->back_off)
upstream = &dns_req->upstreams->upstreams[i];
upstream = &dnsreq->upstreams->upstreams[i];
upstream->back_off++;
upstream->to_retry = 1;
dns_req->upstreams->current = upstream - dns_req->upstreams->upstreams;
dnsreq->upstreams->current = upstream - dnsreq->upstreams->upstreams;
return upstream;
}
#define STUB_TCP_AGAIN -2
#define STUB_TCP_ERROR -1
static int
stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf)
{
size_t read;
uint8_t *buf;
size_t buf_size;
if (!tcp->read_buf) {
/* First time tcp read, create a buffer for reading */
if (!(tcp->read_buf = GETDNS_XMALLOC(*mf, uint8_t, 4096)))
return STUB_TCP_ERROR;
tcp->read_buf_len = 4096;
tcp->read_pos = tcp->read_buf;
tcp->to_read = 2; /* Packet size */
}
read = recv(fd, tcp->read_pos, tcp->to_read, 0);
if (read == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return STUB_TCP_AGAIN;
else
return STUB_TCP_ERROR;
}
tcp->to_read -= read;
tcp->read_pos += read;
if (tcp->to_read > 0)
return STUB_TCP_AGAIN;
read = tcp->read_pos - tcp->read_buf;
if (read == 2) {
/* Read the packet size short */
tcp->to_read = gldns_read_uint16(tcp->read_buf);
if (tcp->to_read < GLDNS_HEADER_SIZE)
return STUB_TCP_ERROR;
/* Resize our buffer if needed */
if (tcp->to_read > tcp->read_buf_len) {
buf_size = tcp->read_buf_len;
while (tcp->to_read > buf_size)
buf_size *= 2;
if (!(buf = GETDNS_XREALLOC(*mf,
tcp->read_buf, uint8_t, tcp->read_buf_len)))
return STUB_TCP_ERROR;
tcp->read_buf = buf;
tcp->read_buf_len = buf_size;
}
/* Ready to start reading the packet */
tcp->read_pos = tcp->read_buf;
return STUB_TCP_AGAIN;
}
return GLDNS_ID_WIRE(tcp->read_buf);
}
static void
stub_tcp_read_cb(void *userarg)
{
getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dnsreq = netreq->owner;
int q;
switch ((q = stub_tcp_read(netreq->fd, &netreq->tcp,
&dnsreq->context->mf))) {
case STUB_TCP_AGAIN:
return;
case STUB_TCP_ERROR:
stub_erred(netreq);
return;
default:
dnsreq->loop->vmt->clear(dnsreq->loop, &netreq->event);
if (q != netreq->query_id)
return;
netreq->state = NET_REQ_FINISHED;
ldns_wire2pkt(&(netreq->result), netreq->tcp.read_buf,
netreq->tcp.read_pos - netreq->tcp.read_buf);
dnsreq->upstreams->current = 0;
/* TODO: DNSSEC */
netreq->secure = 0;
netreq->bogus = 0;
stub_cleanup(netreq);
close(netreq->fd);
priv_getdns_check_dns_req_complete(dnsreq);
}
}
/* stub_tcp_write(fd, tcp, netreq)
* will return STUB_TCP_AGAIN when we need to come back again,
* STUB_TCP_ERROR on error and a query_id on successfull sent.
*/
static int
stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
{
getdns_dns_req *dnsreq = netreq->owner;
static size_t pkt_buf_len = 4096;
uint8_t pkt_buf[pkt_buf_len];
uint8_t *pkt = pkt_buf;
size_t pkt_len;
size_t query_pkt_size;
size_t written;
uint16_t query_id;
intptr_t query_id_intptr;
/* Do we have remaining data that we could write before? */
if (! tcp->write_buf) {
/* Initial write. Create packet and try to send */
query_pkt_size = getdns_get_query_pkt_size(dnsreq->context,
dnsreq->name, netreq->request_type, dnsreq->extensions);
if (query_pkt_size + 2 > pkt_buf_len) {
/* Not enough space in out stack buffer.
* Allocate a buffer on the heap.
*/
if (!(pkt = GETDNS_XMALLOC(dnsreq->context->mf,
uint8_t, query_pkt_size + 2)))
return STUB_TCP_ERROR;
tcp->write_buf = pkt;
tcp->write_buf_len = query_pkt_size + 2;
tcp->written = 0;
pkt_len = query_pkt_size;
} else
pkt_len = pkt_buf_len - 2;
/* Construct query packet */
if (getdns_make_query_pkt_buf(dnsreq->context, dnsreq->name,
netreq->request_type,dnsreq->extensions,pkt + 2,&pkt_len))
return STUB_TCP_ERROR;
/* Prepend length short */
gldns_write_uint16(pkt, pkt_len);
/* Not keeping connections open? Then the first random number
* will do as the query id.
*
* Otherwise find a unique query_id not already written (or in
* the write_queue) for that upstream. Register this netreq
* by query_id in the process.
*/
if (dnsreq->context->dns_transport !=
GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN)
query_id = ldns_get_random();
else do {
query_id = ldns_get_random();
query_id_intptr = (intptr_t)query_id;
netreq->node.key = (void *)query_id_intptr;
} while (!getdns_rbtree_insert(
&netreq->upstream->netreq_by_query_id, &netreq->node));
GLDNS_ID_SET(pkt + 2, query_id);
/* We have an initialized packet buffer.
* Lets see how much of it we can write
*/
written = write(fd, pkt, pkt_len + 2);
if ((written == -1 && (errno == EAGAIN ||
errno == EWOULDBLOCK)) ||
written < pkt_len + 2) {
/* We couldn't write the whole packet.
* We have to return with STUB_TCP_AGAIN, but if
* the packet was on the stack only, we have to copy
* it to heap space fist, because the stack will be
* gone after return.
*/
if (!tcp->write_buf) {
/* Copy stack packet buffer to heap */
if (!(tcp->write_buf = GETDNS_XMALLOC(
dnsreq->context->mf,uint8_t,pkt_len + 2)))
return STUB_TCP_ERROR;
(void) memcpy(pkt, pkt_buf, pkt_len + 2);
tcp->write_buf_len = pkt_len + 2;
}
/* Because written could be -1 (and errno EAGAIN) */
tcp->written = written >= 0 ? written : 0;
return STUB_TCP_AGAIN;
} else if (written == -1)
return STUB_TCP_ERROR;
/* We were able to write everything! Start reading. */
GETDNS_NULL_FREE(dnsreq->context->mf, tcp->write_buf);
} else {/* if (! tcp->write_buf) */
/* Coming back from an earlier unfinished write.
* Try to send remaining data */
written = write(fd, tcp->write_buf + tcp->written,
tcp->write_buf_len - tcp->written);
if (written == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return STUB_TCP_AGAIN;
else
return STUB_TCP_ERROR;
}
tcp->written += written;
if (tcp->written < tcp->write_buf_len)
/* Still more to send */
return STUB_TCP_AGAIN;
/* Done. Start reading */
query_id = GLDNS_ID_WIRE(tcp->write_buf + 2);
} /* if (! tcp->write_buf) */
GETDNS_NULL_FREE(dnsreq->context->mf, tcp->write_buf);
return (int) query_id;
}
static void
stub_tcp_write_cb(void *userarg)
{
getdns_network_req *netreq = (getdns_network_req *)userarg;
getdns_dns_req *dnsreq = netreq->owner;
int q;
switch ((q = stub_tcp_write(netreq->fd, &netreq->tcp, netreq))) {
case STUB_TCP_AGAIN:
return;
case STUB_TCP_ERROR:
stub_erred(netreq);
return;
default:
dnsreq->loop->vmt->clear(dnsreq->loop, &netreq->event);
netreq->query_id = (uint16_t) q;
dnsreq->loop->vmt->schedule(
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
getdns_eventloop_event_init(&netreq->event, netreq,
stub_tcp_read_cb, NULL, stub_timeout_cb));
return;
}
}
getdns_return_t
priv_getdns_submit_stub_request(getdns_network_req *netreq)
{
getdns_dns_req *dns_req = netreq->owner;
getdns_dns_req *dnsreq = netreq->owner;
getdns_upstream *upstream = pick_upstream(dnsreq);
getdns_upstream *upstream;
/* 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)
if (!upstream)
return GETDNS_RETURN_GENERIC_ERROR;
if (!(upstream = pick_upstream(dns_req)))
switch(dnsreq->context->dns_transport) {
case GETDNS_TRANSPORT_UDP_ONLY:
case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP:
if ((netreq->fd = socket(
upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return GETDNS_RETURN_GENERIC_ERROR;
getdns_sock_nonblock(netreq->fd);
netreq->upstream = upstream;
dnsreq->loop->vmt->schedule(
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
getdns_eventloop_event_init(&netreq->event, netreq,
NULL, stub_udp_write_cb, stub_timeout_cb));
return GETDNS_RETURN_GOOD;
case GETDNS_TRANSPORT_TCP_ONLY:
if ((netreq->fd = socket(
upstream->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
return GETDNS_RETURN_GENERIC_ERROR;
getdns_sock_nonblock(netreq->fd);
if (connect(netreq->fd, (struct sockaddr *)&upstream->addr,
upstream->addr_len) == -1 && errno != EINPROGRESS) {
close(netreq->fd);
return GETDNS_RETURN_GENERIC_ERROR;
}
netreq->upstream = upstream;
dnsreq->loop->vmt->schedule(
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
getdns_eventloop_event_init(&netreq->event, netreq,
NULL, stub_tcp_write_cb, stub_timeout_cb));
return GETDNS_RETURN_GOOD;
default:
return GETDNS_RETURN_GENERIC_ERROR;
if ((netreq->udp_fd = socket(
upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return GETDNS_RETURN_GENERIC_ERROR;
netreq->upstream = upstream;
getdns_sock_nonblock(netreq->udp_fd);
netreq->event.userarg = netreq;
netreq->event.read_cb = NULL;
netreq->event.write_cb = stub_resolve_write_cb;
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);
return GETDNS_RETURN_GOOD;
}
}
/* stub.c */

View File

@ -38,6 +38,7 @@
#include "types-internal.h"
getdns_return_t priv_getdns_submit_stub_request(getdns_network_req *netreq);
void priv_getdns_cancel_stub_request(getdns_network_req *netreq);
#endif

View File

@ -150,8 +150,23 @@ typedef struct getdns_extension_format
getdns_data_type exttype;
} getdns_extension_format;
/* State for async tcp stub resolving */
typedef struct getdns_tcp_state {
uint8_t *write_buf;
size_t write_buf_len;
size_t written;
uint8_t *read_buf;
size_t read_buf_len;
uint8_t *read_pos;
size_t to_read;
} getdns_tcp_state;
/**
* Request data for unbound
* Request data
**/
typedef struct getdns_network_req
{
@ -179,10 +194,14 @@ typedef struct getdns_network_req
struct getdns_network_req *next;
/* For stub resolving */
int udp_fd;
uint16_t query_id;
getdns_eventloop_event event;
struct getdns_upstream *upstream;
int fd;
getdns_eventloop_event event;
getdns_tcp_state tcp;
uint16_t query_id;
/* Network requests scheduled to write after me */
struct getdns_network_req *write_queue;
} getdns_network_req;
@ -253,6 +272,17 @@ typedef struct getdns_dns_req
: ((*(obj).mf.ext.free)((obj).mf_arg, (ptr))) \
)
#define GETDNS_NULL_FREE(obj, ptr) \
do { \
if (!(ptr)) \
break; \
if ((obj).mf_arg == MF_PLAIN) \
(*(obj).mf.pln.free)( (ptr)); \
else \
(*(obj).mf.ext.free)((obj).mf_arg, (ptr)); \
(ptr) = NULL; \
} while (0);
#define GETDNS_MALLOC(obj, type) GETDNS_XMALLOC(obj, type, 1)
#define GETDNS_REALLOC(obj, ptr, type) GETDNS_XREALLOC(obj, ptr, type, 1);
@ -284,9 +314,9 @@ void dns_req_free(getdns_dns_req * req);
strftime(buf, 10, "%T", &tm); \
fprintf(stderr, "[%s.%.6d] ", buf, (int)tv.tv_usec); \
fprintf(stderr, __VA_ARGS__); \
} while (false)
} while (0)
#define DEBUG_OFF(...) do {} while (false)
#define DEBUG_OFF(...) do {} while (0)
#if defined(SCHED_DEBUG) && SCHED_DEBUG
#include <time.h>