It's a bit trickier for sdig, though.
if(vars.count("ipBindAddrNoPort")) {
ret->ipBindAddrNoPort=boost::get<bool>(vars["ipBindAddrNoPort"]);
- }
+ }
if(vars.count("addXPF")) {
- ret->addXPF=boost::get<bool>(vars["addXPF"]);
+ ret->xpfOptionCode=std::stoi(boost::get<string>(vars["addXPF"]));
}
if(vars.count("maxCheckFailures")) {
break;
}
- if (dq.addXPF && ds->addXPF) {
- addXPF(dq);
+ if (dq.addXPF && ds->xpfOptionCode != 0) {
+ addXPF(dq, ds->xpfOptionCode);
}
int dsock = -1;
return result;
}
-bool addXPF(DNSQuestion& dq)
+bool addXPF(DNSQuestion& dq, uint16_t optionCode)
{
std::string payload = generateXPFPayload(dq.tcp, *dq.remote, *dq.local);
uint8_t root = '\0';
dnsrecordheader drh;
- drh.d_type = htons(QType::XPF);
+ drh.d_type = htons(optionCode);
drh.d_class = htons(QClass::IN);
drh.d_ttl = 0;
drh.d_clen = htons(payload.size());
return;
}
- if (dq.addXPF && ss->addXPF) {
- addXPF(dq);
+ if (dq.addXPF && ss->xpfOptionCode != 0) {
+ addXPF(dq, ss->xpfOptionCode);
}
ss->queries++;
}
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
-
// listens to incoming queries, sends out to downstream servers, noting the intended return path
static void* udpClientThread(ClientState* cs)
try
int tcpSendTimeout{30};
unsigned int sourceItf{0};
uint16_t retries{5};
+ uint16_t xpfOptionCode{0};
uint8_t currentCheckFailures{0};
uint8_t maxCheckFailures{1};
StopWatch sw;
bool mustResolve{false};
bool upStatus{false};
bool useECS{false};
- bool addXPF{false};
bool setCD{false};
std::atomic<bool> connected{false};
bool tcpFastOpen{false};
bool encryptResponse(char* response, uint16_t* responseLen, size_t responseSize, bool tcp, std::shared_ptr<DnsCryptQuery> dnsCryptQuery, dnsheader** dh, dnsheader* dhCopy);
#endif
-bool addXPF(DNSQuestion& dq);
+bool addXPF(DNSQuestion& dq, uint16_t optionCode);
#include "dnsdist-snmp.hh"
newServer({
address="IP:PORT", -- IP and PORT of the backend server (mandatory)
- addXPF=BOOL, -- Add the client's IP address and port to the query, along with the original destination address and port,
- -- using the experimental XPF record from `draft-bellis-dnsop-xpf`. Default to false
qps=NUM, -- Limit the number of queries per second to NUM, when using the `firstAvailable` policy
order=NUM, -- The order of this server, used by the `leastOustanding` and `firstAvailable` policies
weight=NUM, -- The weight of this server, used by the `wrandom` and `whashed` policies
maxCheckFailures=NUM, -- Allow NUM check failures before declaring the backend down, default: false
mustResolve=BOOL, -- Set to true when the health check MUST return a NOERROR RCODE and an answer
useClientSubnet=BOOL, -- Add the client's IP address in the EDNS Client Subnet option when forwarding the query to this backend
- source=STRING -- The source address or interface to use for queries to this backend, by default this is left to the kernel's address selection
+ source=STRING, -- The source address or interface to use for queries to this backend, by default this is left to the kernel's address selection
-- The following formats are supported:
-- "address", e.g. "192.0.2.2"
-- "interface name", e.g. "eth0"
-- "address@interface", e.g. "192.0.2.2@eth0"
+ addXPF=NUM -- Add the client's IP address and port to the query, along with the original destination address and port,
+ -- using the experimental XPF record from `draft-bellis-dnsop-xpf` and the specified option code. Default is disabled (0)
})
:param str server_string: A simple IP:PORT string.
d_answers.push_back(make_pair(dr, pr.d_pos));
- if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF) {
+ /* XXX: XPF records should be allowed after TSIG as soon as the actual XPF option code has been assigned:
+ if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF)
+ */
+ if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
/* only XPF records are allowed after a TSIG */
throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has an unexpected record ("+std::to_string(dr.d_type)+") after a TSIG one.");
}
if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
- if(dr.d_place != DNSResourceRecord::ADDITIONAL) {
+ if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
}
seenTSIG = true;
static unsigned int g_numWorkerThreads;
static int g_tcpTimeout;
static uint16_t g_udpTruncationThreshold;
+static uint16_t g_xpfOptionCode{0};
static std::atomic<bool> statsWanted;
static std::atomic<bool> g_quiet;
static bool g_logCommonErrors;
bool& foundECS, EDNSSubnetOpts* ednssubnet, std::map<uint16_t, EDNSOptionView>* options,
bool& foundXPF, ComboAddress* xpfSource, ComboAddress* xpfDest)
{
- const bool lookForXPF = xpfSource != nullptr;
+ const bool lookForXPF = xpfSource != nullptr && g_xpfOptionCode != 0;
const bool lookForECS = ednssubnet != nullptr;
const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(question.c_str());
size_t questionLen = question.length();
}
}
}
- else if (lookForXPF && ntohs(drh->d_type) == QType::XPF && ntohs(drh->d_class) == QClass::IN && drh->d_ttl == 0) {
+ else if (lookForXPF && ntohs(drh->d_type) == g_xpfOptionCode && ntohs(drh->d_class) == QClass::IN && drh->d_ttl == 0) {
if ((questionLen - pos) < ntohs(drh->d_clen)) {
return;
}
g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
g_XPFAcl.toMasks(::arg()["xpf-allow-from"]);
+ g_xpfOptionCode = ::arg().asNum("xpf-option-code");
g_networkTimeoutMsec = ::arg().asNum("network-timeout");
::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level")="no";
::arg().set("xpf-allow-from","XPF information is only processed from these subnets")="";
+ ::arg().set("xpf-option-code","XPF option code to use")="0";
::arg().setCmd("help","Provide a helpful message");
::arg().setCmd("version","Print version string");
is placed behind a proxy like dnsdist.
Note that the `allow-from`_ setting is still applied to the original source address, and thus access restriction
should be done on the proxy.
+
+``xpf-option-code``
+-------------
+.. versionadded:: 4.1.0
+
+- Integer
+- Default: 0
+
+This is an experimental implementation of `draft-bellis-dnsop-xpf`.
+The option code to use for XPF records, as long as an official code has not been assigned to it. 0 means disabled.
_xpfCode = 65422
_config_template = """
- newServer{address="127.0.0.1:%s", addXPF=true}
+ newServer{address="127.0.0.1:%d", addXPF=%d}
"""
+ _config_params = ['_testServerPort', '_xpfCode']
def checkMessageHasXPF(self, msg, expectedValue):
self.assertGreaterEqual(len(msg.additional), 1)