#include "rec-lua-conf.hh"
#include "ednsoptions.hh"
#include "gettime.hh"
+#include "proxy-protocol.hh"
#include "pubsuffix.hh"
#ifdef NOD_ENABLED
#include "nod.hh"
static std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
static NetmaskGroup g_XPFAcl;
+static NetmaskGroup g_proxyProtocolACL;
+static size_t g_proxyProtocolMaximumSize;
static size_t g_tcpMaxQueriesPerConn;
static size_t s_maxUDPQueriesPerRound;
static uint64_t g_latencyStatSize;
return d_source.toStringWithPort() + " (proxied by " + d_remote.toStringWithPort() + ")";
}
+ std::vector<ProxyProtocolValue> d_proxyProtocolValues;
MOADNSParser d_mdp;
struct timeval d_now;
/* Remote client, might differ from d_source
}
}
+static bool handleTCPReadResult(int fd, ssize_t bytes)
+{
+ if (bytes == 0) {
+ /* EOF */
+ t_fdm->removeReadFD(fd);
+ return false;
+ }
+ else if (bytes < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ t_fdm->removeReadFD(fd);
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
{
shared_ptr<TCPConnection> conn=any_cast<shared_ptr<TCPConnection> >(var);
- if(conn->state==TCPConnection::BYTE0) {
+ if (conn->state == TCPConnection::PROXYPROTOCOLHEADER) {
+ ssize_t bytes = recv(conn->getFD(), &conn->data.at(conn->proxyProtocolGot), conn->proxyProtocolNeed, 0);
+ if (bytes <= 0) {
+ handleTCPReadResult(fd, bytes);
+ return;
+ }
+
+ conn->proxyProtocolGot += bytes;
+ conn->data.resize(conn->proxyProtocolGot);
+ ssize_t remaining = isProxyHeaderComplete(conn->data);
+ if (remaining == 0) {
+ ++g_stats.proxyProtocolInvalidCount;
+ t_fdm->removeReadFD(fd);
+ return;
+ }
+ else if (remaining < 0) {
+ conn->proxyProtocolNeed = -remaining;
+ conn->data.resize(conn->proxyProtocolGot + conn->proxyProtocolNeed);
+ return;
+ }
+ else {
+ /* proxy header received */
+ /* we ignore the TCP field for now, but we could properly set whether
+ the connection was received over UDP or TCP if neede */
+ bool tcp;
+ if (parseProxyHeader(conn->data, conn->d_source, conn->d_destination, tcp, conn->proxyProtocolValues) <= 0) {
+ t_fdm->removeReadFD(fd);
+ return;
+ }
+ conn->data.resize(2);
+ conn->state = TCPConnection::BYTE0;
+ }
+ }
+
+ if (conn->state==TCPConnection::BYTE0) {
ssize_t bytes=recv(conn->getFD(), &conn->data[0], 2, 0);
if(bytes==1)
conn->state=TCPConnection::BYTE1;
conn->state=TCPConnection::GETQUESTION;
}
if(!bytes || bytes < 0) {
- t_fdm->removeReadFD(fd);
+ handleTCPReadResult(fd, bytes);
return;
}
}
- else if(conn->state==TCPConnection::BYTE1) {
+
+ if (conn->state==TCPConnection::BYTE1) {
ssize_t bytes=recv(conn->getFD(), &conn->data[1], 1, 0);
if(bytes==1) {
conn->state=TCPConnection::GETQUESTION;
conn->bytesread=0;
}
if(!bytes || bytes < 0) {
- if(g_logCommonErrors)
- g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected after first byte"<<endl;
- t_fdm->removeReadFD(fd);
+ if (!handleTCPReadResult(fd, bytes)) {
+ if(g_logCommonErrors) {
+ g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected after first byte"<<endl;
+ }
+ }
return;
}
}
- else if(conn->state==TCPConnection::GETQUESTION) {
+
+ if(conn->state==TCPConnection::GETQUESTION) {
ssize_t bytes=recv(conn->getFD(), &conn->data[conn->bytesread], conn->qlen - conn->bytesread, 0);
- if(!bytes || bytes < 0 || bytes > std::numeric_limits<std::uint16_t>::max()) {
+ if (bytes <= 0) {
+ if (!handleTCPReadResult(fd, bytes)) {
+ if(g_logCommonErrors) {
+ g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected while reading question body"<<endl;
+ }
+ }
+ return;
+ }
+ else if (bytes > std::numeric_limits<std::uint16_t>::max()) {
if(g_logCommonErrors) {
- g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected while reading question body"<<endl;
+ g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" sent an invalid question size while reading question body"<<endl;
}
t_fdm->removeReadFD(fd);
return;
dc->setSocket(conn->getFD()); // this is the only time a copy is made of the actual fd
dc->d_tcp=true;
dc->setRemote(conn->d_remote);
- dc->setSource(conn->d_remote);
+ dc->setSource(conn->d_source);
ComboAddress dest;
dest.reset();
dest.sin4.sin_family = conn->d_remote.sin4.sin_family;
socklen_t len = dest.getSocklen();
getsockname(conn->getFD(), (sockaddr*)&dest, &len); // if this fails, we're ok with it
dc->setLocal(dest);
- dc->setDestination(dest);
+ dc->setDestination(conn->d_destination);
+ dc->d_proxyProtocolValues = std::move(conn->proxyProtocolValues);
DNSName qname;
uint16_t qtype=0;
uint16_t qclass=0;
}
}
+static bool expectProxyProtocol(const ComboAddress& from)
+{
+ return g_proxyProtocolACL.match(from);
+}
+
//! Handle new incoming TCP connection
static void handleNewTCPQuestion(int fd, FDMultiplexer::funcparam_t& )
{
setNonBlocking(newsock);
std::shared_ptr<TCPConnection> tc = std::make_shared<TCPConnection>(newsock, addr);
- tc->state=TCPConnection::BYTE0;
+ tc->d_source = addr;
+ tc->d_destination.reset();
+ tc->d_destination.sin4.sin_family = addr.sin4.sin_family;
+ socklen_t len = tc->d_destination.getSocklen();
+ getsockname(tc->getFD(), reinterpret_cast<sockaddr*>(&tc->d_destination), &len); // if this fails, we're ok with it
+
+ if (expectProxyProtocol(addr)) {
+ tc->proxyProtocolNeed = s_proxyProtocolMinimumHeaderSize;
+ tc->data.resize(tc->proxyProtocolNeed);
+ tc->state = TCPConnection::PROXYPROTOCOLHEADER;
+ }
+ else {
+ tc->state = TCPConnection::BYTE0;
+ }
struct timeval ttd;
Utility::gettimeofday(&ttd, 0);
}
}
-static string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fromaddr, const ComboAddress& destaddr, struct timeval tv, int fd)
+static string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fromaddr, const ComboAddress& destaddr, ComboAddress source, ComboAddress destination, struct timeval tv, int fd, std::vector<ProxyProtocolValue>& proxyProtocolValues)
{
gettimeofday(&g_now, 0);
if (tv.tv_sec) {
bool needXPF = g_XPFAcl.match(fromaddr);
std::vector<std::string> policyTags;
LuaContext::LuaObject data;
- ComboAddress source = fromaddr;
- ComboAddress destination = destaddr;
string requestorId;
string deviceId;
string deviceName;
dc->d_deviceName = deviceName;
dc->d_kernelTimestamp = tv;
#endif
+ dc->d_proxyProtocolValues = std::move(proxyProtocolValues);
MT->makeThread(startDoResolve, (void*) dc.release()); // deletes dc
return 0;
static void handleNewUDPQuestion(int fd, FDMultiplexer::funcparam_t& var)
{
ssize_t len;
- static const size_t maxIncomingQuerySize = 512;
+ static const size_t maxIncomingQuerySize = g_proxyProtocolACL.empty() ? 512 : (512 + g_proxyProtocolMaximumSize);
static thread_local std::string data;
ComboAddress fromaddr;
+ ComboAddress source;
+ ComboAddress destination;
struct msghdr msgh;
struct iovec iov;
cmsgbuf_aligned cbuf;
bool firstQuery = true;
+ std::vector<ProxyProtocolValue> proxyProtocolValues;
for(size_t queriesCounter = 0; queriesCounter < s_maxUDPQueriesPerRound; queriesCounter++) {
+ bool proxyProto = false;
data.resize(maxIncomingQuerySize);
fromaddr.sin6.sin6_family=AF_INET6; // this makes sure fromaddr is big enough
fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), &data[0], data.size(), &fromaddr);
if((len=recvmsg(fd, &msgh, 0)) >= 0) {
firstQuery = false;
+ data.resize(static_cast<size_t>(len));
+
+ if (expectProxyProtocol(fromaddr)) {
+ bool tcp;
+ ssize_t used = parseProxyHeader(data, source, destination, tcp, proxyProtocolValues);
+ if (used <= 0) {
+ ++g_stats.proxyProtocolInvalidCount;
+ if (!g_quiet) {
+ g_log<<Logger::Error<<"Ignoring invalid proxy protocol ("<<std::to_string(len)<<") query from "<<fromaddr.toString()<<endl;
+ }
+ return;
+ }
+ proxyProto = true;
+ data.erase(0, used);
+ }
+ else {
+ source = fromaddr;
+ }
- if (static_cast<size_t>(len) < sizeof(dnsheader)) {
+ if (data.size() < sizeof(dnsheader)) {
g_stats.ignoredCount++;
if (!g_quiet) {
- g_log<<Logger::Error<<"Ignoring too-short ("<<std::to_string(len)<<") query from "<<fromaddr.toString()<<endl;
+ g_log<<Logger::Error<<"Ignoring too-short ("<<std::to_string(data.size())<<") query from "<<fromaddr.toString()<<endl;
}
return;
}
}
try {
- data.resize(static_cast<size_t>(len));
dnsheader* dh=(dnsheader*)&data[0];
if(dh->qr) {
getsockname(fd, (sockaddr*)&dest, &slen); // if this fails, we're ok with it
}
}
+ if (!proxyProto) {
+ destination = dest;
+ }
if(g_weDistributeQueries) {
- distributeAsyncFunction(data, boost::bind(doProcessUDPQuestion, data, fromaddr, dest, tv, fd));
+ distributeAsyncFunction(data, boost::bind(doProcessUDPQuestion, data, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues));
}
else {
++s_threadInfos[t_id].numberOfDistributedQueries;
- doProcessUDPQuestion(data, fromaddr, dest, tv, fd);
+ doProcessUDPQuestion(data, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues);
}
}
}
g_XPFAcl.toMasks(::arg()["xpf-allow-from"]);
g_xpfRRCode = ::arg().asNum("xpf-rr-code");
+ g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-allow-from"]);
+ g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
+
g_networkTimeoutMsec = ::arg().asNum("network-timeout");
g_initialDomainMap = parseAuthAndForwards();
::arg().set("xpf-allow-from","XPF information is only processed from these subnets")="";
::arg().set("xpf-rr-code","XPF option code to use")="0";
+ ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets")="";
+ ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values")="512";
+
::arg().set("udp-source-port-min", "Minimum UDP port to bind on")="1024";
::arg().set("udp-source-port-max", "Maximum UDP port to bind on")="65535";
::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid")="11211";