2014-09-09 10:19:52 -05:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* /brief function for stub resolving
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2013, NLnet Labs, Verisign, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the names of the copyright holders nor the
|
|
|
|
* names of its contributors may be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
2014-10-15 16:28:59 -05:00
|
|
|
#include <fcntl.h>
|
2014-09-16 08:43:20 -05:00
|
|
|
#include "stub.h"
|
2014-10-15 16:04:39 -05:00
|
|
|
#include "gldns/gbuffer.h"
|
2014-09-16 08:43:20 -05:00
|
|
|
#include "gldns/pkthdr.h"
|
2015-04-29 13:20:25 -05:00
|
|
|
#include "gldns/rrdef.h"
|
|
|
|
#include "gldns/str2wire.h"
|
|
|
|
#include "rr-iter.h"
|
2014-09-16 08:43:20 -05:00
|
|
|
#include "context.h"
|
2014-09-09 10:19:52 -05:00
|
|
|
#include <ldns/util.h>
|
2014-09-16 08:43:20 -05:00
|
|
|
#include "util-internal.h"
|
2014-10-15 16:04:39 -05:00
|
|
|
#include "general.h"
|
2014-09-09 10:19:52 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
#define STUB_TLS_SETUP_ERROR -4
|
|
|
|
#define STUB_TCP_AGAIN -3
|
|
|
|
#define STUB_TCP_ERROR -2
|
2015-04-27 09:32:57 -05:00
|
|
|
|
2015-03-22 10:50:48 -05:00
|
|
|
static time_t secret_rollover_time = 0;
|
|
|
|
static uint32_t secret = 0;
|
|
|
|
static uint32_t prev_secret = 0;
|
|
|
|
|
2015-04-19 11:16:58 -05:00
|
|
|
static void upstream_read_cb(void *userarg);
|
|
|
|
static void upstream_write_cb(void *userarg);
|
|
|
|
static void upstream_schedule_netreq(getdns_upstream *upstream,
|
|
|
|
getdns_network_req *netreq);
|
2015-04-27 09:32:57 -05:00
|
|
|
static void netreq_upstream_read_cb(void *userarg);
|
|
|
|
static void netreq_upstream_write_cb(void *userarg);
|
2015-04-29 13:20:25 -05:00
|
|
|
static int fallback_on_write(getdns_network_req *netreq);
|
2015-04-19 11:16:58 -05:00
|
|
|
|
2015-03-22 10:50:48 -05:00
|
|
|
static void
|
|
|
|
rollover_secret()
|
|
|
|
{
|
|
|
|
time_t now = 0;
|
|
|
|
|
|
|
|
/* Create and roll server secrets */
|
|
|
|
if (time(&now) <= secret_rollover_time)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Remember previous secret, in to keep answering on rollover
|
|
|
|
* boundry with old cookie.
|
|
|
|
*/
|
|
|
|
prev_secret = secret;
|
2015-03-22 11:01:37 -05:00
|
|
|
secret = arc4random();
|
2015-03-22 10:50:48 -05:00
|
|
|
|
|
|
|
/* Next rollover over EDNS_COOKIE_ROLLOVER_TIME with 30% jitter,
|
|
|
|
* I.e. some offset + or - 15% of the future point in time.
|
|
|
|
*/
|
|
|
|
secret_rollover_time = now + (EDNS_COOKIE_ROLLOVER_TIME / 20 * 17)
|
2015-03-22 11:01:37 -05:00
|
|
|
+ arc4random_uniform(EDNS_COOKIE_ROLLOVER_TIME / 10 * 3);
|
2015-03-22 10:50:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
calc_new_cookie(getdns_upstream *upstream, uint8_t *cookie)
|
|
|
|
{
|
|
|
|
const EVP_MD *md;
|
|
|
|
EVP_MD_CTX *mdctx;
|
|
|
|
unsigned char md_value[EVP_MAX_MD_SIZE];
|
|
|
|
unsigned int md_len;
|
|
|
|
size_t i;
|
|
|
|
sa_family_t af = upstream->addr.ss_family;
|
|
|
|
void *sa_addr = ((struct sockaddr*)&upstream->addr)->sa_data;
|
|
|
|
size_t addr_len = ( af == AF_INET6 ? sizeof(struct sockaddr_in6)
|
|
|
|
: af == AF_INET ? sizeof(struct sockaddr_in)
|
|
|
|
: 0 ) - sizeof(sa_family_t);
|
|
|
|
|
|
|
|
md = EVP_sha256();
|
|
|
|
mdctx = EVP_MD_CTX_create();
|
|
|
|
EVP_DigestInit_ex(mdctx, md, NULL);
|
|
|
|
EVP_DigestUpdate(mdctx, &secret, sizeof(secret));
|
|
|
|
EVP_DigestUpdate(mdctx, sa_addr, addr_len);
|
|
|
|
EVP_DigestFinal_ex(mdctx, md_value, &md_len);
|
|
|
|
EVP_MD_CTX_destroy(mdctx);
|
|
|
|
|
|
|
|
(void) memset(cookie, 0, 8);
|
|
|
|
for (i = 0; i < md_len; i++)
|
|
|
|
cookie[i % 8] ^= md_value[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t *
|
|
|
|
attach_edns_cookie(getdns_upstream *upstream, uint8_t *opt)
|
|
|
|
{
|
|
|
|
rollover_secret();
|
|
|
|
|
|
|
|
if (!upstream->has_client_cookie) {
|
|
|
|
calc_new_cookie(upstream, upstream->client_cookie);
|
|
|
|
upstream->secret = secret;
|
|
|
|
upstream->has_client_cookie = 1;
|
|
|
|
|
|
|
|
gldns_write_uint16(opt + 9, 12); /* rdata len */
|
|
|
|
gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE);
|
|
|
|
gldns_write_uint16(opt + 13, 8); /* opt len */
|
|
|
|
memcpy(opt + 15, upstream->client_cookie, 8);
|
|
|
|
return opt + 23;
|
|
|
|
|
|
|
|
} else if (upstream->secret != secret) {
|
|
|
|
memcpy( upstream->prev_client_cookie
|
|
|
|
, upstream->client_cookie, 8);
|
|
|
|
upstream->has_prev_client_cookie = 1;
|
|
|
|
calc_new_cookie(upstream, upstream->client_cookie);
|
|
|
|
upstream->secret = secret;
|
|
|
|
|
|
|
|
gldns_write_uint16(opt + 9, 12); /* rdata len */
|
|
|
|
gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE);
|
|
|
|
gldns_write_uint16(opt + 13, 8); /* opt len */
|
|
|
|
memcpy(opt + 15, upstream->client_cookie, 8);
|
|
|
|
return opt + 23;
|
|
|
|
|
|
|
|
} else if (!upstream->has_server_cookie) {
|
|
|
|
gldns_write_uint16(opt + 9, 12); /* rdata len */
|
|
|
|
gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE);
|
|
|
|
gldns_write_uint16(opt + 13, 8); /* opt len */
|
|
|
|
memcpy(opt + 15, upstream->client_cookie, 8);
|
|
|
|
return opt + 23;
|
|
|
|
} else {
|
|
|
|
gldns_write_uint16( opt + 9, 12 /* rdata len */
|
|
|
|
+ upstream->server_cookie_len);
|
|
|
|
gldns_write_uint16(opt + 11, EDNS_COOKIE_OPCODE);
|
|
|
|
gldns_write_uint16(opt + 13, 8 /* opt len */
|
|
|
|
+ upstream->server_cookie_len);
|
|
|
|
memcpy(opt + 15, upstream->client_cookie, 8);
|
|
|
|
memcpy(opt + 23, upstream->server_cookie
|
|
|
|
, upstream->server_cookie_len);
|
|
|
|
return opt + 23+ upstream->server_cookie_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
match_and_process_server_cookie(
|
|
|
|
getdns_upstream *upstream, uint8_t *response, size_t response_len)
|
|
|
|
{
|
|
|
|
priv_getdns_rr_iter rr_iter_storage, *rr_iter;
|
|
|
|
uint8_t *pos;
|
|
|
|
uint16_t rdata_len, opt_code, opt_len;
|
|
|
|
|
|
|
|
/* Search for the OPT RR (if any) */
|
|
|
|
for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage
|
|
|
|
, response, response_len)
|
|
|
|
; rr_iter
|
|
|
|
; rr_iter = priv_getdns_rr_iter_next(rr_iter)) {
|
|
|
|
|
|
|
|
if (priv_getdns_rr_iter_section(rr_iter) !=
|
|
|
|
GLDNS_SECTION_ADDITIONAL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (gldns_read_uint16(rr_iter->rr_type) != GETDNS_RRTYPE_OPT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (! rr_iter)
|
|
|
|
return 0; /* No OPT, no cookie */
|
|
|
|
|
|
|
|
pos = rr_iter->rr_type + 8;
|
|
|
|
|
|
|
|
/* OPT found, now search for the cookie option */
|
|
|
|
if (pos + 2 > rr_iter->nxt)
|
|
|
|
return 1; /* FORMERR */
|
|
|
|
|
|
|
|
rdata_len = gldns_read_uint16(pos); pos += 2;
|
|
|
|
if (pos + rdata_len > rr_iter->nxt)
|
|
|
|
return 1; /* FORMERR */
|
|
|
|
|
|
|
|
while (pos < rr_iter->nxt) {
|
|
|
|
opt_code = gldns_read_uint16(pos); pos += 2;
|
|
|
|
opt_len = gldns_read_uint16(pos); pos += 2;
|
|
|
|
if (pos + opt_len > rr_iter->nxt)
|
|
|
|
return 1; /* FORMERR */
|
|
|
|
if (opt_code == EDNS_COOKIE_OPCODE)
|
|
|
|
break;
|
|
|
|
pos += opt_len; /* Skip unknown options */
|
|
|
|
}
|
|
|
|
if (pos >= rr_iter->nxt || opt_code != EDNS_COOKIE_OPCODE)
|
|
|
|
return 0; /* Everything OK, just no cookie found. */
|
|
|
|
|
|
|
|
if (opt_len < 16 || opt_len > 40)
|
|
|
|
return 1; /* FORMERR */
|
|
|
|
|
|
|
|
if (!upstream->has_client_cookie)
|
|
|
|
return 1; /* Cookie reply, but we didn't sent one */
|
|
|
|
|
|
|
|
if (memcmp(upstream->client_cookie, pos, 8) != 0) {
|
|
|
|
if (!upstream->has_prev_client_cookie)
|
|
|
|
return 1; /* Cookie didn't match */
|
|
|
|
if (memcmp(upstream->prev_client_cookie, pos, 8) != 0)
|
|
|
|
return 1; /* Previous cookie didn't match either */
|
|
|
|
|
|
|
|
upstream->has_server_cookie = 0;
|
|
|
|
return 0; /* Don't store server cookie, because it
|
|
|
|
* is for our previous client cookie
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
pos += 8;
|
|
|
|
opt_len -= 8;
|
|
|
|
upstream->has_server_cookie = 1;
|
|
|
|
upstream->server_cookie_len = opt_len;
|
|
|
|
(void) memcpy(upstream->server_cookie, pos, opt_len);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-09-16 08:43:20 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
static int
|
|
|
|
create_starttls_request(getdns_dns_req *dnsreq, getdns_upstream *upstream,
|
|
|
|
getdns_eventloop *loop)
|
|
|
|
{
|
|
|
|
getdns_return_t r = GETDNS_RETURN_GOOD;
|
|
|
|
getdns_dict* extensions = getdns_dict_create_with_context(dnsreq->context);
|
|
|
|
if (!extensions) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
r = getdns_dict_set_int(extensions, "specify_class", GLDNS_RR_CLASS_CH);
|
|
|
|
if (r != GETDNS_RETURN_GOOD) {
|
|
|
|
getdns_dict_destroy(extensions);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
upstream->starttls_req = dns_req_new(dnsreq->context, loop,
|
|
|
|
"STARTTLS", GETDNS_RRTYPE_TXT, extensions);
|
|
|
|
/*TODO[STARTTLS]: TO BIT*/
|
|
|
|
if (upstream->starttls_req == NULL)
|
|
|
|
return 0;
|
|
|
|
getdns_dict_destroy(extensions);
|
|
|
|
|
|
|
|
upstream->starttls_req->netreqs[0]->upstream = upstream;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dname_equal(uint8_t *s1, uint8_t *s2)
|
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
for (;;) {
|
|
|
|
if (*s1 != *s2)
|
|
|
|
return 0;
|
|
|
|
else if (!*s1)
|
|
|
|
return 1;
|
|
|
|
for (i = *s1++, s2++; i > 0; i--, s1++, s2++)
|
|
|
|
if ((*s1 & 0xDF) != (*s2 & 0xDF))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_starttls_response(getdns_network_req *netreq) {
|
|
|
|
|
|
|
|
priv_getdns_rr_iter rr_iter_storage, *rr_iter;
|
|
|
|
priv_getdns_rdf_iter rdf_iter_storage, *rdf_iter;
|
|
|
|
uint16_t rr_type;
|
|
|
|
gldns_pkt_section section;
|
|
|
|
uint8_t starttls_name_space[256],
|
|
|
|
*starttls_name = starttls_name_space;
|
|
|
|
uint8_t owner_name_space[256], *owner_name;
|
|
|
|
size_t starttls_name_len = 256, owner_name_len;
|
|
|
|
|
|
|
|
/* Servers that are not STARTTLS aware will refuse the CH query*/
|
|
|
|
if (LDNS_RCODE_NOERROR !=
|
|
|
|
GLDNS_RCODE_WIRE(netreq->response)) {
|
|
|
|
fprintf(stderr, "[STARTTLS] STARTTLS response had error %d\n", GLDNS_RCODE_WIRE(netreq->response));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GLDNS_ANCOUNT(netreq->response) != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
(void) gldns_str2wire_dname_buf(
|
|
|
|
netreq->owner->name, starttls_name_space, &starttls_name_len);
|
|
|
|
|
|
|
|
for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage
|
|
|
|
, netreq->response
|
|
|
|
, netreq->response_len)
|
|
|
|
; rr_iter
|
|
|
|
; rr_iter = priv_getdns_rr_iter_next(rr_iter)) {
|
|
|
|
|
|
|
|
section = priv_getdns_rr_iter_section(rr_iter);
|
|
|
|
rr_type = gldns_read_uint16(rr_iter->rr_type);
|
|
|
|
if (section != GLDNS_SECTION_ANSWER || rr_type != GETDNS_RRTYPE_TXT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
owner_name = priv_getdns_owner_if_or_as_decompressed(
|
|
|
|
rr_iter, owner_name_space, &owner_name_len);
|
|
|
|
if (!dname_equal(starttls_name, owner_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!(rdf_iter = priv_getdns_rdf_iter_init(
|
|
|
|
&rdf_iter_storage, rr_iter)))
|
|
|
|
continue;
|
|
|
|
/* re-use the starttls_name for the response dname*/
|
|
|
|
starttls_name = priv_getdns_rdf_if_or_as_decompressed(
|
|
|
|
rdf_iter,starttls_name_space,&starttls_name_len);
|
|
|
|
if (dname_equal(starttls_name, owner_name)) {
|
|
|
|
fprintf(stderr, "[STARTTLS] STARTTLS response received :%s:\n", (char*)starttls_name);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "[STARTTLS] NO_TLS response received :%s:\n", (char*)starttls_name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-10-15 16:28:59 -05:00
|
|
|
/** best effort to set nonblocking */
|
|
|
|
static void
|
|
|
|
getdns_sock_nonblock(int sockfd)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_FCNTL
|
|
|
|
int flag;
|
|
|
|
if((flag = fcntl(sockfd, F_GETFL)) != -1) {
|
|
|
|
flag |= O_NONBLOCK;
|
|
|
|
if(fcntl(sockfd, F_SETFL, flag) == -1) {
|
|
|
|
/* ignore error, continue blockingly */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#elif defined(HAVE_IOCTLSOCKET)
|
|
|
|
unsigned long on = 1;
|
|
|
|
if(ioctlsocket(sockfd, FIONBIO, &on) != 0) {
|
|
|
|
/* ignore error, continue blockingly */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-10-15 16:04:39 -05:00
|
|
|
static void
|
2014-10-17 17:25:41 -05:00
|
|
|
stub_next_upstream(getdns_network_req *netreq)
|
2014-10-15 16:04:39 -05:00
|
|
|
{
|
2014-10-17 17:25:41 -05:00
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2014-10-16 07:24:13 -05:00
|
|
|
if (! --netreq->upstream->to_retry)
|
|
|
|
netreq->upstream->to_retry = -(netreq->upstream->back_off *= 2);
|
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
if (++dnsreq->upstreams->current > dnsreq->upstreams->count)
|
|
|
|
dnsreq->upstreams->current = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stub_cleanup(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
2014-10-18 07:32:55 -05:00
|
|
|
getdns_network_req *r, *prev_r;
|
|
|
|
getdns_upstream *upstream;
|
|
|
|
intptr_t query_id_intptr;
|
2014-10-19 15:51:42 -05:00
|
|
|
int reschedule;
|
2014-10-17 17:25:41 -05:00
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
2014-10-16 07:24:13 -05:00
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
GETDNS_NULL_FREE(dnsreq->context->mf, netreq->tcp.read_buf);
|
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
/* Nothing globally scheduled? Then nothing queued */
|
|
|
|
if (!(upstream = netreq->upstream)->event.ev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Delete from upstream->netreq_by_query_id (if present) */
|
|
|
|
query_id_intptr = (intptr_t)netreq->query_id;
|
|
|
|
(void) getdns_rbtree_delete(
|
|
|
|
&upstream->netreq_by_query_id, (void *)query_id_intptr);
|
|
|
|
|
|
|
|
/* Delete from upstream->write_queue (if present) */
|
|
|
|
for (prev_r = NULL, r = upstream->write_queue; r;
|
|
|
|
prev_r = r, r = r->write_queue_tail)
|
|
|
|
|
|
|
|
if (r == netreq) {
|
|
|
|
if (prev_r)
|
|
|
|
prev_r->write_queue_tail = r->write_queue_tail;
|
|
|
|
else
|
|
|
|
upstream->write_queue = r->write_queue_tail;
|
|
|
|
|
|
|
|
if (r == upstream->write_queue_last)
|
|
|
|
upstream->write_queue_last =
|
|
|
|
prev_r ? prev_r : NULL;
|
|
|
|
break;
|
|
|
|
}
|
2014-10-19 15:51:42 -05:00
|
|
|
reschedule = 0;
|
2014-10-18 07:32:55 -05:00
|
|
|
if (!upstream->write_queue && upstream->event.write_cb) {
|
|
|
|
upstream->event.write_cb = NULL;
|
2014-10-19 15:51:42 -05:00
|
|
|
reschedule = 1;
|
2014-10-18 07:32:55 -05:00
|
|
|
}
|
|
|
|
if (!upstream->netreq_by_query_id.count && upstream->event.read_cb) {
|
|
|
|
upstream->event.read_cb = NULL;
|
2014-10-19 15:51:42 -05:00
|
|
|
reschedule = 1;
|
2014-10-18 07:32:55 -05:00
|
|
|
}
|
2014-10-19 15:51:42 -05:00
|
|
|
if (reschedule) {
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
if (upstream->event.read_cb || upstream->event.write_cb)
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-19 01:08:45 -05:00
|
|
|
upstream_erred(getdns_upstream *upstream)
|
2014-10-18 07:32:55 -05:00
|
|
|
{
|
2014-10-19 01:08:45 -05:00
|
|
|
getdns_network_req *netreq;
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: ERROR(upstream_erred)\n");
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2014-10-19 01:08:45 -05:00
|
|
|
while ((netreq = upstream->write_queue)) {
|
|
|
|
stub_cleanup(netreq);
|
2014-10-19 15:51:42 -05:00
|
|
|
netreq->state = NET_REQ_FINISHED;
|
2014-10-19 01:08:45 -05:00
|
|
|
priv_getdns_check_dns_req_complete(netreq->owner);
|
|
|
|
}
|
|
|
|
while (upstream->netreq_by_query_id.count) {
|
|
|
|
netreq = (getdns_network_req *)
|
|
|
|
getdns_rbtree_first(&upstream->netreq_by_query_id);
|
|
|
|
stub_cleanup(netreq);
|
2014-10-19 15:51:42 -05:00
|
|
|
netreq->state = NET_REQ_FINISHED;
|
2014-10-19 01:08:45 -05:00
|
|
|
priv_getdns_check_dns_req_complete(netreq->owner);
|
|
|
|
}
|
2014-12-07 13:03:34 -06:00
|
|
|
if (upstream->tls_obj) {
|
|
|
|
SSL_shutdown(upstream->tls_obj);
|
|
|
|
SSL_free(upstream->tls_obj);
|
|
|
|
upstream->tls_obj = NULL;
|
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
close(upstream->fd);
|
|
|
|
upstream->fd = -1;
|
2015-04-19 11:16:58 -05:00
|
|
|
/*TODO[TLS]: Upstream errors don't trigger the user callback....*/
|
2014-10-17 17:25:41 -05:00
|
|
|
}
|
2015-04-29 13:20:25 -05:00
|
|
|
static void
|
|
|
|
message_erred(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
stub_cleanup(netreq);
|
|
|
|
netreq->state = NET_REQ_FINISHED;
|
|
|
|
priv_getdns_check_dns_req_complete(netreq->owner);
|
|
|
|
}
|
2014-10-17 17:25:41 -05:00
|
|
|
|
|
|
|
void
|
|
|
|
priv_getdns_cancel_stub_request(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
stub_cleanup(netreq);
|
|
|
|
if (netreq->fd >= 0) close(netreq->fd);
|
2014-10-15 16:04:39 -05:00
|
|
|
}
|
2014-09-16 08:43:20 -05:00
|
|
|
|
2014-10-15 16:04:39 -05:00
|
|
|
static void
|
2014-10-17 17:25:41 -05:00
|
|
|
stub_erred(getdns_network_req *netreq)
|
|
|
|
{
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: ERROR(stub_erred)\n");
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
stub_next_upstream(netreq);
|
|
|
|
stub_cleanup(netreq);
|
2015-04-19 11:16:58 -05:00
|
|
|
/* TODO[TLS]: When we get an error (which is probably a timeout) and are
|
|
|
|
* using to keep connections open should we leave the connection up here? */
|
2014-10-17 17:25:41 -05:00
|
|
|
if (netreq->fd >= 0) close(netreq->fd);
|
2014-10-19 15:51:42 -05:00
|
|
|
netreq->state = NET_REQ_FINISHED;
|
2014-10-17 17:25:41 -05:00
|
|
|
priv_getdns_check_dns_req_complete(netreq->owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stub_timeout_cb(void *userarg)
|
2014-09-16 08:43:20 -05:00
|
|
|
{
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: TIMEOUT(stub_timeout_cb)\n");
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2014-10-15 16:04:39 -05:00
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
2014-10-17 17:25:41 -05:00
|
|
|
|
|
|
|
stub_next_upstream(netreq);
|
|
|
|
stub_cleanup(netreq);
|
|
|
|
if (netreq->fd >= 0) close(netreq->fd);
|
|
|
|
(void) getdns_context_request_timed_out(netreq->owner);
|
|
|
|
}
|
|
|
|
|
2014-10-20 17:17:57 -05:00
|
|
|
static void stub_tcp_write_cb(void *userarg);
|
2014-10-17 17:25:41 -05:00
|
|
|
static void
|
|
|
|
stub_udp_read_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
2014-10-20 17:17:57 -05:00
|
|
|
getdns_upstream *upstream = netreq->upstream;
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2014-10-24 16:12:28 -05:00
|
|
|
ssize_t read;
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2015-02-03 04:12:05 -06:00
|
|
|
read = recvfrom(netreq->fd, netreq->response,
|
|
|
|
netreq->max_udp_payload_size + 1, /* If read == max_udp_payload_size
|
|
|
|
* then all is good. If read ==
|
|
|
|
* max_udp_payload_size + 1, then
|
|
|
|
* we receive more then requested!
|
|
|
|
* i.e. overflow
|
|
|
|
*/
|
|
|
|
0, NULL, NULL);
|
2014-10-15 16:32:33 -05:00
|
|
|
if (read == -1 && (errno = EAGAIN || errno == EWOULDBLOCK))
|
2015-02-03 04:12:05 -06:00
|
|
|
return;
|
2014-10-15 16:32:33 -05:00
|
|
|
|
2014-10-15 16:04:39 -05:00
|
|
|
if (read < GLDNS_HEADER_SIZE)
|
2015-02-03 04:12:05 -06:00
|
|
|
return; /* Not DNS */
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2015-02-03 04:12:05 -06:00
|
|
|
if (GLDNS_ID_WIRE(netreq->response) != netreq->query_id)
|
|
|
|
return; /* Cache poisoning attempt ;) */
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2015-03-22 10:50:48 -05:00
|
|
|
if (netreq->owner->edns_cookies && match_and_process_server_cookie(
|
|
|
|
upstream, netreq->response, read))
|
|
|
|
return; /* Client cookie didn't match? */
|
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
close(netreq->fd);
|
2015-04-24 10:29:08 -05:00
|
|
|
/*TODO[TLS]: Switch this to use the transport fallback list*/
|
2015-02-03 04:12:05 -06:00
|
|
|
if (GLDNS_TC_WIRE(netreq->response) &&
|
2014-10-20 17:17:57 -05:00
|
|
|
dnsreq->context->dns_transport ==
|
|
|
|
GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP) {
|
|
|
|
|
|
|
|
if ((netreq->fd = socket(
|
|
|
|
upstream->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
getdns_sock_nonblock(netreq->fd);
|
|
|
|
if (connect(netreq->fd, (struct sockaddr *)&upstream->addr,
|
|
|
|
upstream->addr_len) == -1 && errno != EINPROGRESS) {
|
|
|
|
|
|
|
|
close(netreq->fd);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
|
|
|
NULL, stub_tcp_write_cb, stub_timeout_cb));
|
|
|
|
|
2015-02-03 04:12:05 -06:00
|
|
|
return;
|
2014-10-20 17:17:57 -05:00
|
|
|
}
|
2015-02-11 07:55:22 -06:00
|
|
|
netreq->response_len = read;
|
2014-10-17 17:25:41 -05:00
|
|
|
dnsreq->upstreams->current = 0;
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
/* TODO: DNSSEC */
|
2014-10-15 16:04:39 -05:00
|
|
|
netreq->secure = 0;
|
|
|
|
netreq->bogus = 0;
|
2014-10-20 17:17:57 -05:00
|
|
|
done:
|
|
|
|
netreq->state = NET_REQ_FINISHED;
|
2015-02-03 04:12:05 -06:00
|
|
|
priv_getdns_check_dns_req_complete(dnsreq);
|
2014-10-15 16:04:39 -05:00
|
|
|
}
|
|
|
|
|
2014-10-15 16:57:24 -05:00
|
|
|
static void
|
2014-10-17 17:25:41 -05:00
|
|
|
stub_udp_write_cb(void *userarg)
|
2014-10-15 16:04:39 -05:00
|
|
|
{
|
2014-10-15 16:57:24 -05:00
|
|
|
getdns_network_req *netreq = (getdns_network_req *)userarg;
|
2015-02-03 03:46:44 -06:00
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
size_t pkt_len = netreq->response - netreq->query;
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
2014-10-15 16:04:39 -05:00
|
|
|
|
2015-03-21 04:41:25 -05:00
|
|
|
netreq->query_id = arc4random();
|
2015-02-03 03:46:44 -06:00
|
|
|
GLDNS_ID_SET(netreq->query, netreq->query_id);
|
2015-03-22 10:50:48 -05:00
|
|
|
if (netreq->opt) {
|
|
|
|
if (netreq->edns_maximum_udp_payload_size == -1)
|
|
|
|
gldns_write_uint16(netreq->opt + 3,
|
|
|
|
( netreq->max_udp_payload_size =
|
|
|
|
netreq->upstream->addr.ss_family == AF_INET6
|
|
|
|
? 1232 : 1432));
|
|
|
|
if (netreq->owner->edns_cookies) {
|
|
|
|
netreq->response = attach_edns_cookie(
|
|
|
|
netreq->upstream, netreq->opt);
|
|
|
|
pkt_len = netreq->response - netreq->query;
|
|
|
|
}
|
|
|
|
}
|
2015-02-03 03:46:44 -06:00
|
|
|
|
|
|
|
if ((ssize_t)pkt_len != sendto(netreq->fd, netreq->query, pkt_len, 0,
|
2014-10-15 16:57:24 -05:00
|
|
|
(struct sockaddr *)&netreq->upstream->addr,
|
|
|
|
netreq->upstream->addr_len)) {
|
2014-10-17 17:25:41 -05:00
|
|
|
close(netreq->fd);
|
2015-02-03 03:46:44 -06:00
|
|
|
return;
|
2014-10-15 16:57:24 -05:00
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(
|
2014-10-17 17:25:41 -05:00
|
|
|
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
|
|
|
stub_udp_read_cb, NULL, stub_timeout_cb));
|
2014-10-15 16:57:24 -05:00
|
|
|
}
|
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
/* TODO[TLS]: Optimise to re-use TCP (or failed STARTTLS) where possible.*/
|
2015-04-19 11:16:58 -05:00
|
|
|
static int
|
2015-04-24 10:29:08 -05:00
|
|
|
transport_valid(struct getdns_upstream *upstream, getdns_base_transport_t transport) {
|
2015-04-29 13:20:25 -05:00
|
|
|
if (transport == GETDNS_BASE_TRANSPORT_UDP ||
|
|
|
|
transport == GETDNS_BASE_TRANSPORT_TCP_SINGLE)
|
|
|
|
return 1;
|
2015-04-24 10:29:08 -05:00
|
|
|
if (upstream->dns_base_transport != transport)
|
2015-04-19 11:16:58 -05:00
|
|
|
return 0;
|
2015-04-29 13:20:25 -05:00
|
|
|
if ((transport == GETDNS_BASE_TRANSPORT_TLS ||
|
|
|
|
transport == GETDNS_BASE_TRANSPORT_STARTTLS)
|
|
|
|
&& upstream->tls_hs_state == GETDNS_HS_FAILED)
|
2015-04-19 11:16:58 -05:00
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-10-16 07:24:13 -05:00
|
|
|
static getdns_upstream *
|
2015-04-24 10:29:08 -05:00
|
|
|
pick_upstream(getdns_network_req *netreq, getdns_base_transport_t transport)
|
2014-10-16 07:24:13 -05:00
|
|
|
{
|
|
|
|
getdns_upstream *upstream;
|
2015-04-24 10:29:08 -05:00
|
|
|
getdns_upstreams *upstreams = netreq->owner->context->upstreams;
|
2014-10-16 07:24:13 -05:00
|
|
|
size_t i;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
if (!upstreams->count)
|
2014-10-16 07:24:13 -05:00
|
|
|
return NULL;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
for (i = 0; i < upstreams->count; i++)
|
|
|
|
if (upstreams->upstreams[i].to_retry <= 0)
|
|
|
|
upstreams->upstreams[i].to_retry++;
|
2015-04-19 11:16:58 -05:00
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
i = upstreams->current;
|
2014-10-16 07:24:13 -05:00
|
|
|
do {
|
2015-04-24 10:29:08 -05:00
|
|
|
if (upstreams->upstreams[i].to_retry > 0 &&
|
|
|
|
transport_valid(&upstreams->upstreams[i], transport)) {
|
|
|
|
upstreams->current = i;
|
|
|
|
return &upstreams->upstreams[i];
|
2014-10-16 07:24:13 -05:00
|
|
|
}
|
2015-04-24 10:29:08 -05:00
|
|
|
if (++i > upstreams->count)
|
2014-10-16 07:24:13 -05:00
|
|
|
i = 0;
|
2015-04-24 10:29:08 -05:00
|
|
|
} while (i != upstreams->current);
|
2014-10-16 07:24:13 -05:00
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
upstream = upstreams->upstreams;
|
|
|
|
for (i = 1; i < upstreams->count; i++)
|
|
|
|
if (upstreams->upstreams[i].back_off < upstream->back_off &&
|
|
|
|
transport_valid(&upstreams->upstreams[i], transport))
|
|
|
|
upstream = &upstreams->upstreams[i];
|
2014-10-16 07:24:13 -05:00
|
|
|
|
|
|
|
upstream->back_off++;
|
|
|
|
upstream->to_retry = 1;
|
2015-04-24 10:29:08 -05:00
|
|
|
upstreams->current = upstream - upstreams->upstreams;
|
2014-10-16 07:24:13 -05:00
|
|
|
return upstream;
|
|
|
|
}
|
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
static int
|
|
|
|
stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf)
|
|
|
|
{
|
2014-10-24 16:12:28 -05:00
|
|
|
ssize_t read;
|
2014-10-17 17:25:41 -05:00
|
|
|
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;
|
2014-10-18 07:32:55 -05:00
|
|
|
} else if (read == 0) {
|
|
|
|
/* Remote end closed the socket */
|
|
|
|
/* TODO: Try to reconnect */
|
|
|
|
return STUB_TCP_ERROR;
|
2014-10-17 17:25:41 -05:00
|
|
|
}
|
|
|
|
tcp->to_read -= read;
|
|
|
|
tcp->read_pos += read;
|
|
|
|
|
2014-12-07 13:03:34 -06:00
|
|
|
if ((int)tcp->to_read > 0)
|
2014-10-17 17:25:41 -05:00
|
|
|
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,
|
2015-02-12 05:05:10 -06:00
|
|
|
tcp->read_buf, uint8_t, buf_size)))
|
2014-10-17 17:25:41 -05:00
|
|
|
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:
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
2014-10-17 17:25:41 -05:00
|
|
|
if (q != netreq->query_id)
|
|
|
|
return;
|
2015-03-22 10:50:48 -05:00
|
|
|
if (netreq->owner->edns_cookies &&
|
|
|
|
match_and_process_server_cookie(
|
|
|
|
netreq->upstream, netreq->tcp.read_buf,
|
|
|
|
netreq->tcp.read_pos - netreq->tcp.read_buf))
|
|
|
|
return; /* Client cookie didn't match? */
|
2014-10-17 17:25:41 -05:00
|
|
|
netreq->state = NET_REQ_FINISHED;
|
2015-02-03 04:12:05 -06:00
|
|
|
netreq->response = netreq->tcp.read_buf;
|
2015-02-11 07:55:22 -06:00
|
|
|
netreq->response_len =
|
2015-02-03 04:12:05 -06:00
|
|
|
netreq->tcp.read_pos - netreq->tcp.read_buf;
|
|
|
|
netreq->tcp.read_buf = NULL;
|
2014-10-17 17:25:41 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-17 09:50:08 -05:00
|
|
|
static SSL*
|
2015-04-19 11:16:58 -05:00
|
|
|
create_tls_object(getdns_context *context, int fd)
|
2014-12-07 13:03:34 -06:00
|
|
|
{
|
|
|
|
/* Create SSL instance */
|
2015-04-19 11:16:58 -05:00
|
|
|
if (context->tls_ctx == NULL)
|
2015-04-17 12:38:13 -05:00
|
|
|
return NULL;
|
2015-04-19 11:16:58 -05:00
|
|
|
SSL* ssl = SSL_new(context->tls_ctx);
|
2014-12-07 13:03:34 -06:00
|
|
|
if(!ssl) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Connect the SSL object with a file descriptor */
|
2015-04-19 11:16:58 -05:00
|
|
|
if(!SSL_set_fd(ssl,fd)) {
|
2014-12-07 13:03:34 -06:00
|
|
|
SSL_free(ssl);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
SSL_set_connect_state(ssl);
|
|
|
|
(void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
2015-04-19 11:16:58 -05:00
|
|
|
return ssl;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_tls_handshake(getdns_upstream *upstream)
|
|
|
|
{
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: TLS(do_tls_handshake)\n");
|
2014-12-07 13:03:34 -06:00
|
|
|
|
|
|
|
int r;
|
|
|
|
int want;
|
2015-04-19 11:16:58 -05:00
|
|
|
ERR_clear_error();
|
|
|
|
while ((r = SSL_do_handshake(upstream->tls_obj)) != 1)
|
2014-12-07 13:03:34 -06:00
|
|
|
{
|
2015-04-19 11:16:58 -05:00
|
|
|
want = SSL_get_error(upstream->tls_obj, r);
|
2014-12-07 13:03:34 -06:00
|
|
|
switch (want) {
|
|
|
|
case SSL_ERROR_WANT_READ:
|
2015-04-19 11:16:58 -05:00
|
|
|
fprintf(stderr,"[TLS]: SSL_ERROR_WANT_READ\n");
|
|
|
|
upstream->event.read_cb = upstream_read_cb;
|
|
|
|
upstream->event.write_cb = NULL;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_READ;
|
|
|
|
return STUB_TCP_AGAIN;
|
2014-12-07 13:03:34 -06:00
|
|
|
case SSL_ERROR_WANT_WRITE:
|
2015-04-19 11:16:58 -05:00
|
|
|
fprintf(stderr,"[TLS]: SSL_ERROR_WANT_WRITE\n");
|
|
|
|
upstream->event.read_cb = NULL;
|
|
|
|
upstream->event.write_cb = upstream_write_cb;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_WRITE;
|
|
|
|
return STUB_TCP_AGAIN;
|
2014-12-07 13:03:34 -06:00
|
|
|
default:
|
2015-04-19 11:16:58 -05:00
|
|
|
SSL_free(upstream->tls_obj);
|
|
|
|
upstream->tls_obj = NULL;
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_FAILED;
|
|
|
|
return STUB_TLS_SETUP_ERROR;
|
2014-12-07 13:03:34 -06:00
|
|
|
}
|
|
|
|
}
|
2015-04-19 11:16:58 -05:00
|
|
|
upstream->tls_hs_state = GETDNS_HS_DONE;
|
|
|
|
upstream->event.read_cb = NULL;
|
|
|
|
upstream->event.write_cb = upstream_write_cb;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
return 0;
|
2014-12-07 13:03:34 -06:00
|
|
|
}
|
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
static int
|
2015-04-29 13:20:25 -05:00
|
|
|
tls_handshake_active(getdns_tls_hs_state_t hs_state)
|
2015-04-27 09:32:57 -05:00
|
|
|
{
|
2015-04-29 13:20:25 -05:00
|
|
|
return (hs_state == GETDNS_HS_FAILED ||
|
|
|
|
hs_state == GETDNS_HS_NONE) ? 0 : 1;
|
2015-04-19 11:16:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-04-24 10:29:08 -05:00
|
|
|
check_tls(getdns_upstream* upstream)
|
2015-04-19 11:16:58 -05:00
|
|
|
{
|
|
|
|
/* Already have a connection*/
|
|
|
|
if (upstream->tls_hs_state == GETDNS_HS_DONE &&
|
|
|
|
(upstream->tls_obj != NULL) && (upstream->fd != -1))
|
|
|
|
return 0;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
/* This upstream can't be used, so let the fallback code take care of things */
|
|
|
|
if (upstream->tls_hs_state == GETDNS_HS_FAILED)
|
|
|
|
return STUB_TLS_SETUP_ERROR;
|
|
|
|
|
2015-04-19 11:16:58 -05:00
|
|
|
/* Lets make sure the connection is up before we try a handshake*/
|
|
|
|
int error = 0;
|
|
|
|
socklen_t len = (socklen_t)sizeof(error);
|
|
|
|
/* TODO: This doesn't handle the case where the far end doesn't do a reset
|
|
|
|
* as is the case with e.g. 8.8.8.8. For that case the timeout kicks in
|
2015-04-27 09:32:57 -05:00
|
|
|
* and the user callback fails the message without the chance to fallback.*/
|
2015-04-19 11:16:58 -05:00
|
|
|
getsockopt(upstream->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &len);
|
2015-04-27 09:32:57 -05:00
|
|
|
if (error == EINPROGRESS || error == EWOULDBLOCK)
|
2015-04-19 11:16:58 -05:00
|
|
|
return STUB_TCP_AGAIN; /* try again */
|
|
|
|
else if (error != 0) {
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: TLS(check_tls): died gettting connection\n");
|
2015-04-19 11:16:58 -05:00
|
|
|
SSL_free(upstream->tls_obj);
|
|
|
|
upstream->tls_obj = NULL;
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_FAILED;
|
|
|
|
return STUB_TLS_SETUP_ERROR;
|
|
|
|
}
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
return do_tls_handshake(upstream);
|
2015-04-19 11:16:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
stub_tls_read(getdns_upstream *upstream, getdns_tcp_state *tcp, struct mem_funcs *mf)
|
2014-12-07 13:03:34 -06:00
|
|
|
{
|
|
|
|
ssize_t read;
|
|
|
|
uint8_t *buf;
|
|
|
|
size_t buf_size;
|
2015-04-19 11:16:58 -05:00
|
|
|
SSL* tls_obj = upstream->tls_obj;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
int q = check_tls(upstream);
|
2015-04-19 11:16:58 -05:00
|
|
|
if (q != 0)
|
|
|
|
return q;
|
2014-12-07 13:03:34 -06:00
|
|
|
|
|
|
|
if (!tcp->read_buf) {
|
|
|
|
/* First time tls 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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
ERR_clear_error();
|
|
|
|
read = SSL_read(tls_obj, tcp->read_pos, tcp->to_read);
|
|
|
|
if (read <= 0) {
|
|
|
|
/* TODO[TLS]: Handle SSL_ERROR_WANT_WRITE which means handshake
|
|
|
|
renegotiation. Need to keep handshake state to do that.*/
|
|
|
|
int want = SSL_get_error(tls_obj, read);
|
|
|
|
if (want == SSL_ERROR_WANT_READ) {
|
|
|
|
return STUB_TCP_AGAIN; /* read more later */
|
|
|
|
} else
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
}
|
|
|
|
tcp->to_read -= read;
|
|
|
|
tcp->read_pos += read;
|
|
|
|
|
|
|
|
if ((int)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, buf_size)))
|
|
|
|
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;
|
|
|
|
read = SSL_read(tls_obj, tcp->read_pos, tcp->to_read);
|
|
|
|
if (read <= 0) {
|
|
|
|
/* TODO[TLS]: Handle SSL_ERROR_WANT_WRITE which means handshake
|
|
|
|
renegotiation. Need to keep handshake state to do that.*/
|
|
|
|
int want = SSL_get_error(tls_obj, read);
|
|
|
|
if (want == SSL_ERROR_WANT_READ) {
|
|
|
|
return STUB_TCP_AGAIN; /* read more later */
|
|
|
|
} else
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
}
|
|
|
|
tcp->to_read -= read;
|
|
|
|
tcp->read_pos += read;
|
|
|
|
if ((int)tcp->to_read > 0)
|
|
|
|
return STUB_TCP_AGAIN;
|
|
|
|
}
|
|
|
|
return GLDNS_ID_WIRE(tcp->read_buf);
|
|
|
|
}
|
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
static void
|
|
|
|
upstream_read_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_upstream *upstream = (getdns_upstream *)userarg;
|
|
|
|
getdns_network_req *netreq;
|
2014-10-20 16:51:05 -05:00
|
|
|
getdns_dns_req *dnsreq;
|
2014-10-18 07:32:55 -05:00
|
|
|
int q;
|
|
|
|
uint16_t query_id;
|
|
|
|
intptr_t query_id_intptr;
|
2015-04-19 11:16:58 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: **********CALLBACK***********\n");
|
|
|
|
fprintf(stderr,"[TLS]: READ(upstream_read_cb): on %d\n", upstream->fd);
|
2014-10-18 07:32:55 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
if (upstream->dns_base_transport == GETDNS_BASE_TRANSPORT_TLS ||
|
|
|
|
(upstream->dns_base_transport == GETDNS_BASE_TRANSPORT_STARTTLS &&
|
|
|
|
tls_handshake_active(upstream->tls_hs_state)))
|
2015-04-19 11:16:58 -05:00
|
|
|
q = stub_tls_read(upstream, &upstream->tcp,
|
2014-12-07 13:03:34 -06:00
|
|
|
&upstream->upstreams->mf);
|
|
|
|
else
|
|
|
|
q = stub_tcp_read(upstream->fd, &upstream->tcp,
|
|
|
|
&upstream->upstreams->mf);
|
|
|
|
|
|
|
|
switch (q) {
|
2014-10-18 07:32:55 -05:00
|
|
|
case STUB_TCP_AGAIN:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case STUB_TCP_ERROR:
|
2014-10-19 01:08:45 -05:00
|
|
|
upstream_erred(upstream);
|
2014-10-18 07:32:55 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
2014-12-07 13:03:34 -06:00
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
/* Lookup netreq */
|
|
|
|
query_id = (uint16_t) q;
|
|
|
|
query_id_intptr = (intptr_t) query_id;
|
|
|
|
netreq = (getdns_network_req *)getdns_rbtree_delete(
|
|
|
|
&upstream->netreq_by_query_id, (void *)query_id_intptr);
|
2015-02-03 04:12:05 -06:00
|
|
|
if (! netreq) /* maybe canceled */ {
|
|
|
|
/* reset read buffer */
|
|
|
|
upstream->tcp.read_pos = upstream->tcp.read_buf;
|
|
|
|
upstream->tcp.to_read = 2;
|
|
|
|
return;
|
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
|
|
|
|
netreq->state = NET_REQ_FINISHED;
|
2015-02-03 04:12:05 -06:00
|
|
|
netreq->response = upstream->tcp.read_buf;
|
2015-02-11 07:55:22 -06:00
|
|
|
netreq->response_len =
|
2015-02-03 04:12:05 -06:00
|
|
|
upstream->tcp.read_pos - upstream->tcp.read_buf;
|
|
|
|
upstream->tcp.read_buf = NULL;
|
2014-10-18 07:32:55 -05:00
|
|
|
upstream->upstreams->current = 0;
|
|
|
|
|
|
|
|
/* TODO: DNSSEC */
|
|
|
|
netreq->secure = 0;
|
|
|
|
netreq->bogus = 0;
|
|
|
|
|
|
|
|
stub_cleanup(netreq);
|
2014-10-20 16:51:05 -05:00
|
|
|
|
2014-10-24 16:12:28 -05:00
|
|
|
/* More to read/write for syncronous lookups? */
|
2014-10-20 16:51:05 -05:00
|
|
|
if (netreq->event.read_cb) {
|
|
|
|
dnsreq = netreq->owner;
|
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
|
|
|
if (upstream->netreq_by_query_id.count ||
|
|
|
|
upstream->write_queue)
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
dnsreq->loop, upstream->fd,
|
|
|
|
dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(
|
|
|
|
&netreq->event, netreq,
|
|
|
|
( upstream->netreq_by_query_id.count ?
|
|
|
|
netreq_upstream_read_cb : NULL ),
|
|
|
|
( upstream->write_queue ?
|
|
|
|
netreq_upstream_write_cb : NULL),
|
|
|
|
stub_timeout_cb));
|
|
|
|
}
|
2015-04-29 13:20:25 -05:00
|
|
|
|
|
|
|
if (netreq->owner == upstream->starttls_req) {
|
|
|
|
dnsreq = netreq->owner;
|
|
|
|
fprintf(stderr, "[STARTTLS] processing STARTTLS response!\n");
|
|
|
|
if (is_starttls_response(netreq)) {
|
|
|
|
upstream->tls_obj = create_tls_object(dnsreq->context, upstream->fd);
|
|
|
|
if (upstream->tls_obj == NULL) {
|
|
|
|
fprintf(stderr,"[TLS]: could not create tls object\n");
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_FAILED;
|
|
|
|
}
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_WRITE;
|
|
|
|
} else
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_FAILED;
|
|
|
|
dns_req_free(upstream->starttls_req);
|
|
|
|
upstream->starttls_req = NULL;
|
|
|
|
|
|
|
|
// Now reschedule the writes on this connection
|
|
|
|
upstream->event.write_cb = upstream_write_cb;
|
|
|
|
fprintf(stderr, "[STARTTLS] method: upstream_schedule_netreq -> re-instating writes\n");
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "[STARTTLS] processing standard response....\n");
|
|
|
|
priv_getdns_check_dns_req_complete(netreq->owner);
|
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
|
2014-10-24 16:12:28 -05:00
|
|
|
/* Nothing more to read? Then deschedule the reads.*/
|
2014-10-20 16:51:05 -05:00
|
|
|
if (! upstream->netreq_by_query_id.count) {
|
|
|
|
upstream->event.read_cb = NULL;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
if (upstream->event.write_cb)
|
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER,
|
|
|
|
&upstream->event);
|
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
netreq_upstream_read_cb(void *userarg)
|
|
|
|
{
|
|
|
|
upstream_read_cb(((getdns_network_req *)userarg)->upstream);
|
|
|
|
}
|
|
|
|
|
2014-10-17 17:25:41 -05:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
|
2015-02-03 03:46:44 -06:00
|
|
|
size_t pkt_len = netreq->response - netreq->query;
|
2014-10-24 16:12:28 -05:00
|
|
|
ssize_t written;
|
2014-10-17 17:25:41 -05:00
|
|
|
uint16_t query_id;
|
|
|
|
intptr_t query_id_intptr;
|
|
|
|
|
2014-10-24 16:12:28 -05:00
|
|
|
/* Do we have remaining data that we could not write before? */
|
2014-10-17 17:25:41 -05:00
|
|
|
if (! tcp->write_buf) {
|
2015-02-03 03:46:44 -06:00
|
|
|
/* No, this is an initial write. Try to send
|
2014-10-24 16:12:28 -05:00
|
|
|
*/
|
2014-10-17 17:25:41 -05:00
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2015-04-29 13:20:25 -05:00
|
|
|
if ((*netreq->dns_base_transport == GETDNS_BASE_TRANSPORT_TCP_SINGLE) ||
|
|
|
|
(*netreq->dns_base_transport == GETDNS_BASE_TRANSPORT_UDP))
|
2015-03-21 04:41:25 -05:00
|
|
|
query_id = arc4random();
|
2014-10-17 17:25:41 -05:00
|
|
|
else do {
|
2015-03-21 04:41:25 -05:00
|
|
|
query_id = arc4random();
|
2014-10-17 17:25:41 -05:00
|
|
|
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));
|
|
|
|
|
2015-02-03 03:46:44 -06:00
|
|
|
GLDNS_ID_SET(netreq->query, query_id);
|
2015-03-22 10:50:48 -05:00
|
|
|
if (netreq->opt) {
|
2015-02-12 05:03:20 -06:00
|
|
|
/* no limits on the max udp payload size with tcp */
|
|
|
|
gldns_write_uint16(netreq->opt + 3, 65535);
|
2014-10-17 17:25:41 -05:00
|
|
|
|
2015-03-22 10:50:48 -05:00
|
|
|
if (netreq->owner->edns_cookies) {
|
|
|
|
netreq->response = attach_edns_cookie(
|
|
|
|
netreq->upstream, netreq->opt);
|
|
|
|
pkt_len = netreq->response - netreq->query;
|
|
|
|
gldns_write_uint16(netreq->query - 2, pkt_len);
|
|
|
|
}
|
|
|
|
}
|
2014-10-17 17:25:41 -05:00
|
|
|
/* We have an initialized packet buffer.
|
|
|
|
* Lets see how much of it we can write
|
|
|
|
*/
|
2014-10-28 12:37:11 -05:00
|
|
|
#ifdef USE_TCP_FASTOPEN
|
2014-10-24 10:08:20 -05:00
|
|
|
/* We use sendto() here which will do both a connect and send */
|
2015-02-03 03:46:44 -06:00
|
|
|
written = sendto(fd, netreq->query - 2, pkt_len + 2,
|
|
|
|
MSG_FASTOPEN, (struct sockaddr *)&(netreq->upstream->addr),
|
|
|
|
netreq->upstream->addr_len);
|
2014-10-24 10:08:20 -05:00
|
|
|
/* If pipelining we will find that the connection is already up so
|
|
|
|
just fall back to a 'normal' write. */
|
|
|
|
if (written == -1 && errno == EISCONN)
|
2015-02-03 03:46:44 -06:00
|
|
|
written = write(fd, netreq->query - 2, pkt_len + 2);
|
2014-10-24 10:08:20 -05:00
|
|
|
|
|
|
|
if ((written == -1 && (errno == EAGAIN ||
|
|
|
|
errno == EWOULDBLOCK ||
|
|
|
|
/* Add the error case where the connection is in progress which is when
|
|
|
|
a cookie is not available (e.g. when doing the first request to an
|
|
|
|
upstream). We must let the handshake complete since non-blocking. */
|
|
|
|
errno == EINPROGRESS)) ||
|
|
|
|
written < pkt_len + 2) {
|
|
|
|
#else
|
2015-02-03 03:46:44 -06:00
|
|
|
written = write(fd, netreq->query - 2, pkt_len + 2);
|
2014-10-17 17:25:41 -05:00
|
|
|
if ((written == -1 && (errno == EAGAIN ||
|
|
|
|
errno == EWOULDBLOCK)) ||
|
|
|
|
written < pkt_len + 2) {
|
2014-10-24 10:08:20 -05:00
|
|
|
#endif
|
2014-10-17 17:25:41 -05:00
|
|
|
/* We couldn't write the whole packet.
|
2015-02-03 03:46:44 -06:00
|
|
|
* We have to return with STUB_TCP_AGAIN.
|
|
|
|
* Setup tcp to track the state.
|
2014-10-17 17:25:41 -05:00
|
|
|
*/
|
2015-02-03 03:46:44 -06:00
|
|
|
tcp->write_buf = netreq->query - 2;
|
|
|
|
tcp->write_buf_len = pkt_len + 2;
|
2014-10-24 16:12:28 -05:00
|
|
|
tcp->written = written >= 0 ? written : 0;
|
2014-10-17 17:25:41 -05:00
|
|
|
|
|
|
|
return STUB_TCP_AGAIN;
|
|
|
|
|
|
|
|
} else if (written == -1)
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
|
|
|
|
/* We were able to write everything! Start reading. */
|
2015-02-03 03:46:44 -06:00
|
|
|
return (int) query_id;
|
2014-10-17 17:25:41 -05:00
|
|
|
|
|
|
|
} else {/* if (! tcp->write_buf) */
|
|
|
|
|
2014-10-24 10:08:20 -05:00
|
|
|
/* Coming back from an earlier unfinished write or handshake.
|
2014-10-17 17:25:41 -05:00
|
|
|
* 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;
|
|
|
|
|
2015-03-23 15:38:50 -05:00
|
|
|
query_id = (int)GLDNS_ID_WIRE(tcp->write_buf + 2);
|
2014-10-17 17:25:41 -05:00
|
|
|
/* Done. Start reading */
|
2015-02-03 03:46:44 -06:00
|
|
|
tcp->write_buf = NULL;
|
2015-03-23 15:38:50 -05:00
|
|
|
return query_id;
|
2014-10-17 17:25:41 -05:00
|
|
|
|
|
|
|
} /* if (! tcp->write_buf) */
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
|
|
|
netreq->query_id = (uint16_t) q;
|
2014-10-19 15:51:42 -05:00
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(
|
2014-10-17 17:25:41 -05:00
|
|
|
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
|
|
|
stub_tcp_read_cb, NULL, stub_timeout_cb));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-07 13:03:34 -06:00
|
|
|
static int
|
2015-04-29 13:20:25 -05:00
|
|
|
stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
|
|
|
|
getdns_network_req *netreq)
|
2014-12-07 13:03:34 -06:00
|
|
|
{
|
|
|
|
size_t pkt_len = netreq->response - netreq->query;
|
|
|
|
ssize_t written;
|
|
|
|
uint16_t query_id;
|
|
|
|
intptr_t query_id_intptr;
|
2015-04-19 11:16:58 -05:00
|
|
|
SSL* tls_obj = upstream->tls_obj;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
int q = check_tls(upstream);
|
2015-04-19 11:16:58 -05:00
|
|
|
if (q != 0)
|
|
|
|
return q;
|
2014-12-07 13:03:34 -06:00
|
|
|
|
|
|
|
/* Do we have remaining data that we could not write before? */
|
|
|
|
if (! tcp->write_buf) {
|
|
|
|
/* No, this is an initial write. Try to send
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
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(netreq->query, query_id);
|
|
|
|
if (netreq->opt)
|
|
|
|
/* no limits on the max udp payload size with tcp */
|
|
|
|
gldns_write_uint16(netreq->opt + 3, 65535);
|
|
|
|
|
|
|
|
/* We have an initialized packet buffer.
|
|
|
|
* Lets see how much of it we can write */
|
|
|
|
|
|
|
|
// TODO[TLS]: Handle error cases, partial writes, renegotiation etc.
|
|
|
|
ERR_clear_error();
|
|
|
|
written = SSL_write(tls_obj, netreq->query - 2, pkt_len + 2);
|
|
|
|
if (written <= 0)
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
|
|
|
|
/* We were able to write everything! Start reading. */
|
|
|
|
return (int) query_id;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
static void
|
|
|
|
upstream_write_cb(void *userarg)
|
|
|
|
{
|
|
|
|
getdns_upstream *upstream = (getdns_upstream *)userarg;
|
|
|
|
getdns_network_req *netreq = upstream->write_queue;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
int q;
|
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: **********CALLBACK***********\n");
|
2015-04-29 13:20:25 -05:00
|
|
|
fprintf(stderr,"[TLS]: WRITE(upstream_write_cb): upstream fd %d, SEND"
|
|
|
|
" netreq %p \n", upstream->fd, netreq);
|
2015-04-19 11:16:58 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
if (*netreq->dns_base_transport == GETDNS_BASE_TRANSPORT_TLS ||
|
|
|
|
(*netreq->dns_base_transport == GETDNS_BASE_TRANSPORT_STARTTLS &&
|
|
|
|
upstream->tls_hs_state != GETDNS_HS_NONE))
|
2015-04-19 11:16:58 -05:00
|
|
|
q = stub_tls_write(upstream, &upstream->tcp, netreq);
|
2014-12-07 13:03:34 -06:00
|
|
|
else
|
|
|
|
q = stub_tcp_write(upstream->fd, &upstream->tcp, netreq);
|
|
|
|
|
|
|
|
switch (q) {
|
2014-10-18 07:32:55 -05:00
|
|
|
case STUB_TCP_AGAIN:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case STUB_TCP_ERROR:
|
|
|
|
stub_erred(netreq);
|
|
|
|
return;
|
|
|
|
|
2015-04-19 11:16:58 -05:00
|
|
|
case STUB_TLS_SETUP_ERROR:
|
2015-04-29 13:20:25 -05:00
|
|
|
/* Could not complete the TLS set up. Need to fallback.*/
|
|
|
|
if (fallback_on_write(netreq) == STUB_TCP_ERROR) {
|
|
|
|
fprintf(stderr,"[TLS]: Fallback failed.\n");
|
|
|
|
message_erred(netreq);
|
2015-04-27 10:37:16 -05:00
|
|
|
}
|
2015-04-19 11:16:58 -05:00
|
|
|
return;
|
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
default:
|
|
|
|
netreq->query_id = (uint16_t) q;
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr, "[TLS]: WRITE(upstream_write_cb): successfull write on fd %d\n", upstream->fd);
|
2014-10-18 07:32:55 -05:00
|
|
|
|
|
|
|
/* Unqueue the netreq from the write_queue */
|
|
|
|
if (!(upstream->write_queue = netreq->write_queue_tail)) {
|
|
|
|
upstream->write_queue_last = NULL;
|
|
|
|
upstream->event.write_cb = NULL;
|
|
|
|
|
|
|
|
/* Reschedule (if already reading) to clear writable */
|
2014-10-19 15:51:42 -05:00
|
|
|
if (upstream->event.read_cb) {
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER,
|
|
|
|
&upstream->event);
|
2014-10-19 15:51:42 -05:00
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
}
|
|
|
|
/* Schedule reading (if not already scheduled) */
|
|
|
|
if (!upstream->event.read_cb) {
|
|
|
|
upstream->event.read_cb = upstream_read_cb;
|
2014-10-19 15:51:42 -05:00
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
}
|
2015-04-29 13:20:25 -05:00
|
|
|
if (upstream->starttls_req) {
|
|
|
|
/* Now deschedule any further writes on this connection until we get
|
|
|
|
the STARTTLS answer*/
|
|
|
|
fprintf(stderr, "[STARTTLS] method: upstream_write_cb -> STARTTTLS -"
|
|
|
|
"clearing upstream->event.write_cb\n");
|
|
|
|
upstream->event.write_cb = NULL;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
}
|
2014-10-18 07:32:55 -05:00
|
|
|
/* With synchonous lookups, schedule the read locally too */
|
|
|
|
if (netreq->event.write_cb) {
|
2014-10-19 15:51:42 -05:00
|
|
|
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
dnsreq->loop, upstream->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
2014-10-20 16:51:05 -05:00
|
|
|
netreq_upstream_read_cb,
|
2015-04-29 13:20:25 -05:00
|
|
|
(upstream->write_queue && !upstream->starttls_req ?
|
2014-10-20 16:51:05 -05:00
|
|
|
netreq_upstream_write_cb : NULL),
|
|
|
|
stub_timeout_cb));
|
2014-10-18 07:32:55 -05:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
netreq_upstream_write_cb(void *userarg)
|
|
|
|
{
|
|
|
|
upstream_write_cb(((getdns_network_req *)userarg)->upstream);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
/* We have a connected socket and a global event loop */
|
|
|
|
assert(upstream->fd >= 0);
|
|
|
|
assert(upstream->loop);
|
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: SCHEDULE(upstream_schedule_netreq): fd %d\n", upstream->fd);
|
2015-04-19 11:16:58 -05:00
|
|
|
|
2014-10-18 07:32:55 -05:00
|
|
|
/* Append netreq to write_queue */
|
|
|
|
if (!upstream->write_queue) {
|
|
|
|
upstream->write_queue = upstream->write_queue_last = netreq;
|
|
|
|
upstream->event.write_cb = upstream_write_cb;
|
2014-10-19 15:51:42 -05:00
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
|
|
|
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
|
|
|
} else {
|
|
|
|
upstream->write_queue_last->write_queue_tail = netreq;
|
|
|
|
upstream->write_queue_last = netreq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-17 09:50:08 -05:00
|
|
|
static int
|
2015-04-19 11:16:58 -05:00
|
|
|
tcp_connect(getdns_upstream *upstream, getdns_base_transport_t transport)
|
|
|
|
{
|
2014-12-07 13:03:34 -06:00
|
|
|
|
2015-04-19 11:16:58 -05:00
|
|
|
int fd = -1;
|
|
|
|
if ((fd = socket(upstream->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
2014-12-07 13:03:34 -06:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
getdns_sock_nonblock(fd);
|
|
|
|
#ifdef USE_TCP_FASTOPEN
|
|
|
|
/* Leave the connect to the later call to sendto() if using TCP*/
|
2015-04-24 10:29:08 -05:00
|
|
|
if (transport == GETDNS_BASE_TRANSPORT_TCP ||
|
|
|
|
transport == GETDNS_BASE_TRANSPORT_TCP_SINGLE)
|
2014-12-07 13:03:34 -06:00
|
|
|
return fd;
|
|
|
|
#endif
|
2015-04-19 11:16:58 -05:00
|
|
|
if (connect(fd, (struct sockaddr *)&upstream->addr,
|
|
|
|
upstream->addr_len) == -1) {
|
2014-12-07 13:03:34 -06:00
|
|
|
if (errno != EINPROGRESS) {
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2015-04-19 11:16:58 -05:00
|
|
|
int
|
|
|
|
connect_to_upstream(getdns_upstream *upstream, getdns_base_transport_t transport,
|
2015-04-29 13:20:25 -05:00
|
|
|
getdns_dns_req *dnsreq)
|
2015-04-19 11:16:58 -05:00
|
|
|
{
|
2015-04-29 13:20:25 -05:00
|
|
|
/* First check if existing connection can be used, which may still be being
|
|
|
|
* set up. */
|
|
|
|
switch(transport) {
|
|
|
|
case GETDNS_BASE_TRANSPORT_TCP:
|
|
|
|
if (upstream->fd != -1) {
|
|
|
|
fprintf(stderr,"[TLS]: CONNECT(connect_to_upstream):"
|
|
|
|
"tcp_connect using existing TCP fd %d\n", upstream->fd);
|
|
|
|
return upstream->fd;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GETDNS_BASE_TRANSPORT_TLS:
|
|
|
|
if (tls_handshake_active(upstream->tls_hs_state)) {
|
|
|
|
fprintf(stderr,"[TLS]: CONNECT(connect_to_upstream):"
|
|
|
|
"tcp_connect using existing TLS fd %d\n", upstream->fd);
|
|
|
|
return upstream->fd;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GETDNS_BASE_TRANSPORT_STARTTLS:
|
|
|
|
/* Either negotiating, or doing handshake*/
|
|
|
|
if ((upstream->starttls_req != NULL) ||
|
|
|
|
(upstream->starttls_req == NULL &&
|
|
|
|
tls_handshake_active(upstream->tls_hs_state))) {
|
|
|
|
fprintf(stderr,"[TLS]: CONNECT(connect_to_upstream):"
|
|
|
|
"tcp_connect using existing STARTTLS fd %d\n", upstream->fd);
|
|
|
|
return upstream->fd;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2015-04-19 11:16:58 -05:00
|
|
|
}
|
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
/* If not, create a new one */
|
2015-04-24 10:29:08 -05:00
|
|
|
int fd = -1;
|
2015-04-19 11:16:58 -05:00
|
|
|
switch(transport) {
|
2015-04-24 10:29:08 -05:00
|
|
|
case GETDNS_BASE_TRANSPORT_UDP:
|
2015-04-19 11:16:58 -05:00
|
|
|
if ((fd = socket(
|
|
|
|
upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
|
|
|
|
return -1;
|
|
|
|
getdns_sock_nonblock(fd);
|
|
|
|
return fd;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
case GETDNS_BASE_TRANSPORT_TCP_SINGLE:
|
|
|
|
case GETDNS_BASE_TRANSPORT_TCP:
|
2015-04-19 11:16:58 -05:00
|
|
|
fd = tcp_connect(upstream, transport);
|
2015-04-29 13:20:25 -05:00
|
|
|
upstream->loop = dnsreq->context->extension;
|
|
|
|
upstream->fd = fd;
|
2015-04-19 11:16:58 -05:00
|
|
|
break;
|
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
case GETDNS_BASE_TRANSPORT_TLS:
|
2015-04-19 11:16:58 -05:00
|
|
|
fd = tcp_connect(upstream, transport);
|
2015-04-24 10:29:08 -05:00
|
|
|
if (fd == -1) return -1;
|
2015-04-29 13:20:25 -05:00
|
|
|
upstream->tls_obj = create_tls_object(dnsreq->context, fd);
|
2015-04-24 10:29:08 -05:00
|
|
|
if (upstream->tls_obj == NULL) {
|
|
|
|
fprintf(stderr,"[TLS]: could not create tls object\n");
|
2015-04-19 11:16:58 -05:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
upstream->tls_hs_state = GETDNS_HS_WRITE;
|
2015-04-29 13:20:25 -05:00
|
|
|
upstream->loop = dnsreq->context->extension;
|
|
|
|
upstream->fd = fd;
|
|
|
|
break;
|
|
|
|
case GETDNS_BASE_TRANSPORT_STARTTLS:
|
|
|
|
fd = tcp_connect(upstream, transport);
|
|
|
|
if (fd == -1) return -1;
|
|
|
|
if (!create_starttls_request(dnsreq, upstream, dnsreq->loop))
|
|
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
getdns_network_req *starttls_netreq = upstream->starttls_req->netreqs[0];
|
|
|
|
upstream->loop = dnsreq->context->extension;
|
|
|
|
upstream->fd = fd;
|
|
|
|
upstream_schedule_netreq(upstream, starttls_netreq);
|
|
|
|
/* Schedule at least the timeout locally.
|
|
|
|
* And also the write if we perform a synchronous lookup */
|
|
|
|
/* TODO[TLS]: How should we handle timeout on STARTTLS negotiation?*/
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
dnsreq->loop, upstream->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&starttls_netreq->event,
|
|
|
|
starttls_netreq, NULL, (dnsreq->loop != upstream->loop
|
|
|
|
? netreq_upstream_write_cb : NULL), stub_timeout_cb));
|
2015-04-19 11:16:58 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
/* Nothing to do*/
|
|
|
|
}
|
2015-04-27 09:32:57 -05:00
|
|
|
fprintf(stderr,"[TLS]: CONNECT(connect_to_upstream): created new connection %d\n", fd);
|
2015-04-19 11:16:58 -05:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
static getdns_upstream*
|
|
|
|
pick_and_connect_to_upstream(getdns_network_req *netreq,
|
|
|
|
getdns_base_transport_t transport,
|
|
|
|
int *fd)
|
|
|
|
{
|
|
|
|
/* TODO[TLS]: Fallback through upstreams....?*/
|
|
|
|
getdns_upstream *upstream = pick_upstream(netreq, transport);
|
|
|
|
if (!upstream)
|
|
|
|
return NULL;
|
|
|
|
*fd = connect_to_upstream(upstream, transport, netreq->owner);
|
|
|
|
return upstream;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
find_upstream_for_netreq(getdns_network_req *netreq)
|
2014-10-15 16:57:24 -05:00
|
|
|
{
|
2015-04-24 10:29:08 -05:00
|
|
|
int fd = -1;
|
2015-04-29 13:20:25 -05:00
|
|
|
for (int i = 0; i < GETDNS_BASE_TRANSPORT_MAX &&
|
|
|
|
netreq->dns_base_transports[i] != GETDNS_BASE_TRANSPORT_NONE; i++) {
|
|
|
|
netreq->upstream = pick_and_connect_to_upstream(netreq,
|
|
|
|
netreq->dns_base_transports[i],
|
|
|
|
&fd);
|
|
|
|
if (fd == -1)
|
|
|
|
continue;
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
move_netreq(getdns_network_req *netreq, getdns_upstream *upstream,
|
|
|
|
getdns_upstream *new_upstream)
|
|
|
|
{
|
|
|
|
/* Remove from queue, clearing event and fd if we are the last*/
|
|
|
|
|
|
|
|
fprintf(stderr,"[TLS]: FALLBACK(move_netreq)\n");
|
|
|
|
|
|
|
|
if (!(upstream->write_queue = netreq->write_queue_tail)) {
|
|
|
|
upstream->write_queue_last = NULL;
|
|
|
|
upstream->event.write_cb = NULL;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
|
|
close(upstream->fd);
|
|
|
|
upstream->fd = -1;
|
|
|
|
}
|
|
|
|
netreq->write_queue_tail = NULL;
|
|
|
|
|
|
|
|
/* Schedule with the new upstream */
|
|
|
|
netreq->upstream = new_upstream;
|
|
|
|
upstream_schedule_netreq(new_upstream, netreq);
|
|
|
|
|
|
|
|
/* TODO[TLS]: Timout need to be adjusted and rescheduled on the new fd ...*/
|
|
|
|
/* Note, setup timeout should be shorter than message timeout for
|
|
|
|
* messages with fallback or don't have time to re-try. */
|
|
|
|
|
|
|
|
/* For sync messages we must re-schedule the events here.*/
|
|
|
|
if (netreq->owner->loop != upstream->loop) {
|
|
|
|
/* Create an event for the new upstream*/
|
|
|
|
GETDNS_CLEAR_EVENT(netreq->owner->loop, &netreq->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
netreq->owner->loop, new_upstream->fd, netreq->owner->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
|
|
|
( new_upstream->netreq_by_query_id.count ?
|
|
|
|
netreq_upstream_read_cb : NULL ),
|
|
|
|
( new_upstream->write_queue ?
|
|
|
|
netreq_upstream_write_cb : NULL),
|
|
|
|
stub_timeout_cb));
|
|
|
|
|
|
|
|
/* Now one for the old upstream. Must schedule this last to make sure
|
|
|
|
* it is called back first....?*/
|
|
|
|
if (upstream->write_queue) {
|
|
|
|
GETDNS_CLEAR_EVENT(netreq->owner->loop, &upstream->write_queue->event);
|
|
|
|
GETDNS_SCHEDULE_EVENT(
|
|
|
|
upstream->write_queue->owner->loop, upstream->fd,
|
|
|
|
upstream->write_queue->owner->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&upstream->write_queue->event,
|
|
|
|
upstream->write_queue,
|
|
|
|
( upstream->netreq_by_query_id.count ?
|
|
|
|
netreq_upstream_read_cb : NULL ),
|
|
|
|
( upstream->write_queue ?
|
|
|
|
netreq_upstream_write_cb : NULL),
|
|
|
|
stub_timeout_cb));
|
2015-04-24 10:29:08 -05:00
|
|
|
}
|
2015-04-19 11:16:58 -05:00
|
|
|
}
|
2015-04-29 13:20:25 -05:00
|
|
|
netreq->dns_base_transport++;
|
|
|
|
return upstream->fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fallback_on_write(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
|
|
|
|
fprintf(stderr,"[TLS]: FALLBACK(fallback_on_write)\n");
|
|
|
|
|
|
|
|
/* TODO[TLS]: Fallback through all transports.*/
|
|
|
|
getdns_base_transport_t *next_transport = netreq->dns_base_transport;
|
|
|
|
if (*(++next_transport) == GETDNS_BASE_TRANSPORT_NONE)
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
|
|
|
|
if (*netreq->dns_base_transport == GETDNS_BASE_TRANSPORT_STARTTLS &&
|
|
|
|
*next_transport == GETDNS_BASE_TRANSPORT_TCP) {
|
|
|
|
fprintf(stderr,"[TLS]: FALLBACK(fallback_on_write) STARTTLS->TCP\n");
|
|
|
|
/* Special case where can stay on same upstream*/
|
|
|
|
netreq->dns_base_transport++;
|
|
|
|
return netreq->upstream->fd;
|
|
|
|
}
|
|
|
|
getdns_upstream *upstream = netreq->upstream;
|
|
|
|
int fd;
|
|
|
|
getdns_upstream *new_upstream =
|
|
|
|
pick_and_connect_to_upstream(netreq, *next_transport, &fd);
|
|
|
|
if (!new_upstream)
|
|
|
|
return STUB_TCP_ERROR;
|
|
|
|
return move_netreq(netreq, upstream, new_upstream);
|
|
|
|
}
|
|
|
|
|
|
|
|
getdns_return_t
|
|
|
|
priv_getdns_submit_stub_request(getdns_network_req *netreq)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
getdns_dns_req *dnsreq = netreq->owner;
|
|
|
|
|
|
|
|
/* This does a best effort to get a initial fd.
|
|
|
|
* All other set up is done async*/
|
|
|
|
fd = find_upstream_for_netreq(netreq);
|
2015-04-24 10:29:08 -05:00
|
|
|
if (fd == -1)
|
|
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
2014-10-17 17:25:41 -05:00
|
|
|
|
2015-04-24 10:29:08 -05:00
|
|
|
switch(*netreq->dns_base_transport) {
|
|
|
|
case GETDNS_BASE_TRANSPORT_UDP:
|
|
|
|
case GETDNS_BASE_TRANSPORT_TCP_SINGLE:
|
2015-04-19 11:16:58 -05:00
|
|
|
netreq->fd = fd;
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(
|
2014-10-17 17:25:41 -05:00
|
|
|
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
|
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq,
|
2015-04-24 10:29:08 -05:00
|
|
|
NULL, (*netreq->dns_base_transport == GETDNS_BASE_TRANSPORT_UDP ?
|
|
|
|
stub_udp_write_cb: stub_tcp_write_cb), stub_timeout_cb));
|
2014-10-17 17:25:41 -05:00
|
|
|
return GETDNS_RETURN_GOOD;
|
2014-10-18 07:32:55 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
case GETDNS_BASE_TRANSPORT_STARTTLS:
|
2015-04-24 10:29:08 -05:00
|
|
|
case GETDNS_BASE_TRANSPORT_TCP:
|
|
|
|
case GETDNS_BASE_TRANSPORT_TLS:
|
2014-10-17 17:25:41 -05:00
|
|
|
|
2015-04-29 13:20:25 -05:00
|
|
|
upstream_schedule_netreq(netreq->upstream, netreq);
|
2015-04-19 11:16:58 -05:00
|
|
|
/* TODO[TLS]: Timeout handling for async calls must change....
|
|
|
|
* Maybe even change scheduling for sync calls here too*/
|
2014-10-18 07:32:55 -05:00
|
|
|
GETDNS_SCHEDULE_EVENT(
|
2015-04-29 13:20:25 -05:00
|
|
|
dnsreq->loop, netreq->upstream->fd, dnsreq->context->timeout,
|
2014-10-18 07:32:55 -05:00
|
|
|
getdns_eventloop_event_init(&netreq->event, netreq, NULL,
|
2015-04-29 13:20:25 -05:00
|
|
|
( dnsreq->loop != netreq->upstream->loop /* Synchronous lookup? */
|
2014-10-18 07:32:55 -05:00
|
|
|
? netreq_upstream_write_cb : NULL), stub_timeout_cb));
|
|
|
|
|
|
|
|
return GETDNS_RETURN_GOOD;
|
2014-10-17 17:25:41 -05:00
|
|
|
default:
|
|
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
}
|
2014-09-16 08:43:20 -05:00
|
|
|
}
|
|
|
|
|
2015-04-19 11:16:58 -05:00
|
|
|
/* stub.c */
|