From: Remi Gacogne Date: Wed, 19 Feb 2020 14:15:38 +0000 (+0100) Subject: Implement support for 'LOCAL' proxy protocol command X-Git-Tag: dnsdist-1.5.0-alpha1~12^2~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8c73c7034eb77142f5849426b2a034178fdea761;p=thirdparty%2Fpdns.git Implement support for 'LOCAL' proxy protocol command --- diff --git a/pdns/dnsdistdist/dnsdist-healthchecks.cc b/pdns/dnsdistdist/dnsdist-healthchecks.cc index 3dc7e6a371..05ea151717 100644 --- a/pdns/dnsdistdist/dnsdist-healthchecks.cc +++ b/pdns/dnsdistdist/dnsdist-healthchecks.cc @@ -207,6 +207,11 @@ bool queueHealthCheck(std::shared_ptr& mplexer, const std::shared dnsheader * requestHeader = dpw.getHeader(); *requestHeader = checkHeader; + if (ds->useProxyProtocol) { + auto payload = makeLocalProxyHeader(); + packet.insert(packet.begin(), payload.begin(), payload.end()); + } + Socket sock(ds->remote.sin4.sin_family, SOCK_DGRAM); sock.setNonBlocking(); if (!IsAnyAddress(ds->sourceAddr)) { diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index bed4ea90b6..741a7f4339 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -2046,12 +2046,14 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var) /* 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) { + bool proxy = false; + if (parseProxyHeader(conn->data, proxy, conn->d_source, conn->d_destination, tcp, conn->proxyProtocolValues) <= 0) { t_fdm->removeReadFD(fd); return; } /* check the real source */ + /* note that if the proxy header used a 'LOCAL' command, the original source and destination are untouched so everything should be fine */ if (t_allowFrom && !t_allowFrom->match(&conn->d_source)) { if (!g_quiet) { g_log<getTid()<<"] dropping TCP query from "<d_source.toString()<<", address not matched by allow-from"<(&versioncommand), sizeof(versioncommand)); + ret.append(reinterpret_cast(&protocol), sizeof(protocol)); + + ret.append(reinterpret_cast(&contentLen), sizeof(contentLen)); + + return ret; +} + +std::string makeLocalProxyHeader() +{ + return makeSimpleHeader(0x00, 0, 0); +} + std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector& values) { if (source.sin4.sin_family != destination.sin4.sin_family) { throw std::runtime_error("The PROXY destination and source addresses must be of the same family"); } - std::string ret; - const uint8_t versioncommand = (0x20 | 0x01); + const uint8_t command = 0x01; const uint8_t protocol = (source.isIPv4() ? 0x10 : 0x20) | (tcp ? 0x01 : 0x02); const size_t addrSize = source.isIPv4() ? sizeof(source.sin4.sin_addr.s_addr) : sizeof(source.sin6.sin6_addr.s6_addr); const uint16_t sourcePort = source.sin4.sin_port; @@ -52,14 +73,7 @@ std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAdd const uint16_t contentlen = htons((addrSize * 2) + sizeof(sourcePort) + sizeof(destinationPort) + valuesSize); - ret.reserve(proxymagic.size() + sizeof(versioncommand) + sizeof(protocol) + sizeof(contentlen) + contentlen); - - ret.append(proxymagic); - - ret.append(reinterpret_cast(&versioncommand), sizeof(versioncommand)); - ret.append(reinterpret_cast(&protocol), sizeof(protocol)); - - ret.append(reinterpret_cast(&contentlen), sizeof(contentlen)); + std::string ret = makeSimpleHeader(command, protocol, contentlen); // We already established source and destination sin_family equivalence if (source.isIPv4()) { @@ -91,10 +105,11 @@ std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAdd /* returns: number of bytes consumed (positive) after successful parse or number of bytes missing (negative) or unfixable parse error (0)*/ -ssize_t isProxyHeaderComplete(const std::string& header, bool* tcp, size_t* addrSizeOut, uint8_t* protocolOut) +ssize_t isProxyHeaderComplete(const std::string& header, bool* proxy, bool* tcp, size_t* addrSizeOut, uint8_t* protocolOut) { static const size_t addr4Size = sizeof(ComboAddress::sin4.sin_addr.s_addr); static const size_t addr6Size = sizeof(ComboAddress::sin6.sin6_addr.s6_addr); + size_t addrSize = 0; uint8_t versioncommand; uint8_t protocol; @@ -109,48 +124,68 @@ ssize_t isProxyHeaderComplete(const std::string& header, bool* tcp, size_t* addr } versioncommand = header.at(12); - if (versioncommand != 0x21) { - // FIXME: handle 0x20 here to mean 'proxy header present but use socket peer&local' + /* check version */ + if (!(versioncommand & 0x20)) { return 0; } - protocol = header.at(13); - size_t addrSize; - if ((protocol & 0xf) == 1) { - if (tcp) { - *tcp = true; + /* remove the version to get the command */ + uint8_t command = versioncommand & ~0x20; + + if (command == 0x01) { + protocol = header.at(13); + if ((protocol & 0xf) == 1) { + if (tcp) { + *tcp = true; + } + } else if ((protocol & 0xf) == 2) { + if (tcp) { + *tcp = false; + } + } else { + return 0; } - } else if ((protocol & 0xf) == 2) { - if (tcp) { - *tcp = false; + + protocol = protocol >> 4; + + if (protocol == 1) { + if (protocolOut) { + *protocolOut = 4; + } + addrSize = addr4Size; // IPv4 + } else if (protocol == 2) { + if (protocolOut) { + *protocolOut = 6; + } + addrSize = addr6Size; // IPv6 + } else { + // invalid protocol + return 0; } - } else { - return 0; - } - protocol = protocol >> 4; + if (addrSizeOut) { + *addrSizeOut = addrSize; + } - if (protocol == 1) { - if (protocolOut) { - *protocolOut = 4; + if (proxy) { + *proxy = true; } - addrSize = addr4Size; // IPv4 - } else if (protocol == 2) { - if (protocolOut) { - *protocolOut = 6; + } + else if (command == 0x00) { + if (proxy) { + *proxy = false; } - addrSize = addr6Size; // IPv6 - } else { - // invalid protocol - return 0; } - - if (addrSizeOut) { - *addrSizeOut = addrSize; + else { + /* unsupported command */ + return 0; } uint16_t contentlen = (header.at(14) << 8) + header.at(15); - uint16_t expectedlen = (addrSize * 2) + sizeof(ComboAddress::sin4.sin_port) + sizeof(ComboAddress::sin4.sin_port); + uint16_t expectedlen = 0; + if (command != 0x00) { + expectedlen = (addrSize * 2) + sizeof(ComboAddress::sin4.sin_port) + sizeof(ComboAddress::sin4.sin_port); + } if (contentlen < expectedlen) { return 0; @@ -166,25 +201,27 @@ ssize_t isProxyHeaderComplete(const std::string& header, bool* tcp, size_t* addr /* returns: number of bytes consumed (positive) after successful parse or number of bytes missing (negative) or unfixable parse error (0)*/ -ssize_t parseProxyHeader(const std::string& header, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector& values) +ssize_t parseProxyHeader(const std::string& header, bool& proxy, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector& values) { size_t addrSize = 0; uint8_t protocol = 0; - ssize_t got = isProxyHeaderComplete(header, &tcp, &addrSize, &protocol); + ssize_t got = isProxyHeaderComplete(header, &proxy, &tcp, &addrSize, &protocol); if (got <= 0) { return got; } size_t pos = s_proxyProtocolMinimumHeaderSize; - source = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize); - pos = pos + addrSize; - destination = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize); - pos = pos + addrSize; - source.setPort((header.at(pos) << 8) + header.at(pos+1)); - pos = pos + sizeof(uint16_t); - destination.setPort((header.at(pos) << 8) + header.at(pos+1)); - pos = pos + sizeof(uint16_t); + if (proxy) { + source = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize); + pos = pos + addrSize; + destination = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize); + pos = pos + addrSize; + source.setPort((header.at(pos) << 8) + header.at(pos+1)); + pos = pos + sizeof(uint16_t); + destination.setPort((header.at(pos) << 8) + header.at(pos+1)); + pos = pos + sizeof(uint16_t); + } size_t remaining = got - pos; while (remaining >= (sizeof(uint8_t) + sizeof(uint16_t))) { diff --git a/pdns/proxy-protocol.hh b/pdns/proxy-protocol.hh index 70047d8217..66b8f0aaf4 100644 --- a/pdns/proxy-protocol.hh +++ b/pdns/proxy-protocol.hh @@ -32,13 +32,15 @@ struct ProxyProtocolValue static const size_t s_proxyProtocolMinimumHeaderSize = 16; +std::string makeLocalProxyHeader(); std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector& values); /* returns: number of bytes consumed (positive) after successful parse or number of bytes missing (negative) or unfixable parse error (0)*/ -ssize_t isProxyHeaderComplete(const std::string& header, bool* tcp=nullptr, size_t* addrSizeOut=nullptr, uint8_t* protocolOut=nullptr); +ssize_t isProxyHeaderComplete(const std::string& header, bool* proxy=nullptr, bool* tcp=nullptr, size_t* addrSizeOut=nullptr, uint8_t* protocolOut=nullptr); + /* returns: number of bytes consumed (positive) after successful parse or number of bytes missing (negative) or unfixable parse error (0)*/ -ssize_t parseProxyHeader(const std::string& payload, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector& values); +ssize_t parseProxyHeader(const std::string& payload, bool& proxy, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector& values); diff --git a/pdns/sdig.cc b/pdns/sdig.cc index 4c763f4f20..1ef4520c11 100644 --- a/pdns/sdig.cc +++ b/pdns/sdig.cc @@ -320,9 +320,10 @@ try { ComboAddress source, destination; bool wastcp; + bool proxy = false; std::vector ignoredValues; - ssize_t offset = parseProxyHeader(reply, source, destination, wastcp, ignoredValues); - if (offset) { + ssize_t offset = parseProxyHeader(reply, proxy, source, destination, wastcp, ignoredValues); + if (offset && proxy) { cout<<"proxy "<<(wastcp ? "tcp" : "udp")<<" headersize="<