monero/external/unbound/util/netevent.c
Erik de Castro Lopo a85b5759f3 Upgrade unbound library
These files were pulled from the 1.6.3 release tarball.

This new version builds against OpenSSL version 1.1 which will be
the default in the new Debian Stable which is due to be released
RealSoonNow (tm).
2017-06-17 23:04:00 +10:00

2410 lines
63 KiB
C

/*
* util/netevent.c - event notification
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 name of the NLNET LABS 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 THE COPYRIGHT
* HOLDER OR CONTRIBUTORS 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.
*/
/**
* \file
*
* This file contains event notification functions.
*/
#include "config.h"
#include "util/netevent.h"
#include "util/ub_event.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/fptr_wlist.h"
#include "sldns/pkthdr.h"
#include "sldns/sbuffer.h"
#include "dnstap/dnstap.h"
#include "dnscrypt/dnscrypt.h"
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
/* -------- Start of local definitions -------- */
/** if CMSG_ALIGN is not defined on this platform, a workaround */
#ifndef CMSG_ALIGN
# ifdef __CMSG_ALIGN
# define CMSG_ALIGN(n) __CMSG_ALIGN(n)
# elif defined(CMSG_DATA_ALIGN)
# define CMSG_ALIGN _CMSG_DATA_ALIGN
# else
# define CMSG_ALIGN(len) (((len)+sizeof(long)-1) & ~(sizeof(long)-1))
# endif
#endif
/** if CMSG_LEN is not defined on this platform, a workaround */
#ifndef CMSG_LEN
# define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+(len))
#endif
/** if CMSG_SPACE is not defined on this platform, a workaround */
#ifndef CMSG_SPACE
# ifdef _CMSG_HDR_ALIGN
# define CMSG_SPACE(l) (CMSG_ALIGN(l)+_CMSG_HDR_ALIGN(sizeof(struct cmsghdr)))
# else
# define CMSG_SPACE(l) (CMSG_ALIGN(l)+CMSG_ALIGN(sizeof(struct cmsghdr)))
# endif
#endif
/** The TCP reading or writing query timeout in milliseconds */
#define TCP_QUERY_TIMEOUT 120000
/** The TCP timeout in msec for fast queries, above half are used */
#define TCP_QUERY_TIMEOUT_FAST 200
#ifndef NONBLOCKING_IS_BROKEN
/** number of UDP reads to perform per read indication from select */
#define NUM_UDP_PER_SELECT 100
#else
#define NUM_UDP_PER_SELECT 1
#endif
/**
* The internal event structure for keeping ub_event info for the event.
* Possibly other structures (list, tree) this is part of.
*/
struct internal_event {
/** the comm base */
struct comm_base* base;
/** ub_event event type */
struct ub_event* ev;
};
/**
* Internal base structure, so that every thread has its own events.
*/
struct internal_base {
/** ub_event event_base type. */
struct ub_event_base* base;
/** seconds time pointer points here */
time_t secs;
/** timeval with current time */
struct timeval now;
/** the event used for slow_accept timeouts */
struct ub_event* slow_accept;
/** true if slow_accept is enabled */
int slow_accept_enabled;
};
/**
* Internal timer structure, to store timer event in.
*/
struct internal_timer {
/** the super struct from which derived */
struct comm_timer super;
/** the comm base */
struct comm_base* base;
/** ub_event event type */
struct ub_event* ev;
/** is timer enabled */
uint8_t enabled;
};
/**
* Internal signal structure, to store signal event in.
*/
struct internal_signal {
/** ub_event event type */
struct ub_event* ev;
/** next in signal list */
struct internal_signal* next;
};
/** create a tcp handler with a parent */
static struct comm_point* comm_point_create_tcp_handler(
struct comm_base *base, struct comm_point* parent, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg);
/* -------- End of local definitions -------- */
struct comm_base*
comm_base_create(int sigs)
{
struct comm_base* b = (struct comm_base*)calloc(1,
sizeof(struct comm_base));
const char *evnm="event", *evsys="", *evmethod="";
if(!b)
return NULL;
b->eb = (struct internal_base*)calloc(1, sizeof(struct internal_base));
if(!b->eb) {
free(b);
return NULL;
}
b->eb->base = ub_default_event_base(sigs, &b->eb->secs, &b->eb->now);
if(!b->eb->base) {
free(b->eb);
free(b);
return NULL;
}
ub_comm_base_now(b);
ub_get_event_sys(b->eb->base, &evnm, &evsys, &evmethod);
verbose(VERB_ALGO, "%s %s user %s method.", evnm, evsys, evmethod);
return b;
}
struct comm_base*
comm_base_create_event(struct ub_event_base* base)
{
struct comm_base* b = (struct comm_base*)calloc(1,
sizeof(struct comm_base));
if(!b)
return NULL;
b->eb = (struct internal_base*)calloc(1, sizeof(struct internal_base));
if(!b->eb) {
free(b);
return NULL;
}
b->eb->base = base;
ub_comm_base_now(b);
return b;
}
void
comm_base_delete(struct comm_base* b)
{
if(!b)
return;
if(b->eb->slow_accept_enabled) {
if(ub_event_del(b->eb->slow_accept) != 0) {
log_err("could not event_del slow_accept");
}
ub_event_free(b->eb->slow_accept);
}
ub_event_base_free(b->eb->base);
b->eb->base = NULL;
free(b->eb);
free(b);
}
void
comm_base_delete_no_base(struct comm_base* b)
{
if(!b)
return;
if(b->eb->slow_accept_enabled) {
if(ub_event_del(b->eb->slow_accept) != 0) {
log_err("could not event_del slow_accept");
}
ub_event_free(b->eb->slow_accept);
}
b->eb->base = NULL;
free(b->eb);
free(b);
}
void
comm_base_timept(struct comm_base* b, time_t** tt, struct timeval** tv)
{
*tt = &b->eb->secs;
*tv = &b->eb->now;
}
void
comm_base_dispatch(struct comm_base* b)
{
int retval;
retval = ub_event_base_dispatch(b->eb->base);
if(retval < 0) {
fatal_exit("event_dispatch returned error %d, "
"errno is %s", retval, strerror(errno));
}
}
void comm_base_exit(struct comm_base* b)
{
if(ub_event_base_loopexit(b->eb->base) != 0) {
log_err("Could not loopexit");
}
}
void comm_base_set_slow_accept_handlers(struct comm_base* b,
void (*stop_acc)(void*), void (*start_acc)(void*), void* arg)
{
b->stop_accept = stop_acc;
b->start_accept = start_acc;
b->cb_arg = arg;
}
struct ub_event_base* comm_base_internal(struct comm_base* b)
{
return b->eb->base;
}
/** see if errno for udp has to be logged or not uses globals */
static int
udp_send_errno_needs_log(struct sockaddr* addr, socklen_t addrlen)
{
/* do not log transient errors (unless high verbosity) */
#if defined(ENETUNREACH) || defined(EHOSTDOWN) || defined(EHOSTUNREACH) || defined(ENETDOWN)
switch(errno) {
# ifdef ENETUNREACH
case ENETUNREACH:
# endif
# ifdef EHOSTDOWN
case EHOSTDOWN:
# endif
# ifdef EHOSTUNREACH
case EHOSTUNREACH:
# endif
# ifdef ENETDOWN
case ENETDOWN:
# endif
if(verbosity < VERB_ALGO)
return 0;
default:
break;
}
#endif
/* permission denied is gotten for every send if the
* network is disconnected (on some OS), squelch it */
if( ((errno == EPERM)
# ifdef EADDRNOTAVAIL
/* 'Cannot assign requested address' also when disconnected */
|| (errno == EADDRNOTAVAIL)
# endif
) && verbosity < VERB_DETAIL)
return 0;
/* squelch errors where people deploy AAAA ::ffff:bla for
* authority servers, which we try for intranets. */
if(errno == EINVAL && addr_is_ip4mapped(
(struct sockaddr_storage*)addr, addrlen) &&
verbosity < VERB_DETAIL)
return 0;
/* SO_BROADCAST sockopt can give access to 255.255.255.255,
* but a dns cache does not need it. */
if(errno == EACCES && addr_is_broadcast(
(struct sockaddr_storage*)addr, addrlen) &&
verbosity < VERB_DETAIL)
return 0;
return 1;
}
int tcp_connect_errno_needs_log(struct sockaddr* addr, socklen_t addrlen)
{
return udp_send_errno_needs_log(addr, addrlen);
}
/* send a UDP reply */
int
comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet,
struct sockaddr* addr, socklen_t addrlen)
{
ssize_t sent;
log_assert(c->fd != -1);
#ifdef UNBOUND_DEBUG
if(sldns_buffer_remaining(packet) == 0)
log_err("error: send empty UDP packet");
#endif
log_assert(addr && addrlen > 0);
sent = sendto(c->fd, (void*)sldns_buffer_begin(packet),
sldns_buffer_remaining(packet), 0,
addr, addrlen);
if(sent == -1) {
/* try again and block, waiting for IO to complete,
* we want to send the answer, and we will wait for
* the ethernet interface buffer to have space. */
#ifndef USE_WINSOCK
if(errno == EAGAIN ||
# ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
# endif
errno == ENOBUFS) {
#else
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAENOBUFS ||
WSAGetLastError() == WSAEWOULDBLOCK) {
#endif
int e;
fd_set_block(c->fd);
sent = sendto(c->fd, (void*)sldns_buffer_begin(packet),
sldns_buffer_remaining(packet), 0,
addr, addrlen);
e = errno;
fd_set_nonblock(c->fd);
errno = e;
}
}
if(sent == -1) {
if(!udp_send_errno_needs_log(addr, addrlen))
return 0;
#ifndef USE_WINSOCK
verbose(VERB_OPS, "sendto failed: %s", strerror(errno));
#else
verbose(VERB_OPS, "sendto failed: %s",
wsa_strerror(WSAGetLastError()));
#endif
log_addr(VERB_OPS, "remote address is",
(struct sockaddr_storage*)addr, addrlen);
return 0;
} else if((size_t)sent != sldns_buffer_remaining(packet)) {
log_err("sent %d in place of %d bytes",
(int)sent, (int)sldns_buffer_remaining(packet));
return 0;
}
return 1;
}
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && (defined(HAVE_RECVMSG) || defined(HAVE_SENDMSG))
/** print debug ancillary info */
static void p_ancil(const char* str, struct comm_reply* r)
{
if(r->srctype != 4 && r->srctype != 6) {
log_info("%s: unknown srctype %d", str, r->srctype);
return;
}
if(r->srctype == 6) {
char buf[1024];
if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr,
buf, (socklen_t)sizeof(buf)) == 0) {
(void)strlcpy(buf, "(inet_ntop error)", sizeof(buf));
}
buf[sizeof(buf)-1]=0;
log_info("%s: %s %d", str, buf, r->pktinfo.v6info.ipi6_ifindex);
} else if(r->srctype == 4) {
#ifdef IP_PKTINFO
char buf1[1024], buf2[1024];
if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_addr,
buf1, (socklen_t)sizeof(buf1)) == 0) {
(void)strlcpy(buf1, "(inet_ntop error)", sizeof(buf1));
}
buf1[sizeof(buf1)-1]=0;
#ifdef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST
if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst,
buf2, (socklen_t)sizeof(buf2)) == 0) {
(void)strlcpy(buf2, "(inet_ntop error)", sizeof(buf2));
}
buf2[sizeof(buf2)-1]=0;
#else
buf2[0]=0;
#endif
log_info("%s: %d %s %s", str, r->pktinfo.v4info.ipi_ifindex,
buf1, buf2);
#elif defined(IP_RECVDSTADDR)
char buf1[1024];
if(inet_ntop(AF_INET, &r->pktinfo.v4addr,
buf1, (socklen_t)sizeof(buf1)) == 0) {
(void)strlcpy(buf1, "(inet_ntop error)", sizeof(buf1));
}
buf1[sizeof(buf1)-1]=0;
log_info("%s: %s", str, buf1);
#endif /* IP_PKTINFO or PI_RECVDSTDADDR */
}
}
#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG||HAVE_SENDMSG */
/** send a UDP reply over specified interface*/
static int
comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet,
struct sockaddr* addr, socklen_t addrlen, struct comm_reply* r)
{
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_SENDMSG)
ssize_t sent;
struct msghdr msg;
struct iovec iov[1];
char control[256];
#ifndef S_SPLINT_S
struct cmsghdr *cmsg;
#endif /* S_SPLINT_S */
log_assert(c->fd != -1);
#ifdef UNBOUND_DEBUG
if(sldns_buffer_remaining(packet) == 0)
log_err("error: send empty UDP packet");
#endif
log_assert(addr && addrlen > 0);
msg.msg_name = addr;
msg.msg_namelen = addrlen;
iov[0].iov_base = sldns_buffer_begin(packet);
iov[0].iov_len = sldns_buffer_remaining(packet);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
#ifndef S_SPLINT_S
msg.msg_controllen = sizeof(control);
#endif /* S_SPLINT_S */
msg.msg_flags = 0;
#ifndef S_SPLINT_S
cmsg = CMSG_FIRSTHDR(&msg);
if(r->srctype == 4) {
#ifdef IP_PKTINFO
void* cmsg_data;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
log_assert(msg.msg_controllen <= sizeof(control));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
memmove(CMSG_DATA(cmsg), &r->pktinfo.v4info,
sizeof(struct in_pktinfo));
/* unset the ifindex to not bypass the routing tables */
cmsg_data = CMSG_DATA(cmsg);
((struct in_pktinfo *) cmsg_data)->ipi_ifindex = 0;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
#elif defined(IP_SENDSRCADDR)
msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
log_assert(msg.msg_controllen <= sizeof(control));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
memmove(CMSG_DATA(cmsg), &r->pktinfo.v4addr,
sizeof(struct in_addr));
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
#else
verbose(VERB_ALGO, "no IP_PKTINFO or IP_SENDSRCADDR");
msg.msg_control = NULL;
#endif /* IP_PKTINFO or IP_SENDSRCADDR */
} else if(r->srctype == 6) {
void* cmsg_data;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
log_assert(msg.msg_controllen <= sizeof(control));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
memmove(CMSG_DATA(cmsg), &r->pktinfo.v6info,
sizeof(struct in6_pktinfo));
/* unset the ifindex to not bypass the routing tables */
cmsg_data = CMSG_DATA(cmsg);
((struct in6_pktinfo *) cmsg_data)->ipi6_ifindex = 0;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
} else {
/* try to pass all 0 to use default route */
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
log_assert(msg.msg_controllen <= sizeof(control));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
memset(CMSG_DATA(cmsg), 0, sizeof(struct in6_pktinfo));
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
}
#endif /* S_SPLINT_S */
if(verbosity >= VERB_ALGO)
p_ancil("send_udp over interface", r);
sent = sendmsg(c->fd, &msg, 0);
if(sent == -1) {
/* try again and block, waiting for IO to complete,
* we want to send the answer, and we will wait for
* the ethernet interface buffer to have space. */
#ifndef USE_WINSOCK
if(errno == EAGAIN ||
# ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
# endif
errno == ENOBUFS) {
#else
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAENOBUFS ||
WSAGetLastError() == WSAEWOULDBLOCK) {
#endif
int e;
fd_set_block(c->fd);
sent = sendmsg(c->fd, &msg, 0);
e = errno;
fd_set_nonblock(c->fd);
errno = e;
}
}
if(sent == -1) {
if(!udp_send_errno_needs_log(addr, addrlen))
return 0;
verbose(VERB_OPS, "sendmsg failed: %s", strerror(errno));
log_addr(VERB_OPS, "remote address is",
(struct sockaddr_storage*)addr, addrlen);
#ifdef __NetBSD__
/* netbsd 7 has IP_PKTINFO for recv but not send */
if(errno == EINVAL && r->srctype == 4)
log_err("sendmsg: No support for sendmsg(IP_PKTINFO). "
"Please disable interface-automatic");
#endif
return 0;
} else if((size_t)sent != sldns_buffer_remaining(packet)) {
log_err("sent %d in place of %d bytes",
(int)sent, (int)sldns_buffer_remaining(packet));
return 0;
}
return 1;
#else
(void)c;
(void)packet;
(void)addr;
(void)addrlen;
(void)r;
log_err("sendmsg: IPV6_PKTINFO not supported");
return 0;
#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_SENDMSG */
}
void
comm_point_udp_ancil_callback(int fd, short event, void* arg)
{
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
struct comm_reply rep;
struct msghdr msg;
struct iovec iov[1];
ssize_t rcv;
char ancil[256];
int i;
#ifndef S_SPLINT_S
struct cmsghdr* cmsg;
#endif /* S_SPLINT_S */
rep.c = (struct comm_point*)arg;
log_assert(rep.c->type == comm_udp);
if(!(event&UB_EV_READ))
return;
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
ub_comm_base_now(rep.c->ev->base);
for(i=0; i<NUM_UDP_PER_SELECT; i++) {
sldns_buffer_clear(rep.c->buffer);
rep.addrlen = (socklen_t)sizeof(rep.addr);
log_assert(fd != -1);
log_assert(sldns_buffer_remaining(rep.c->buffer) > 0);
msg.msg_name = &rep.addr;
msg.msg_namelen = (socklen_t)sizeof(rep.addr);
iov[0].iov_base = sldns_buffer_begin(rep.c->buffer);
iov[0].iov_len = sldns_buffer_remaining(rep.c->buffer);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ancil;
#ifndef S_SPLINT_S
msg.msg_controllen = sizeof(ancil);
#endif /* S_SPLINT_S */
msg.msg_flags = 0;
rcv = recvmsg(fd, &msg, 0);
if(rcv == -1) {
if(errno != EAGAIN && errno != EINTR) {
log_err("recvmsg failed: %s", strerror(errno));
}
return;
}
rep.addrlen = msg.msg_namelen;
sldns_buffer_skip(rep.c->buffer, rcv);
sldns_buffer_flip(rep.c->buffer);
rep.srctype = 0;
#ifndef S_SPLINT_S
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if( cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO) {
rep.srctype = 6;
memmove(&rep.pktinfo.v6info, CMSG_DATA(cmsg),
sizeof(struct in6_pktinfo));
break;
#ifdef IP_PKTINFO
} else if( cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO) {
rep.srctype = 4;
memmove(&rep.pktinfo.v4info, CMSG_DATA(cmsg),
sizeof(struct in_pktinfo));
break;
#elif defined(IP_RECVDSTADDR)
} else if( cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_RECVDSTADDR) {
rep.srctype = 4;
memmove(&rep.pktinfo.v4addr, CMSG_DATA(cmsg),
sizeof(struct in_addr));
break;
#endif /* IP_PKTINFO or IP_RECVDSTADDR */
}
}
if(verbosity >= VERB_ALGO)
p_ancil("receive_udp on interface", &rep);
#endif /* S_SPLINT_S */
fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
/* send back immediate reply */
(void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer,
(struct sockaddr*)&rep.addr, rep.addrlen, &rep);
}
if(rep.c->fd == -1) /* commpoint closed */
break;
}
#else
(void)fd;
(void)event;
(void)arg;
fatal_exit("recvmsg: No support for IPV6_PKTINFO; IP_PKTINFO or IP_RECVDSTADDR. "
"Please disable interface-automatic");
#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */
}
void
comm_point_udp_callback(int fd, short event, void* arg)
{
struct comm_reply rep;
ssize_t rcv;
int i;
struct sldns_buffer *buffer;
rep.c = (struct comm_point*)arg;
log_assert(rep.c->type == comm_udp);
if(!(event&UB_EV_READ))
return;
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
ub_comm_base_now(rep.c->ev->base);
for(i=0; i<NUM_UDP_PER_SELECT; i++) {
sldns_buffer_clear(rep.c->buffer);
rep.addrlen = (socklen_t)sizeof(rep.addr);
log_assert(fd != -1);
log_assert(sldns_buffer_remaining(rep.c->buffer) > 0);
rcv = recvfrom(fd, (void*)sldns_buffer_begin(rep.c->buffer),
sldns_buffer_remaining(rep.c->buffer), 0,
(struct sockaddr*)&rep.addr, &rep.addrlen);
if(rcv == -1) {
#ifndef USE_WINSOCK
if(errno != EAGAIN && errno != EINTR)
log_err("recvfrom %d failed: %s",
fd, strerror(errno));
#else
if(WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAECONNRESET &&
WSAGetLastError()!= WSAEWOULDBLOCK)
log_err("recvfrom failed: %s",
wsa_strerror(WSAGetLastError()));
#endif
return;
}
sldns_buffer_skip(rep.c->buffer, rcv);
sldns_buffer_flip(rep.c->buffer);
rep.srctype = 0;
fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
/* send back immediate reply */
#ifdef USE_DNSCRYPT
buffer = rep.c->dnscrypt_buffer;
#else
buffer = rep.c->buffer;
#endif
(void)comm_point_send_udp_msg(rep.c, buffer,
(struct sockaddr*)&rep.addr, rep.addrlen);
}
if(rep.c->fd != fd) /* commpoint closed to -1 or reused for
another UDP port. Note rep.c cannot be reused with TCP fd. */
break;
}
}
/** Use a new tcp handler for new query fd, set to read query */
static void
setup_tcp_handler(struct comm_point* c, int fd, int cur, int max)
{
log_assert(c->type == comm_tcp);
log_assert(c->fd == -1);
sldns_buffer_clear(c->buffer);
#ifdef USE_DNSCRYPT
if (c->dnscrypt)
sldns_buffer_clear(c->dnscrypt_buffer);
#endif
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
c->tcp_timeout_msec = TCP_QUERY_TIMEOUT;
/* if more than half the tcp handlers are in use, use a shorter
* timeout for this TCP connection, we need to make space for
* other connections to be able to get attention */
if(cur > max/2)
c->tcp_timeout_msec = TCP_QUERY_TIMEOUT_FAST;
comm_point_start_listening(c, fd, c->tcp_timeout_msec);
}
void comm_base_handle_slow_accept(int ATTR_UNUSED(fd),
short ATTR_UNUSED(event), void* arg)
{
struct comm_base* b = (struct comm_base*)arg;
/* timeout for the slow accept, re-enable accepts again */
if(b->start_accept) {
verbose(VERB_ALGO, "wait is over, slow accept disabled");
fptr_ok(fptr_whitelist_start_accept(b->start_accept));
(*b->start_accept)(b->cb_arg);
b->eb->slow_accept_enabled = 0;
}
}
int comm_point_perform_accept(struct comm_point* c,
struct sockaddr_storage* addr, socklen_t* addrlen)
{
int new_fd;
*addrlen = (socklen_t)sizeof(*addr);
new_fd = accept(c->fd, (struct sockaddr*)addr, addrlen);
if(new_fd == -1) {
#ifndef USE_WINSOCK
/* EINTR is signal interrupt. others are closed connection. */
if( errno == EINTR || errno == EAGAIN
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
#ifdef ECONNABORTED
|| errno == ECONNABORTED
#endif
#ifdef EPROTO
|| errno == EPROTO
#endif /* EPROTO */
)
return -1;
#if defined(ENFILE) && defined(EMFILE)
if(errno == ENFILE || errno == EMFILE) {
/* out of file descriptors, likely outside of our
* control. stop accept() calls for some time */
if(c->ev->base->stop_accept) {
struct comm_base* b = c->ev->base;
struct timeval tv;
verbose(VERB_ALGO, "out of file descriptors: "
"slow accept");
b->eb->slow_accept_enabled = 1;
fptr_ok(fptr_whitelist_stop_accept(
b->stop_accept));
(*b->stop_accept)(b->cb_arg);
/* set timeout, no mallocs */
tv.tv_sec = NETEVENT_SLOW_ACCEPT_TIME/1000;
tv.tv_usec = (NETEVENT_SLOW_ACCEPT_TIME%1000)*1000;
b->eb->slow_accept = ub_event_new(b->eb->base,
-1, UB_EV_TIMEOUT,
comm_base_handle_slow_accept, b);
if(b->eb->slow_accept == NULL) {
/* we do not want to log here, because
* that would spam the logfiles.
* error: "event_base_set failed." */
}
else if(ub_event_add(b->eb->slow_accept, &tv)
!= 0) {
/* we do not want to log here,
* error: "event_add failed." */
}
}
return -1;
}
#endif
log_err_addr("accept failed", strerror(errno), addr, *addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAECONNRESET)
return -1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return -1;
}
log_err_addr("accept failed", wsa_strerror(WSAGetLastError()),
addr, *addrlen);
#endif
return -1;
}
fd_set_nonblock(new_fd);
return new_fd;
}
#ifdef USE_WINSOCK
static long win_bio_cb(BIO *b, int oper, const char* ATTR_UNUSED(argp),
int ATTR_UNUSED(argi), long argl, long retvalue)
{
verbose(VERB_ALGO, "bio_cb %d, %s %s %s", oper,
(oper&BIO_CB_RETURN)?"return":"before",
(oper&BIO_CB_READ)?"read":((oper&BIO_CB_WRITE)?"write":"other"),
WSAGetLastError()==WSAEWOULDBLOCK?"wsawb":"");
/* on windows, check if previous operation caused EWOULDBLOCK */
if( (oper == (BIO_CB_READ|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_GETS|BIO_CB_RETURN) && argl == 0)) {
if(WSAGetLastError() == WSAEWOULDBLOCK)
ub_winsock_tcp_wouldblock((struct ub_event*)
BIO_get_callback_arg(b), UB_EV_READ);
}
if( (oper == (BIO_CB_WRITE|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_PUTS|BIO_CB_RETURN) && argl == 0)) {
if(WSAGetLastError() == WSAEWOULDBLOCK)
ub_winsock_tcp_wouldblock((struct ub_event*)
BIO_get_callback_arg(b), UB_EV_WRITE);
}
/* return original return value */
return retvalue;
}
/** set win bio callbacks for nonblocking operations */
void
comm_point_tcp_win_bio_cb(struct comm_point* c, void* thessl)
{
SSL* ssl = (SSL*)thessl;
/* set them both just in case, but usually they are the same BIO */
BIO_set_callback(SSL_get_rbio(ssl), &win_bio_cb);
BIO_set_callback_arg(SSL_get_rbio(ssl), (char*)c->ev->ev);
BIO_set_callback(SSL_get_wbio(ssl), &win_bio_cb);
BIO_set_callback_arg(SSL_get_wbio(ssl), (char*)c->ev->ev);
}
#endif
void
comm_point_tcp_accept_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg, *c_hdl;
int new_fd;
log_assert(c->type == comm_tcp_accept);
if(!(event & UB_EV_READ)) {
log_info("ignoring tcp accept event %d", (int)event);
return;
}
ub_comm_base_now(c->ev->base);
/* find free tcp handler. */
if(!c->tcp_free) {
log_warn("accepted too many tcp, connections full");
return;
}
/* accept incoming connection. */
c_hdl = c->tcp_free;
log_assert(fd != -1);
(void)fd;
new_fd = comm_point_perform_accept(c, &c_hdl->repinfo.addr,
&c_hdl->repinfo.addrlen);
if(new_fd == -1)
return;
if(c->ssl) {
c_hdl->ssl = incoming_ssl_fd(c->ssl, new_fd);
if(!c_hdl->ssl) {
c_hdl->fd = new_fd;
comm_point_close(c_hdl);
return;
}
c_hdl->ssl_shake_state = comm_ssl_shake_read;
#ifdef USE_WINSOCK
comm_point_tcp_win_bio_cb(c_hdl, c_hdl->ssl);
#endif
}
/* grab the tcp handler buffers */
c->cur_tcp_count++;
c->tcp_free = c_hdl->tcp_free;
if(!c->tcp_free) {
/* stop accepting incoming queries for now. */
comm_point_stop_listening(c);
}
setup_tcp_handler(c_hdl, new_fd, c->cur_tcp_count, c->max_tcp_count);
}
/** Make tcp handler free for next assignment */
static void
reclaim_tcp_handler(struct comm_point* c)
{
log_assert(c->type == comm_tcp);
if(c->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
c->ssl = NULL;
#endif
}
comm_point_close(c);
if(c->tcp_parent) {
c->tcp_parent->cur_tcp_count--;
c->tcp_free = c->tcp_parent->tcp_free;
c->tcp_parent->tcp_free = c;
if(!c->tcp_free) {
/* re-enable listening on accept socket */
comm_point_start_listening(c->tcp_parent, -1, -1);
}
}
}
/** do the callback when writing is done */
static void
tcp_callback_writer(struct comm_point* c)
{
log_assert(c->type == comm_tcp);
sldns_buffer_clear(c->buffer);
if(c->tcp_do_toggle_rw)
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
/* switch from listening(write) to listening(read) */
comm_point_stop_listening(c);
comm_point_start_listening(c, -1, -1);
}
/** do the callback when reading is done */
static void
tcp_callback_reader(struct comm_point* c)
{
log_assert(c->type == comm_tcp || c->type == comm_local);
sldns_buffer_flip(c->buffer);
if(c->tcp_do_toggle_rw)
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
if(c->type == comm_tcp)
comm_point_stop_listening(c);
fptr_ok(fptr_whitelist_comm_point(c->callback));
if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo) ) {
comm_point_start_listening(c, -1, c->tcp_timeout_msec);
}
}
/** continue ssl handshake */
#ifdef HAVE_SSL
static int
ssl_handshake(struct comm_point* c)
{
int r;
if(c->ssl_shake_state == comm_ssl_shake_hs_read) {
/* read condition satisfied back to writing */
comm_point_listen_for_rw(c, 1, 1);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
if(c->ssl_shake_state == comm_ssl_shake_hs_write) {
/* write condition satisfied, back to reading */
comm_point_listen_for_rw(c, 1, 0);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
ERR_clear_error();
r = SSL_do_handshake(c->ssl);
if(r != 1) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_WANT_READ) {
if(c->ssl_shake_state == comm_ssl_shake_read)
return 1;
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1;
} else if(want == SSL_ERROR_WANT_WRITE) {
if(c->ssl_shake_state == comm_ssl_shake_write)
return 1;
c->ssl_shake_state = comm_ssl_shake_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(r == 0) {
return 0; /* closed */
} else if(want == SSL_ERROR_SYSCALL) {
/* SYSCALL and errno==0 means closed uncleanly */
if(errno != 0)
log_err("SSL_handshake syscall: %s",
strerror(errno));
return 0;
} else {
log_crypto_err("ssl handshake failed");
log_addr(1, "ssl handshake failed", &c->repinfo.addr,
c->repinfo.addrlen);
return 0;
}
}
/* this is where peer verification could take place */
log_addr(VERB_ALGO, "SSL DNS connection", &c->repinfo.addr,
c->repinfo.addrlen);
/* setup listen rw correctly */
if(c->tcp_is_reading) {
if(c->ssl_shake_state != comm_ssl_shake_read)
comm_point_listen_for_rw(c, 1, 0);
} else {
comm_point_listen_for_rw(c, 1, 1);
}
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
#endif /* HAVE_SSL */
/** ssl read callback on TCP */
static int
ssl_handle_read(struct comm_point* c)
{
#ifdef HAVE_SSL
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
if(c->tcp_byte_count < sizeof(uint16_t)) {
/* read length bytes */
ERR_clear_error();
if((r=SSL_read(c->ssl, (void*)sldns_buffer_at(c->buffer,
c->tcp_byte_count), (int)(sizeof(uint16_t) -
c->tcp_byte_count))) <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_read");
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count != sizeof(uint16_t))
return 1;
if(sldns_buffer_read_u16_at(c->buffer, 0) >
sldns_buffer_capacity(c->buffer)) {
verbose(VERB_QUERY, "ssl: dropped larger than buffer");
return 0;
}
sldns_buffer_set_limit(c->buffer,
sldns_buffer_read_u16_at(c->buffer, 0));
if(sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "ssl: dropped bogus too short.");
return 0;
}
verbose(VERB_ALGO, "Reading ssl tcp query of length %d",
(int)sldns_buffer_limit(c->buffer));
}
log_assert(sldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_read(c->ssl, (void*)sldns_buffer_current(c->buffer),
(int)sldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_read");
return 0;
}
sldns_buffer_skip(c->buffer, (ssize_t)r);
if(sldns_buffer_remaining(c->buffer) <= 0) {
tcp_callback_reader(c);
}
return 1;
#else
(void)c;
return 0;
#endif /* HAVE_SSL */
}
/** ssl write callback on TCP */
static int
ssl_handle_write(struct comm_point* c)
{
#ifdef HAVE_SSL
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
/* ignore return, if fails we may simply block */
(void)SSL_set_mode(c->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(sldns_buffer_limit(c->buffer));
ERR_clear_error();
r = SSL_write(c->ssl,
(void*)(((uint8_t*)&len)+c->tcp_byte_count),
(int)(sizeof(uint16_t)-c->tcp_byte_count));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_write");
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
sldns_buffer_set_position(c->buffer, c->tcp_byte_count -
sizeof(uint16_t));
if(sldns_buffer_remaining(c->buffer) == 0) {
tcp_callback_writer(c);
return 1;
}
}
log_assert(sldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_write(c->ssl, (void*)sldns_buffer_current(c->buffer),
(int)sldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_write");
return 0;
}
sldns_buffer_skip(c->buffer, (ssize_t)r);
if(sldns_buffer_remaining(c->buffer) == 0) {
tcp_callback_writer(c);
}
return 1;
#else
(void)c;
return 0;
#endif /* HAVE_SSL */
}
/** handle ssl tcp connection with dns contents */
static int
ssl_handle_it(struct comm_point* c)
{
if(c->tcp_is_reading)
return ssl_handle_read(c);
return ssl_handle_write(c);
}
/** Handle tcp reading callback.
* @param fd: file descriptor of socket.
* @param c: comm point to read from into buffer.
* @param short_ok: if true, very short packets are OK (for comm_local).
* @return: 0 on error
*/
static int
comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
{
ssize_t r;
log_assert(c->type == comm_tcp || c->type == comm_local);
if(c->ssl)
return ssl_handle_it(c);
if(!c->tcp_is_reading)
return 0;
log_assert(fd != -1);
if(c->tcp_byte_count < sizeof(uint16_t)) {
/* read length bytes */
r = recv(fd,(void*)sldns_buffer_at(c->buffer,c->tcp_byte_count),
sizeof(uint16_t)-c->tcp_byte_count, 0);
if(r == 0)
return 0;
else if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
log_err_addr("read (in tcp s)", strerror(errno),
&c->repinfo.addr, c->repinfo.addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return 0;
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev,
UB_EV_READ);
return 1;
}
log_err_addr("read (in tcp s)",
wsa_strerror(WSAGetLastError()),
&c->repinfo.addr, c->repinfo.addrlen);
#endif
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count != sizeof(uint16_t))
return 1;
if(sldns_buffer_read_u16_at(c->buffer, 0) >
sldns_buffer_capacity(c->buffer)) {
verbose(VERB_QUERY, "tcp: dropped larger than buffer");
return 0;
}
sldns_buffer_set_limit(c->buffer,
sldns_buffer_read_u16_at(c->buffer, 0));
if(!short_ok &&
sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "tcp: dropped bogus too short.");
return 0;
}
verbose(VERB_ALGO, "Reading tcp query of length %d",
(int)sldns_buffer_limit(c->buffer));
}
log_assert(sldns_buffer_remaining(c->buffer) > 0);
r = recv(fd, (void*)sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer), 0);
if(r == 0) {
return 0;
} else if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
log_err_addr("read (in tcp r)", strerror(errno),
&c->repinfo.addr, c->repinfo.addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return 0;
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return 1;
}
log_err_addr("read (in tcp r)",
wsa_strerror(WSAGetLastError()),
&c->repinfo.addr, c->repinfo.addrlen);
#endif
return 0;
}
sldns_buffer_skip(c->buffer, r);
if(sldns_buffer_remaining(c->buffer) <= 0) {
tcp_callback_reader(c);
}
return 1;
}
/**
* Handle tcp writing callback.
* @param fd: file descriptor of socket.
* @param c: comm point to write buffer out of.
* @return: 0 on error
*/
static int
comm_point_tcp_handle_write(int fd, struct comm_point* c)
{
ssize_t r;
struct sldns_buffer *buffer;
log_assert(c->type == comm_tcp);
#ifdef USE_DNSCRYPT
buffer = c->dnscrypt_buffer;
#else
buffer = c->buffer;
#endif
if(c->tcp_is_reading && !c->ssl)
return 0;
log_assert(fd != -1);
if(c->tcp_byte_count == 0 && c->tcp_check_nb_connect) {
/* check for pending error from nonblocking connect */
/* from Stevens, unix network programming, vol1, 3rd ed, p450*/
int error = 0;
socklen_t len = (socklen_t)sizeof(error);
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
&len) < 0){
#ifndef USE_WINSOCK
error = errno; /* on solaris errno is error */
#else /* USE_WINSOCK */
error = WSAGetLastError();
#endif
}
#ifndef USE_WINSOCK
#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
if(error == EINPROGRESS || error == EWOULDBLOCK)
return 1; /* try again later */
else
#endif
if(error != 0 && verbosity < 2)
return 0; /* silence lots of chatter in the logs */
else if(error != 0) {
log_err_addr("tcp connect", strerror(error),
&c->repinfo.addr, c->repinfo.addrlen);
#else /* USE_WINSOCK */
/* examine error */
if(error == WSAEINPROGRESS)
return 1;
else if(error == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
} else if(error != 0 && verbosity < 2)
return 0;
else if(error != 0) {
log_err_addr("tcp connect", wsa_strerror(error),
&c->repinfo.addr, c->repinfo.addrlen);
#endif /* USE_WINSOCK */
return 0;
}
}
if(c->ssl)
return ssl_handle_it(c);
#ifdef USE_MSG_FASTOPEN
/* Only try this on first use of a connection that uses tfo,
otherwise fall through to normal write */
/* Also, TFO support on WINDOWS not implemented at the moment */
if(c->tcp_do_fastopen == 1) {
/* this form of sendmsg() does both a connect() and send() so need to
look for various flavours of error*/
uint16_t len = htons(sldns_buffer_limit(buffer));
struct msghdr msg;
struct iovec iov[2];
c->tcp_do_fastopen = 0;
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
iov[1].iov_base = sldns_buffer_begin(buffer);
iov[1].iov_len = sldns_buffer_limit(buffer);
log_assert(iov[0].iov_len > 0);
log_assert(iov[1].iov_len > 0);
msg.msg_name = &c->repinfo.addr;
msg.msg_namelen = c->repinfo.addrlen;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
r = sendmsg(fd, &msg, MSG_FASTOPEN);
if (r == -1) {
#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
/* Handshake is underway, maybe because no TFO cookie available.
Come back to write the messsage*/
if(errno == EINPROGRESS || errno == EWOULDBLOCK)
return 1;
#endif
if(errno == EINTR || errno == EAGAIN)
return 1;
/* Not handling EISCONN here as shouldn't ever hit that case.*/
if(errno != 0 && verbosity < 2)
return 0; /* silence lots of chatter in the logs */
else if(errno != 0)
log_err_addr("tcp sendmsg", strerror(errno),
&c->repinfo.addr, c->repinfo.addrlen);
return 0;
} else {
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
sldns_buffer_set_position(buffer, c->tcp_byte_count -
sizeof(uint16_t));
if(sldns_buffer_remaining(buffer) == 0) {
tcp_callback_writer(c);
return 1;
}
}
}
#endif /* USE_MSG_FASTOPEN */
if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(sldns_buffer_limit(buffer));
#ifdef HAVE_WRITEV
struct iovec iov[2];
iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
iov[1].iov_base = sldns_buffer_begin(buffer);
iov[1].iov_len = sldns_buffer_limit(buffer);
log_assert(iov[0].iov_len > 0);
log_assert(iov[1].iov_len > 0);
r = writev(fd, iov, 2);
#else /* HAVE_WRITEV */
r = send(fd, (void*)(((uint8_t*)&len)+c->tcp_byte_count),
sizeof(uint16_t)-c->tcp_byte_count, 0);
#endif /* HAVE_WRITEV */
if(r == -1) {
#ifndef USE_WINSOCK
# ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return 0; /* silence 'broken pipe' */
#endif
if(errno == EINTR || errno == EAGAIN)
return 1;
# ifdef HAVE_WRITEV
log_err_addr("tcp writev", strerror(errno),
&c->repinfo.addr, c->repinfo.addrlen);
# else /* HAVE_WRITEV */
log_err_addr("tcp send s", strerror(errno),
&c->repinfo.addr, c->repinfo.addrlen);
# endif /* HAVE_WRITEV */
#else
if(WSAGetLastError() == WSAENOTCONN)
return 1;
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev,
UB_EV_WRITE);
return 1;
}
log_err_addr("tcp send s",
wsa_strerror(WSAGetLastError()),
&c->repinfo.addr, c->repinfo.addrlen);
#endif
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
sldns_buffer_set_position(buffer, c->tcp_byte_count -
sizeof(uint16_t));
if(sldns_buffer_remaining(buffer) == 0) {
tcp_callback_writer(c);
return 1;
}
}
log_assert(sldns_buffer_remaining(buffer) > 0);
r = send(fd, (void*)sldns_buffer_current(buffer),
sldns_buffer_remaining(buffer), 0);
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
log_err_addr("tcp send r", strerror(errno),
&c->repinfo.addr, c->repinfo.addrlen);
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
}
log_err_addr("tcp send r", wsa_strerror(WSAGetLastError()),
&c->repinfo.addr, c->repinfo.addrlen);
#endif
return 0;
}
sldns_buffer_skip(buffer, r);
if(sldns_buffer_remaining(buffer) == 0) {
tcp_callback_writer(c);
}
return 1;
}
void
comm_point_tcp_handle_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_tcp);
ub_comm_base_now(c->ev->base);
#ifdef USE_DNSCRYPT
/* Initialize if this is a dnscrypt socket */
if(c->tcp_parent) {
c->dnscrypt = c->tcp_parent->dnscrypt;
}
if(c->dnscrypt && c->dnscrypt_buffer == c->buffer) {
c->dnscrypt_buffer = sldns_buffer_new(sldns_buffer_capacity(c->buffer));
if(!c->dnscrypt_buffer) {
log_err("Could not allocate dnscrypt buffer");
return;
}
}
#endif
if(event&UB_EV_READ) {
if(!comm_point_tcp_handle_read(fd, c, 0)) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
}
return;
}
if(event&UB_EV_WRITE) {
if(!comm_point_tcp_handle_write(fd, c)) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
}
return;
}
if(event&UB_EV_TIMEOUT) {
verbose(VERB_QUERY, "tcp took too long, dropped");
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_TIMEOUT, NULL);
}
return;
}
log_err("Ignored event %d for tcphdl.", event);
}
void comm_point_local_handle_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_local);
ub_comm_base_now(c->ev->base);
if(event&UB_EV_READ) {
if(!comm_point_tcp_handle_read(fd, c, 1)) {
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg, NETEVENT_CLOSED,
NULL);
}
return;
}
log_err("Ignored event %d for localhdl.", event);
}
void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
int err = NETEVENT_NOERROR;
log_assert(c->type == comm_raw);
ub_comm_base_now(c->ev->base);
if(event&UB_EV_TIMEOUT)
err = NETEVENT_TIMEOUT;
fptr_ok(fptr_whitelist_comm_point_raw(c->callback));
(void)(*c->callback)(c, c->cb_arg, err, NULL);
}
struct comm_point*
comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = buffer;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_udp;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = buffer;
#endif
c->inuse = 0;
c->callback = callback;
c->cb_arg = callback_arg;
evbits = UB_EV_READ | UB_EV_PERSIST;
/* ub_event stuff */
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_udp_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset udp event");
comm_point_delete(c);
return NULL;
}
if(fd!=-1 && ub_event_add(c->ev->ev, c->timeout) != 0 ) {
log_err("could not add udp event");
comm_point_delete(c);
return NULL;
}
return c;
}
struct comm_point*
comm_point_create_udp_ancil(struct comm_base *base, int fd,
sldns_buffer* buffer,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = buffer;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_udp;
c->tcp_do_close = 0;
c->do_not_close = 0;
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = buffer;
#endif
c->inuse = 0;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->callback = callback;
c->cb_arg = callback_arg;
evbits = UB_EV_READ | UB_EV_PERSIST;
/* ub_event stuff */
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_udp_ancil_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset udp event");
comm_point_delete(c);
return NULL;
}
if(fd!=-1 && ub_event_add(c->ev->ev, c->timeout) != 0 ) {
log_err("could not add udp event");
comm_point_delete(c);
return NULL;
}
return c;
}
static struct comm_point*
comm_point_create_tcp_handler(struct comm_base *base,
struct comm_point* parent, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = -1;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = (struct timeval*)malloc(sizeof(struct timeval));
if(!c->timeout) {
sldns_buffer_free(c->buffer);
free(c->ev);
free(c);
return NULL;
}
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = parent;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_tcp;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
// We don't know just yet if this is a dnscrypt channel. Allocation
// will be done when handling the callback.
c->dnscrypt_buffer = c->buffer;
#endif
c->repinfo.c = c;
c->callback = callback;
c->cb_arg = callback_arg;
/* add to parent free list */
c->tcp_free = parent->tcp_free;
parent->tcp_free = c;
/* ub_event stuff */
evbits = UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_tcp_handle_callback, c);
if(c->ev->ev == NULL)
{
log_err("could not basetset tcphdl event");
parent->tcp_free = c->tcp_free;
free(c->ev);
free(c);
return NULL;
}
return c;
}
struct comm_point*
comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
int i;
/* first allocate the TCP accept listener */
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = NULL;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = num;
c->cur_tcp_count = 0;
c->tcp_handlers = (struct comm_point**)calloc((size_t)num,
sizeof(struct comm_point*));
if(!c->tcp_handlers) {
free(c->ev);
free(c);
return NULL;
}
c->tcp_free = NULL;
c->type = comm_tcp_accept;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = NULL;
#endif
c->callback = NULL;
c->cb_arg = NULL;
evbits = UB_EV_READ | UB_EV_PERSIST;
/* ub_event stuff */
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_tcp_accept_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset tcpacc event");
comm_point_delete(c);
return NULL;
}
if (ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("could not add tcpacc event");
comm_point_delete(c);
return NULL;
}
/* now prealloc the tcp handlers */
for(i=0; i<num; i++) {
c->tcp_handlers[i] = comm_point_create_tcp_handler(base,
c, bufsize, callback, callback_arg);
if(!c->tcp_handlers[i]) {
comm_point_delete(c);
return NULL;
}
}
return c;
}
struct comm_point*
comm_point_create_tcp_out(struct comm_base *base, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = -1;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_tcp;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 1;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 1;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->repinfo.c = c;
c->callback = callback;
c->cb_arg = callback_arg;
evbits = UB_EV_PERSIST | UB_EV_WRITE;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_tcp_handle_callback, c);
if(c->ev->ev == NULL)
{
log_err("could not baseset tcpout event");
sldns_buffer_free(c->buffer);
free(c->ev);
free(c);
return NULL;
}
return c;
}
struct comm_point*
comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = NULL;
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_local;
c->tcp_do_close = 0;
c->do_not_close = 1;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->callback = callback;
c->cb_arg = callback_arg;
/* ub_event stuff */
evbits = UB_EV_PERSIST | UB_EV_READ;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_local_handle_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset localhdl event");
free(c->ev);
free(c);
return NULL;
}
if (ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("could not add localhdl event");
ub_event_free(c->ev->ev);
free(c->ev);
free(c);
return NULL;
}
return c;
}
struct comm_point*
comm_point_create_raw(struct comm_base* base, int fd, int writing,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = NULL;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_raw;
c->tcp_do_close = 0;
c->do_not_close = 1;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->callback = callback;
c->cb_arg = callback_arg;
/* ub_event stuff */
if(writing)
evbits = UB_EV_PERSIST | UB_EV_WRITE;
else evbits = UB_EV_PERSIST | UB_EV_READ;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_raw_handle_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset rawhdl event");
free(c->ev);
free(c);
return NULL;
}
if (ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("could not add rawhdl event");
ub_event_free(c->ev->ev);
free(c->ev);
free(c);
return NULL;
}
return c;
}
void
comm_point_close(struct comm_point* c)
{
if(!c)
return;
if(c->fd != -1)
if(ub_event_del(c->ev->ev) != 0) {
log_err("could not event_del on close");
}
/* close fd after removing from event lists, or epoll.. is messed up */
if(c->fd != -1 && !c->do_not_close) {
verbose(VERB_ALGO, "close fd %d", c->fd);
#ifndef USE_WINSOCK
close(c->fd);
#else
closesocket(c->fd);
#endif
}
c->fd = -1;
}
void
comm_point_delete(struct comm_point* c)
{
if(!c)
return;
if(c->type == comm_tcp && c->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
#endif
}
comm_point_close(c);
if(c->tcp_handlers) {
int i;
for(i=0; i<c->max_tcp_count; i++)
comm_point_delete(c->tcp_handlers[i]);
free(c->tcp_handlers);
}
free(c->timeout);
if(c->type == comm_tcp || c->type == comm_local) {
sldns_buffer_free(c->buffer);
#ifdef USE_DNSCRYPT
if(c->dnscrypt && c->dnscrypt_buffer != c->buffer) {
sldns_buffer_free(c->dnscrypt_buffer);
}
#endif
}
ub_event_free(c->ev->ev);
free(c->ev);
free(c);
}
void
comm_point_send_reply(struct comm_reply *repinfo)
{
struct sldns_buffer* buffer;
log_assert(repinfo && repinfo->c);
#ifdef USE_DNSCRYPT
buffer = repinfo->c->dnscrypt_buffer;
if(!dnsc_handle_uncurved_request(repinfo)) {
return;
}
#else
buffer = repinfo->c->buffer;
#endif
if(repinfo->c->type == comm_udp) {
if(repinfo->srctype)
comm_point_send_udp_msg_if(repinfo->c,
buffer, (struct sockaddr*)&repinfo->addr,
repinfo->addrlen, repinfo);
else
comm_point_send_udp_msg(repinfo->c, buffer,
(struct sockaddr*)&repinfo->addr, repinfo->addrlen);
#ifdef USE_DNSTAP
if(repinfo->c->dtenv != NULL &&
repinfo->c->dtenv->log_client_response_messages)
dt_msg_send_client_response(repinfo->c->dtenv,
&repinfo->addr, repinfo->c->type, repinfo->c->buffer);
#endif
} else {
#ifdef USE_DNSTAP
if(repinfo->c->tcp_parent->dtenv != NULL &&
repinfo->c->tcp_parent->dtenv->log_client_response_messages)
dt_msg_send_client_response(repinfo->c->tcp_parent->dtenv,
&repinfo->addr, repinfo->c->type, repinfo->c->buffer);
#endif
comm_point_start_listening(repinfo->c, -1,
repinfo->c->tcp_timeout_msec);
}
}
void
comm_point_drop_reply(struct comm_reply* repinfo)
{
if(!repinfo)
return;
log_assert(repinfo && repinfo->c);
log_assert(repinfo->c->type != comm_tcp_accept);
if(repinfo->c->type == comm_udp)
return;
reclaim_tcp_handler(repinfo->c);
}
void
comm_point_stop_listening(struct comm_point* c)
{
verbose(VERB_ALGO, "comm point stop listening %d", c->fd);
if(ub_event_del(c->ev->ev) != 0) {
log_err("event_del error to stoplisten");
}
}
void
comm_point_start_listening(struct comm_point* c, int newfd, int msec)
{
verbose(VERB_ALGO, "comm point start listening %d",
c->fd==-1?newfd:c->fd);
if(c->type == comm_tcp_accept && !c->tcp_free) {
/* no use to start listening no free slots. */
return;
}
if(msec != -1 && msec != 0) {
if(!c->timeout) {
c->timeout = (struct timeval*)malloc(sizeof(
struct timeval));
if(!c->timeout) {
log_err("cpsl: malloc failed. No net read.");
return;
}
}
ub_event_add_bits(c->ev->ev, UB_EV_TIMEOUT);
#ifndef S_SPLINT_S /* splint fails on struct timeval. */
c->timeout->tv_sec = msec/1000;
c->timeout->tv_usec = (msec%1000)*1000;
#endif /* S_SPLINT_S */
}
if(c->type == comm_tcp) {
ub_event_del_bits(c->ev->ev, UB_EV_READ|UB_EV_WRITE);
if(c->tcp_is_reading)
ub_event_add_bits(c->ev->ev, UB_EV_READ);
else ub_event_add_bits(c->ev->ev, UB_EV_WRITE);
}
if(newfd != -1) {
if(c->fd != -1) {
#ifndef USE_WINSOCK
close(c->fd);
#else
closesocket(c->fd);
#endif
}
c->fd = newfd;
ub_event_set_fd(c->ev->ev, c->fd);
}
if(ub_event_add(c->ev->ev, msec==0?NULL:c->timeout) != 0) {
log_err("event_add failed. in cpsl.");
}
}
void comm_point_listen_for_rw(struct comm_point* c, int rd, int wr)
{
verbose(VERB_ALGO, "comm point listen_for_rw %d %d", c->fd, wr);
if(ub_event_del(c->ev->ev) != 0) {
log_err("event_del error to cplf");
}
ub_event_del_bits(c->ev->ev, UB_EV_READ|UB_EV_WRITE);
if(rd) ub_event_add_bits(c->ev->ev, UB_EV_READ);
if(wr) ub_event_add_bits(c->ev->ev, UB_EV_WRITE);
if(ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("event_add failed. in cplf.");
}
}
size_t comm_point_get_mem(struct comm_point* c)
{
size_t s;
if(!c)
return 0;
s = sizeof(*c) + sizeof(*c->ev);
if(c->timeout)
s += sizeof(*c->timeout);
if(c->type == comm_tcp || c->type == comm_local) {
s += sizeof(*c->buffer) + sldns_buffer_capacity(c->buffer);
#ifdef USE_DNSCRYPT
s += sizeof(*c->dnscrypt_buffer);
if(c->buffer != c->dnscrypt_buffer) {
s += sldns_buffer_capacity(c->dnscrypt_buffer);
}
#endif
}
if(c->type == comm_tcp_accept) {
int i;
for(i=0; i<c->max_tcp_count; i++)
s += comm_point_get_mem(c->tcp_handlers[i]);
}
return s;
}
struct comm_timer*
comm_timer_create(struct comm_base* base, void (*cb)(void*), void* cb_arg)
{
struct internal_timer *tm = (struct internal_timer*)calloc(1,
sizeof(struct internal_timer));
if(!tm) {
log_err("malloc failed");
return NULL;
}
tm->super.ev_timer = tm;
tm->base = base;
tm->super.callback = cb;
tm->super.cb_arg = cb_arg;
tm->ev = ub_event_new(base->eb->base, -1, UB_EV_TIMEOUT,
comm_timer_callback, &tm->super);
if(tm->ev == NULL) {
log_err("timer_create: event_base_set failed.");
free(tm);
return NULL;
}
return &tm->super;
}
void
comm_timer_disable(struct comm_timer* timer)
{
if(!timer)
return;
ub_timer_del(timer->ev_timer->ev);
timer->ev_timer->enabled = 0;
}
void
comm_timer_set(struct comm_timer* timer, struct timeval* tv)
{
log_assert(tv);
if(timer->ev_timer->enabled)
comm_timer_disable(timer);
if(ub_timer_add(timer->ev_timer->ev, timer->ev_timer->base->eb->base,
comm_timer_callback, timer, tv) != 0)
log_err("comm_timer_set: evtimer_add failed.");
timer->ev_timer->enabled = 1;
}
void
comm_timer_delete(struct comm_timer* timer)
{
if(!timer)
return;
comm_timer_disable(timer);
/* Free the sub struct timer->ev_timer derived from the super struct timer.
* i.e. assert(timer == timer->ev_timer)
*/
ub_event_free(timer->ev_timer->ev);
free(timer->ev_timer);
}
void
comm_timer_callback(int ATTR_UNUSED(fd), short event, void* arg)
{
struct comm_timer* tm = (struct comm_timer*)arg;
if(!(event&UB_EV_TIMEOUT))
return;
ub_comm_base_now(tm->ev_timer->base);
tm->ev_timer->enabled = 0;
fptr_ok(fptr_whitelist_comm_timer(tm->callback));
(*tm->callback)(tm->cb_arg);
}
int
comm_timer_is_set(struct comm_timer* timer)
{
return (int)timer->ev_timer->enabled;
}
size_t
comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer))
{
return sizeof(struct internal_timer);
}
struct comm_signal*
comm_signal_create(struct comm_base* base,
void (*callback)(int, void*), void* cb_arg)
{
struct comm_signal* com = (struct comm_signal*)malloc(
sizeof(struct comm_signal));
if(!com) {
log_err("malloc failed");
return NULL;
}
com->base = base;
com->callback = callback;
com->cb_arg = cb_arg;
com->ev_signal = NULL;
return com;
}
void
comm_signal_callback(int sig, short event, void* arg)
{
struct comm_signal* comsig = (struct comm_signal*)arg;
if(!(event & UB_EV_SIGNAL))
return;
ub_comm_base_now(comsig->base);
fptr_ok(fptr_whitelist_comm_signal(comsig->callback));
(*comsig->callback)(sig, comsig->cb_arg);
}
int
comm_signal_bind(struct comm_signal* comsig, int sig)
{
struct internal_signal* entry = (struct internal_signal*)calloc(1,
sizeof(struct internal_signal));
if(!entry) {
log_err("malloc failed");
return 0;
}
log_assert(comsig);
/* add signal event */
entry->ev = ub_signal_new(comsig->base->eb->base, sig,
comm_signal_callback, comsig);
if(entry->ev == NULL) {
log_err("Could not create signal event");
free(entry);
return 0;
}
if(ub_signal_add(entry->ev, NULL) != 0) {
log_err("Could not add signal handler");
ub_event_free(entry->ev);
free(entry);
return 0;
}
/* link into list */
entry->next = comsig->ev_signal;
comsig->ev_signal = entry;
return 1;
}
void
comm_signal_delete(struct comm_signal* comsig)
{
struct internal_signal* p, *np;
if(!comsig)
return;
p=comsig->ev_signal;
while(p) {
np = p->next;
ub_signal_del(p->ev);
ub_event_free(p->ev);
free(p);
p = np;
}
free(comsig);
}