mirror of https://github.com/getdnsapi/getdns.git
stub tcp lookups
And the foundation for tcp keep connections open
This commit is contained in:
parent
c017e75f5a
commit
181d8cd3f4
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
459
src/stub.c
459
src/stub.c
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue