danicoin/external/unbound/services/listen_dnsport.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

1436 lines
38 KiB
C

/*
* services/listen_dnsport.c - listen on port 53 for incoming DNS queries.
*
* 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 has functions to get queries from clients.
*/
#include "config.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <sys/time.h>
#ifdef USE_TCP_FASTOPEN
#include <netinet/tcp.h>
#endif
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "util/netevent.h"
#include "util/log.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "sldns/sbuffer.h"
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <fcntl.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
/** number of queued TCP connections for listen() */
#define TCP_BACKLOG 256
/**
* Debug print of the getaddrinfo returned address.
* @param addr: the address returned.
*/
static void
verbose_print_addr(struct addrinfo *addr)
{
if(verbosity >= VERB_ALGO) {
char buf[100];
void* sinaddr = &((struct sockaddr_in*)addr->ai_addr)->sin_addr;
#ifdef INET6
if(addr->ai_family == AF_INET6)
sinaddr = &((struct sockaddr_in6*)addr->ai_addr)->
sin6_addr;
#endif /* INET6 */
if(inet_ntop(addr->ai_family, sinaddr, buf,
(socklen_t)sizeof(buf)) == 0) {
(void)strlcpy(buf, "(null)", sizeof(buf));
}
buf[sizeof(buf)-1] = 0;
verbose(VERB_ALGO, "creating %s%s socket %s %d",
addr->ai_socktype==SOCK_DGRAM?"udp":
addr->ai_socktype==SOCK_STREAM?"tcp":"otherproto",
addr->ai_family==AF_INET?"4":
addr->ai_family==AF_INET6?"6":
"_otherfam", buf,
ntohs(((struct sockaddr_in*)addr->ai_addr)->sin_port));
}
}
#ifdef HAVE_SYSTEMD
static int
systemd_get_activated(int family, int socktype, int listen,
struct sockaddr *addr, socklen_t addrlen,
const char *path)
{
int i = 0;
int r = 0;
int s = -1;
const char* listen_pid, *listen_fds;
/* We should use "listen" option only for stream protocols. For UDP it should be -1 */
if((r = sd_booted()) < 1) {
if(r == 0)
log_warn("systemd is not running");
else
log_err("systemd sd_booted(): %s", strerror(-r));
return -1;
}
listen_pid = getenv("LISTEN_PID");
listen_fds = getenv("LISTEN_FDS");
if (!listen_pid) {
log_warn("Systemd mandatory ENV variable is not defined: LISTEN_PID");
return -1;
}
if (!listen_fds) {
log_warn("Systemd mandatory ENV variable is not defined: LISTEN_FDS");
return -1;
}
if((r = sd_listen_fds(0)) < 1) {
if(r == 0)
log_warn("systemd: did not return socket, check unit configuration");
else
log_err("systemd sd_listen_fds(): %s", strerror(-r));
return -1;
}
for(i = 0; i < r; i++) {
if(sd_is_socket(SD_LISTEN_FDS_START + i, family, socktype, listen)) {
s = SD_LISTEN_FDS_START + i;
break;
}
}
if (s == -1) {
if (addr)
log_err_addr("systemd sd_listen_fds()",
"no such socket",
(struct sockaddr_storage *)addr, addrlen);
else
log_err("systemd sd_listen_fds(): %s", path);
}
return s;
}
#endif
int
create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t addrlen, int v6only, int* inuse, int* noproto,
int rcv, int snd, int listen, int* reuseport, int transparent,
int freebind, int use_systemd)
{
int s;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND)
int on=1;
#endif
#ifdef IPV6_MTU
int mtu = IPV6_MIN_MTU;
#endif
#if !defined(SO_RCVBUFFORCE) && !defined(SO_RCVBUF)
(void)rcv;
#endif
#if !defined(SO_SNDBUFFORCE) && !defined(SO_SNDBUF)
(void)snd;
#endif
#ifndef IPV6_V6ONLY
(void)v6only;
#endif
#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY)
(void)transparent;
#endif
#if !defined(IP_FREEBIND)
(void)freebind;
#endif
#ifdef HAVE_SYSTEMD
int got_fd_from_systemd = 0;
if (!use_systemd
|| (use_systemd
&& (s = systemd_get_activated(family, socktype, -1, addr,
addrlen, NULL)) == -1)) {
#else
(void)use_systemd;
#endif
if((s = socket(family, socktype, 0)) == -1) {
*inuse = 0;
#ifndef USE_WINSOCK
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
log_err("can't create socket: %s", strerror(errno));
#else
if(WSAGetLastError() == WSAEAFNOSUPPORT ||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
log_err("can't create socket: %s",
wsa_strerror(WSAGetLastError()));
#endif
*noproto = 0;
return -1;
}
#ifdef HAVE_SYSTEMD
} else {
got_fd_from_systemd = 1;
}
#endif
if(listen) {
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifndef USE_WINSOCK
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
strerror(errno));
if(errno != ENOSYS) {
close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
#else
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
*noproto = 0;
*inuse = 0;
return -1;
#endif
}
#endif /* SO_REUSEADDR */
#ifdef SO_REUSEPORT
/* try to set SO_REUSEPORT so that incoming
* queries are distributed evenly among the receiving threads.
* Each thread must have its own socket bound to the same port,
* with SO_REUSEPORT set on each socket.
*/
if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifdef ENOPROTOOPT
if(errno != ENOPROTOOPT || verbosity >= 3)
log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s",
strerror(errno));
#endif
/* this option is not essential, we can continue */
*reuseport = 0;
}
#else
(void)reuseport;
#endif /* defined(SO_REUSEPORT) */
#ifdef IP_TRANSPARENT
if (transparent &&
setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s",
strerror(errno));
}
#elif defined(IP_BINDANY)
if (transparent &&
setsockopt(s, (family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP),
(family == AF_INET6? IPV6_BINDANY:IP_BINDANY),
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s",
(family==AF_INET6?"V6":""), strerror(errno));
}
#endif /* IP_TRANSPARENT || IP_BINDANY */
}
#ifdef IP_FREEBIND
if(freebind &&
setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
strerror(errno));
}
#endif /* IP_FREEBIND */
if(rcv) {
#ifdef SO_RCVBUF
int got;
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_RCVBUFFORCE
/* Linux specific: try to use root permission to override
* system limits on rcvbuf. The limit is stored in
* /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */
if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
if(errno != EPERM) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., SO_RCVBUFFORCE, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., SO_RCVBUFFORCE, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* SO_RCVBUFFORCE */
if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got,
&slen) >= 0 && got < rcv/2) {
log_warn("so-rcvbuf %u was not granted. "
"Got %u. To fix: start with "
"root permissions(linux) or sysctl "
"bigger net.core.rmem_max(linux) or "
"kern.ipc.maxsockbuf(bsd) values.",
(unsigned)rcv, (unsigned)got);
}
# ifdef SO_RCVBUFFORCE
}
# endif
#endif /* SO_RCVBUF */
}
/* first do RCVBUF as the receive buffer is more important */
if(snd) {
#ifdef SO_SNDBUF
int got;
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_SNDBUFFORCE
/* Linux specific: try to use root permission to override
* system limits on sndbuf. The limit is stored in
* /proc/sys/net/core/wmem_max or sysctl net.core.wmem_max */
if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
if(errno != EPERM) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., SO_SNDBUFFORCE, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., SO_SNDBUFFORCE, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* SO_SNDBUFFORCE */
if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., SO_SNDBUF, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., SO_SNDBUF, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got,
&slen) >= 0 && got < snd/2) {
log_warn("so-sndbuf %u was not granted. "
"Got %u. To fix: start with "
"root permissions(linux) or sysctl "
"bigger net.core.wmem_max(linux) or "
"kern.ipc.maxsockbuf(bsd) values.",
(unsigned)snd, (unsigned)got);
}
# ifdef SO_SNDBUFFORCE
}
# endif
#endif /* SO_SNDBUF */
}
if(family == AF_INET6) {
# if defined(IPV6_V6ONLY)
if(v6only) {
int val=(v6only==2)?0:1;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&val, (socklen_t)sizeof(val)) < 0) {
#ifndef USE_WINSOCK
log_err("setsockopt(..., IPV6_V6ONLY"
", ...) failed: %s", strerror(errno));
close(s);
#else
log_err("setsockopt(..., IPV6_V6ONLY"
", ...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
#endif
*noproto = 0;
*inuse = 0;
return -1;
}
}
# endif
# if defined(IPV6_USE_MIN_MTU)
/*
* There is no fragmentation of IPv6 datagrams
* during forwarding in the network. Therefore
* we do not send UDP datagrams larger than
* the minimum IPv6 MTU of 1280 octets. The
* EDNS0 message length can be larger if the
* network stack supports IPV6_USE_MIN_MTU.
*/
if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
# elif defined(IPV6_MTU)
/*
* On Linux, to send no larger than 1280, the PMTUD is
* disabled by default for datagrams anyway, so we set
* the MTU to use.
*/
if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU,
(void*)&mtu, (socklen_t)sizeof(mtu)) < 0) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., IPV6_MTU, ...) failed: %s",
strerror(errno));
close(s);
# else
log_err("setsockopt(..., IPV6_MTU, ...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* IPv6 MTU */
} else if(family == AF_INET) {
# if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
/* linux 3.15 has IP_PMTUDISC_OMIT, Hannes Frederic Sowa made it so that
* PMTU information is not accepted, but fragmentation is allowed
* if and only if the packet size exceeds the outgoing interface MTU
* (and also uses the interface mtu to determine the size of the packets).
* So there won't be any EMSGSIZE error. Against DNS fragmentation attacks.
* FreeBSD already has same semantics without setting the option. */
int omit_set = 0;
int action;
# if defined(IP_PMTUDISC_OMIT)
action = IP_PMTUDISC_OMIT;
if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
if (errno != EINVAL) {
log_err("setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_OMIT...) failed: %s",
strerror(errno));
# ifndef USE_WINSOCK
close(s);
# else
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
}
else
{
omit_set = 1;
}
# endif
if (omit_set == 0) {
action = IP_PMTUDISC_DONT;
if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
log_err("setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s",
strerror(errno));
# ifndef USE_WINSOCK
close(s);
# else
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
}
# elif defined(IP_DONTFRAG)
int off = 0;
if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
&off, (socklen_t)sizeof(off)) < 0) {
log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s",
strerror(errno));
# ifndef USE_WINSOCK
close(s);
# else
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* IPv4 MTU */
}
if(
#ifdef HAVE_SYSTEMD
!got_fd_from_systemd &&
#endif
bind(s, (struct sockaddr*)addr, addrlen) != 0) {
*noproto = 0;
*inuse = 0;
#ifndef USE_WINSOCK
#ifdef EADDRINUSE
*inuse = (errno == EADDRINUSE);
/* detect freebsd jail with no ipv6 permission */
if(family==AF_INET6 && errno==EINVAL)
*noproto = 1;
else if(errno != EADDRINUSE) {
log_err_addr("can't bind socket", strerror(errno),
(struct sockaddr_storage*)addr, addrlen);
}
#endif /* EADDRINUSE */
close(s);
#else /* USE_WINSOCK */
if(WSAGetLastError() != WSAEADDRINUSE &&
WSAGetLastError() != WSAEADDRNOTAVAIL) {
log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr, addrlen);
}
closesocket(s);
#endif /* USE_WINSOCK */
return -1;
}
if(!fd_set_nonblock(s)) {
*noproto = 0;
*inuse = 0;
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return -1;
}
return s;
}
int
create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
int* reuseport, int transparent, int mss, int freebind, int use_systemd)
{
int s;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND)
int on = 1;
#endif
#ifdef HAVE_SYSTEMD
int got_fd_from_systemd = 0;
#endif
#ifdef USE_TCP_FASTOPEN
int qlen;
#endif
#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY)
(void)transparent;
#endif
#if !defined(IP_FREEBIND)
(void)freebind;
#endif
verbose_print_addr(addr);
*noproto = 0;
#ifdef HAVE_SYSTEMD
if (!use_systemd ||
(use_systemd
&& (s = systemd_get_activated(addr->ai_family, addr->ai_socktype, 1,
addr->ai_addr, addr->ai_addrlen,
NULL)) == -1)) {
#else
(void)use_systemd;
#endif
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
#ifndef USE_WINSOCK
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
log_err("can't create socket: %s", strerror(errno));
#else
if(WSAGetLastError() == WSAEAFNOSUPPORT ||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
log_err("can't create socket: %s",
wsa_strerror(WSAGetLastError()));
#endif
return -1;
}
if (mss > 0) {
#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, (void*)&mss,
(socklen_t)sizeof(mss)) < 0) {
#ifndef USE_WINSOCK
log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s",
strerror(errno));
#else
log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s",
wsa_strerror(WSAGetLastError()));
#endif
} else {
verbose(VERB_ALGO,
" tcp socket mss set to %d", mss);
}
#else
log_warn(" setsockopt(TCP_MAXSEG) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
}
#ifdef HAVE_SYSTEMD
} else {
got_fd_from_systemd = 1;
}
#endif
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifndef USE_WINSOCK
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
strerror(errno));
close(s);
#else
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
#endif
return -1;
}
#endif /* SO_REUSEADDR */
#ifdef IP_FREEBIND
if (freebind && setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
strerror(errno));
}
#endif /* IP_FREEBIND */
#ifdef SO_REUSEPORT
/* try to set SO_REUSEPORT so that incoming
* connections are distributed evenly among the receiving threads.
* Each thread must have its own socket bound to the same port,
* with SO_REUSEPORT set on each socket.
*/
if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifdef ENOPROTOOPT
if(errno != ENOPROTOOPT || verbosity >= 3)
log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s",
strerror(errno));
#endif
/* this option is not essential, we can continue */
*reuseport = 0;
}
#else
(void)reuseport;
#endif /* defined(SO_REUSEPORT) */
#if defined(IPV6_V6ONLY)
if(addr->ai_family == AF_INET6 && v6only) {
if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
#ifndef USE_WINSOCK
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
strerror(errno));
close(s);
#else
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
#endif
return -1;
}
}
#else
(void)v6only;
#endif /* IPV6_V6ONLY */
#ifdef IP_TRANSPARENT
if (transparent &&
setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s",
strerror(errno));
}
#elif defined(IP_BINDANY)
if (transparent &&
setsockopt(s, (addr->ai_family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP),
(addr->ai_family == AF_INET6? IPV6_BINDANY:IP_BINDANY),
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s",
(addr->ai_family==AF_INET6?"V6":""), strerror(errno));
}
#endif /* IP_TRANSPARENT || IP_BINDANY */
if(
#ifdef HAVE_SYSTEMD
!got_fd_from_systemd &&
#endif
bind(s, addr->ai_addr, addr->ai_addrlen) != 0) {
#ifndef USE_WINSOCK
/* detect freebsd jail with no ipv6 permission */
if(addr->ai_family==AF_INET6 && errno==EINVAL)
*noproto = 1;
else {
log_err_addr("can't bind socket", strerror(errno),
(struct sockaddr_storage*)addr->ai_addr,
addr->ai_addrlen);
}
close(s);
#else
log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr->ai_addr,
addr->ai_addrlen);
closesocket(s);
#endif
return -1;
}
if(!fd_set_nonblock(s)) {
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return -1;
}
if(listen(s, TCP_BACKLOG) == -1) {
#ifndef USE_WINSOCK
log_err("can't listen: %s", strerror(errno));
close(s);
#else
log_err("can't listen: %s", wsa_strerror(WSAGetLastError()));
closesocket(s);
#endif
return -1;
}
#ifdef USE_TCP_FASTOPEN
/* qlen specifies how many outstanding TFO requests to allow. Limit is a defense
against IP spoofing attacks as suggested in RFC7413 */
#ifdef __APPLE__
/* OS X implementation only supports qlen of 1 via this call. Actual
value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */
qlen = 1;
#else
/* 5 is recommended on linux */
qlen = 5;
#endif
if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
sizeof(qlen))) == -1 ) {
log_err("Setting TCP Fast Open as server failed: %s", strerror(errno));
}
#endif
return s;
}
int
create_local_accept_sock(const char *path, int* noproto, int use_systemd)
{
#ifdef HAVE_SYSTEMD
int ret;
if (use_systemd && (ret = systemd_get_activated(AF_LOCAL, SOCK_STREAM, 1, NULL, 0, path)) != -1)
return ret;
else {
#endif
#ifdef HAVE_SYS_UN_H
int s;
struct sockaddr_un usock;
#ifndef HAVE_SYSTEMD
(void)use_systemd;
#endif
verbose(VERB_ALGO, "creating unix socket %s", path);
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
/* this member exists on BSDs, not Linux */
usock.sun_len = (unsigned)sizeof(usock);
#endif
usock.sun_family = AF_LOCAL;
/* length is 92-108, 104 on FreeBSD */
(void)strlcpy(usock.sun_path, path, sizeof(usock.sun_path));
if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
log_err("Cannot create local socket %s (%s)",
path, strerror(errno));
return -1;
}
if (unlink(path) && errno != ENOENT) {
/* The socket already exists and cannot be removed */
log_err("Cannot remove old local socket %s (%s)",
path, strerror(errno));
goto err;
}
if (bind(s, (struct sockaddr *)&usock,
(socklen_t)sizeof(struct sockaddr_un)) == -1) {
log_err("Cannot bind local socket %s (%s)",
path, strerror(errno));
goto err;
}
if (!fd_set_nonblock(s)) {
log_err("Cannot set non-blocking mode");
goto err;
}
if (listen(s, TCP_BACKLOG) == -1) {
log_err("can't listen: %s", strerror(errno));
goto err;
}
(void)noproto; /*unused*/
return s;
err:
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return -1;
#ifdef HAVE_SYSTEMD
}
#endif
#else
(void)use_systemd;
(void)path;
log_err("Local sockets are not supported");
*noproto = 1;
return -1;
#endif
}
/**
* Create socket from getaddrinfo results
*/
static int
make_sock(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd)
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
hints->ai_socktype = stype;
*noip6 = 0;
if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
#ifdef USE_WINSOCK
if(r == EAI_NONAME && hints->ai_family == AF_INET6){
*noip6 = 1; /* 'Host not found' for IP6 on winXP */
return -1;
}
#endif
log_err("node %s:%s getaddrinfo: %s %s",
ifname?ifname:"default", port, gai_strerror(r),
#ifdef EAI_SYSTEM
r==EAI_SYSTEM?(char*)strerror(errno):""
#else
""
#endif
);
return -1;
}
if(stype == SOCK_DGRAM) {
verbose_print_addr(res);
s = create_udp_sock(res->ai_family, res->ai_socktype,
(struct sockaddr*)res->ai_addr, res->ai_addrlen,
v6only, &inuse, &noproto, (int)rcv, (int)snd, 1,
reuseport, transparent, freebind, use_systemd);
if(s == -1 && inuse) {
log_err("bind: address already in use");
} else if(s == -1 && noproto && hints->ai_family == AF_INET6){
*noip6 = 1;
}
} else {
s = create_tcp_accept_sock(res, v6only, &noproto, reuseport,
transparent, tcp_mss, freebind, use_systemd);
if(s == -1 && noproto && hints->ai_family == AF_INET6){
*noip6 = 1;
}
}
freeaddrinfo(res);
return s;
}
/** make socket and first see if ifname contains port override info */
static int
make_sock_port(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd)
{
char* s = strchr(ifname, '@');
if(s) {
/* override port with ifspec@port */
char p[16];
char newif[128];
if((size_t)(s-ifname) >= sizeof(newif)) {
log_err("ifname too long: %s", ifname);
*noip6 = 0;
return -1;
}
if(strlen(s+1) >= sizeof(p)) {
log_err("portnumber too long: %s", ifname);
*noip6 = 0;
return -1;
}
(void)strlcpy(newif, ifname, sizeof(newif));
newif[s-ifname] = 0;
(void)strlcpy(p, s+1, sizeof(p));
p[strlen(s+1)]=0;
return make_sock(stype, newif, p, hints, v6only, noip6,
rcv, snd, reuseport, transparent, tcp_mss, freebind, use_systemd);
}
return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd,
reuseport, transparent, tcp_mss, freebind, use_systemd);
}
/**
* Add port to open ports list.
* @param list: list head. changed.
* @param s: fd.
* @param ftype: if fd is UDP.
* @return false on failure. list in unchanged then.
*/
static int
port_insert(struct listen_port** list, int s, enum listen_type ftype)
{
struct listen_port* item = (struct listen_port*)malloc(
sizeof(struct listen_port));
if(!item)
return 0;
item->next = *list;
item->fd = s;
item->ftype = ftype;
*list = item;
return 1;
}
/** set fd to receive source address packet info */
static int
set_recvpktinfo(int s, int family)
{
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)) || defined(IP_PKTINFO)
int on = 1;
#else
(void)s;
#endif
if(family == AF_INET6) {
# ifdef IPV6_RECVPKTINFO
if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_RECVPKTINFO, ...) failed: %s",
strerror(errno));
return 0;
}
# elif defined(IPV6_PKTINFO)
if(setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_PKTINFO, ...) failed: %s",
strerror(errno));
return 0;
}
# else
log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please "
"disable interface-automatic or do-ip6 in config");
return 0;
# endif /* defined IPV6_RECVPKTINFO */
} else if(family == AF_INET) {
# ifdef IP_PKTINFO
if(setsockopt(s, IPPROTO_IP, IP_PKTINFO,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s",
strerror(errno));
return 0;
}
# elif defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)
if(setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IP_RECVDSTADDR, ...) failed: %s",
strerror(errno));
return 0;
}
# else
log_err("no IP_SENDSRCADDR or IP_PKTINFO option, please disable "
"interface-automatic or do-ip4 in config");
return 0;
# endif /* IP_PKTINFO */
}
return 1;
}
/**
* Helper for ports_open. Creates one interface (or NULL for default).
* @param ifname: The interface ip address.
* @param do_auto: use automatic interface detection.
* If enabled, then ifname must be the wildcard name.
* @param do_udp: if udp should be used.
* @param do_tcp: if udp should be used.
* @param hints: for getaddrinfo. family and flags have to be set by caller.
* @param port: Port number to use (as string).
* @param list: list of open ports, appended to, changed to point to list head.
* @param rcv: receive buffer size for UDP
* @param snd: send buffer size for UDP
* @param ssl_port: ssl service port number
* @param reuseport: try to set SO_REUSEPORT if nonNULL and true.
* set to false on exit if reuseport failed due to no kernel support.
* @param transparent: set IP_TRANSPARENT socket option.
* @param tcp_mss: maximum segment size of tcp socket. default if zero.
* @param freebind: set IP_FREEBIND socket option.
* @param use_systemd: if true, fetch sockets from systemd.
* @param dnscrypt_port: dnscrypt service port number
* @return: returns false on error.
*/
static int
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list,
size_t rcv, size_t snd, int ssl_port, int* reuseport, int transparent,
int tcp_mss, int freebind, int use_systemd, int dnscrypt_port)
{
int s, noip6=0;
#ifdef USE_DNSCRYPT
int is_dnscrypt = ((strchr(ifname, '@') &&
atoi(strchr(ifname, '@')+1) == dnscrypt_port) ||
(!strchr(ifname, '@') && atoi(port) == dnscrypt_port));
#else
int is_dnscrypt = 0;
(void)dnscrypt_port;
#endif
if(!do_udp && !do_tcp)
return 0;
if(do_auto) {
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, freebind, use_systemd)) == -1) {
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
}
return 0;
}
/* getting source addr packet info is highly non-portable */
if(!set_recvpktinfo(s, hints->ai_family)) {
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return 0;
}
if(!port_insert(list, s,
is_dnscrypt?listen_type_udpancil_dnscrypt:listen_type_udpancil)) {
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return 0;
}
} else if(do_udp) {
/* regular udp socket */
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, freebind, use_systemd)) == -1) {
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
}
return 0;
}
if(!port_insert(list, s,
is_dnscrypt?listen_type_udp_dnscrypt:listen_type_udp)) {
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return 0;
}
}
if(do_tcp) {
int is_ssl = ((strchr(ifname, '@') &&
atoi(strchr(ifname, '@')+1) == ssl_port) ||
(!strchr(ifname, '@') && atoi(port) == ssl_port));
if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1,
&noip6, 0, 0, reuseport, transparent, tcp_mss,
freebind, use_systemd)) == -1) {
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
return 1;
}
return 0;
}
if(is_ssl)
verbose(VERB_ALGO, "setup TCP for SSL service");
if(!port_insert(list, s, is_ssl?listen_type_ssl:
(is_dnscrypt?listen_type_tcp_dnscrypt:listen_type_tcp))) {
#ifndef USE_WINSOCK
close(s);
#else
closesocket(s);
#endif
return 0;
}
}
return 1;
}
/**
* Add items to commpoint list in front.
* @param c: commpoint to add.
* @param front: listen struct.
* @return: false on failure.
*/
static int
listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
{
struct listen_list* item = (struct listen_list*)malloc(
sizeof(struct listen_list));
if(!item)
return 0;
item->com = c;
item->next = front->cps;
front->cps = item;
return 1;
}
struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, void* sslctx,
struct dt_env* dtenv, comm_point_callback_type* cb, void *cb_arg)
{
struct listen_dnsport* front = (struct listen_dnsport*)
malloc(sizeof(struct listen_dnsport));
if(!front)
return NULL;
front->cps = NULL;
front->udp_buff = sldns_buffer_new(bufsize);
#ifdef USE_DNSCRYPT
front->dnscrypt_udp_buff = NULL;
#endif
if(!front->udp_buff) {
free(front);
return NULL;
}
/* create comm points as needed */
while(ports) {
struct comm_point* cp = NULL;
if(ports->ftype == listen_type_udp ||
ports->ftype == listen_type_udp_dnscrypt)
cp = comm_point_create_udp(base, ports->fd,
front->udp_buff, cb, cb_arg);
else if(ports->ftype == listen_type_tcp ||
ports->ftype == listen_type_tcp_dnscrypt)
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, bufsize, cb, cb_arg);
else if(ports->ftype == listen_type_ssl) {
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, bufsize, cb, cb_arg);
cp->ssl = sslctx;
} else if(ports->ftype == listen_type_udpancil ||
ports->ftype == listen_type_udpancil_dnscrypt)
cp = comm_point_create_udp_ancil(base, ports->fd,
front->udp_buff, cb, cb_arg);
if(!cp) {
log_err("can't create commpoint");
listen_delete(front);
return NULL;
}
cp->dtenv = dtenv;
cp->do_not_close = 1;
#ifdef USE_DNSCRYPT
if (ports->ftype == listen_type_udp_dnscrypt ||
ports->ftype == listen_type_tcp_dnscrypt ||
ports->ftype == listen_type_udpancil_dnscrypt) {
cp->dnscrypt = 1;
cp->dnscrypt_buffer = sldns_buffer_new(bufsize);
if(!cp->dnscrypt_buffer) {
log_err("can't alloc dnscrypt_buffer");
comm_point_delete(cp);
listen_delete(front);
return NULL;
}
front->dnscrypt_udp_buff = cp->dnscrypt_buffer;
}
#endif
if(!listen_cp_insert(cp, front)) {
log_err("malloc failed");
comm_point_delete(cp);
listen_delete(front);
return NULL;
}
ports = ports->next;
}
if(!front->cps) {
log_err("Could not open sockets to accept queries.");
listen_delete(front);
return NULL;
}
return front;
}
void
listen_list_delete(struct listen_list* list)
{
struct listen_list *p = list, *pn;
while(p) {
pn = p->next;
comm_point_delete(p->com);
free(p);
p = pn;
}
}
void
listen_delete(struct listen_dnsport* front)
{
if(!front)
return;
listen_list_delete(front->cps);
#ifdef USE_DNSCRYPT
if(front->dnscrypt_udp_buff &&
front->udp_buff != front->dnscrypt_udp_buff) {
sldns_buffer_free(front->dnscrypt_udp_buff);
}
#endif
sldns_buffer_free(front->udp_buff);
free(front);
}
struct listen_port*
listening_ports_open(struct config_file* cfg, int* reuseport)
{
struct listen_port* list = NULL;
struct addrinfo hints;
int i, do_ip4, do_ip6;
int do_tcp, do_auto;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
do_ip4 = cfg->do_ip4;
do_ip6 = cfg->do_ip6;
do_tcp = cfg->do_tcp;
do_auto = cfg->if_automatic && cfg->do_udp;
if(cfg->incoming_num_tcp == 0)
do_tcp = 0;
/* getaddrinfo */
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
/* no name lookups on our listening ports */
if(cfg->num_ifs > 0)
hints.ai_flags |= AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC;
#ifndef INET6
do_ip6 = 0;
#endif
if(!do_ip4 && !do_ip6) {
return NULL;
}
/* create ip4 and ip6 ports so that return addresses are nice. */
if(do_auto || cfg->num_ifs == 0) {
if(do_ip6) {
hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
}
if(do_ip4) {
hints.ai_family = AF_INET;
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
}
} else for(i = 0; i<cfg->num_ifs; i++) {
if(str_is_ip6(cfg->ifs[i])) {
if(!do_ip6)
continue;
hints.ai_family = AF_INET6;
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
} else {
if(!do_ip4)
continue;
hints.ai_family = AF_INET;
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, reuseport,
cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
return NULL;
}
}
}
return list;
}
void listening_ports_free(struct listen_port* list)
{
struct listen_port* nx;
while(list) {
nx = list->next;
if(list->fd != -1) {
#ifndef USE_WINSOCK
close(list->fd);
#else
closesocket(list->fd);
#endif
}
free(list);
list = nx;
}
}
size_t listen_get_mem(struct listen_dnsport* listen)
{
struct listen_list* p;
size_t s = sizeof(*listen) + sizeof(*listen->base) +
sizeof(*listen->udp_buff) +
sldns_buffer_capacity(listen->udp_buff);
#ifdef USE_DNSCRYPT
s += sizeof(*listen->dnscrypt_udp_buff);
if(listen->udp_buff != listen->dnscrypt_udp_buff){
s += sldns_buffer_capacity(listen->dnscrypt_udp_buff);
}
#endif
for(p = listen->cps; p; p = p->next) {
s += sizeof(*p);
s += comm_point_get_mem(p->com);
}
return s;
}
void listen_stop_accept(struct listen_dnsport* listen)
{
/* do not stop the ones that have no tcp_free list
* (they have already stopped listening) */
struct listen_list* p;
for(p=listen->cps; p; p=p->next) {
if(p->com->type == comm_tcp_accept &&
p->com->tcp_free != NULL) {
comm_point_stop_listening(p->com);
}
}
}
void listen_start_accept(struct listen_dnsport* listen)
{
/* do not start the ones that have no tcp_free list, it is no
* use to listen to them because they have no free tcp handlers */
struct listen_list* p;
for(p=listen->cps; p; p=p->next) {
if(p->com->type == comm_tcp_accept &&
p->com->tcp_free != NULL) {
comm_point_start_listening(p->com, -1, -1);
}
}
}