diff --git a/docs/manpages/sdig.1.rst b/docs/manpages/sdig.1.rst index 73e5a9e2d787..996d5ca6ad07 100644 --- a/docs/manpages/sdig.1.rst +++ b/docs/manpages/sdig.1.rst @@ -57,6 +57,8 @@ tlsProvider *name* when using DoT, use TLS provider *name*. Currently supported (if compiled in): `openssl` and `gnutls`. Default is `openssl` if available. opcode *OPNUM* Use opcode *OPNUM* instead of 0 (Query). For example, ``sdig 192.0.2.1 53 example.com SOA opcode 4`` sends a ``NOTIFY``. +cookie *COOKIE* + if *COOKIE* is -d send a random client cookie. Otherwise send the given cookie, which should be a hex string received from a server earlier. Examples -------- diff --git a/pdns/Makefile.am b/pdns/Makefile.am index a5e9cbfe706c..d824d24f2cae 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -540,6 +540,7 @@ sdig_SOURCES = \ dnsrecords.cc \ dnswriter.cc dnswriter.hh \ dolog.hh \ + ednscookies.cc ednscookies.hh \ ednsextendederror.cc ednsextendederror.hh \ ednssubnet.cc iputils.cc \ libssl.cc libssl.hh \ diff --git a/pdns/ednscookies.cc b/pdns/ednscookies.cc index 593d9e728cb3..253d9bab8ca0 100644 --- a/pdns/ednscookies.cc +++ b/pdns/ednscookies.cc @@ -23,6 +23,7 @@ #include "config.h" #include "ednscookies.hh" +#include "dns_random.hh" #include "iputils.hh" #ifdef HAVE_CRYPTO_SHORTHASH @@ -145,6 +146,16 @@ bool EDNSCookiesOpt::shouldRefresh() const return rfc1982LessThan(timestamp + 1800, now); } +void EDNSCookiesOpt::makeClientCookie() +{ + uint32_t lower = dns_random_uint32(); + uint32_t upper = dns_random_uint32(); + client = string(); + client.resize(8); + memcpy(client.data(), &lower, sizeof(lower)); + memcpy(&client.at(4), &upper, sizeof(upper)); +} + bool EDNSCookiesOpt::makeServerCookie([[maybe_unused]] const string& secret, [[maybe_unused]] const ComboAddress& source) { #ifdef HAVE_CRYPTO_SHORTHASH diff --git a/pdns/ednscookies.hh b/pdns/ednscookies.hh index 03c5bb2ca0c4..204ef235622d 100644 --- a/pdns/ednscookies.hh +++ b/pdns/ednscookies.hh @@ -52,6 +52,7 @@ struct EDNSCookiesOpt } [[nodiscard]] bool isValid(const std::string& secret, const ComboAddress& source) const; + void makeClientCookie(); bool makeServerCookie(const std::string& secret, const ComboAddress& source); [[nodiscard]] std::string makeOptString() const; [[nodiscard]] std::string getServer() const diff --git a/pdns/misc.cc b/pdns/misc.cc index 8f0a7bd45333..5e662a8dbf57 100644 --- a/pdns/misc.cc +++ b/pdns/misc.cc @@ -630,15 +630,16 @@ string U32ToIP(uint32_t val) } -string makeHexDump(const string& str) +string makeHexDump(const string& str, const string& sep) { std::array tmp; string ret; - ret.reserve(static_cast(str.size()*2.2)); + ret.reserve(static_cast(str.size() * (2 + sep.size()))); for (char n : str) { - snprintf(tmp.data(), tmp.size(), "%02x ", static_cast(n)); + snprintf(tmp.data(), tmp.size(), "%02x", static_cast(n)); ret += tmp.data(); + ret += sep; } return ret; } diff --git a/pdns/misc.hh b/pdns/misc.hh index 1082b47c5ad6..efc5e8bf2089 100644 --- a/pdns/misc.hh +++ b/pdns/misc.hh @@ -336,7 +336,7 @@ inline double getTime() throw runtime_error(why + ": " + stringerror(errno)); } -string makeHexDump(const string& str); +string makeHexDump(const string& str, const string& sep = " "); //! Convert the hexstring in to a byte string string makeBytesFromHex(const string &in); diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 03beee782b8f..da669c9c9343 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -140,8 +140,8 @@ pdns_recursor_SOURCES = \ dnsseckeeper.hh \ dnswriter.cc dnswriter.hh \ dolog.hh \ - ednsextendederror.cc ednsextendederror.hh \ ednscookies.cc ednscookies.hh \ + ednsextendederror.cc ednsextendederror.hh \ ednsoptions.cc ednsoptions.hh \ ednspadding.cc ednspadding.hh \ ednssubnet.cc ednssubnet.hh \ diff --git a/pdns/sdig.cc b/pdns/sdig.cc index 9f3a3fc6a16c..ca956a0c34c0 100644 --- a/pdns/sdig.cc +++ b/pdns/sdig.cc @@ -6,6 +6,7 @@ #include "dnswriter.hh" #include "ednsoptions.hh" #include "ednssubnet.hh" +#include "ednscookies.hh" #include "ednsextendederror.hh" #include "misc.hh" #include "proxy-protocol.hh" @@ -41,6 +42,7 @@ static void usage() "[dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] [recurse] [showflags] " "[tcp] [dot] [insecure] [fastOpen] [subjectName name] [caStore file] [tlsProvider openssl|gnutls] " "[proxy UDP(0)/TCP(1) SOURCE-IP-ADDRESS-AND-PORT DESTINATION-IP-ADDRESS-AND-PORT] " + "[cookie -/HEX] " "[dumpluaraw] [opcode OPNUM]" << endl; } @@ -57,11 +59,11 @@ static std::unordered_set s_expectedIDs; static void fillPacket(vector& packet, const string& q, const string& t, bool dnssec, const std::optional& ednsnm, - bool recurse, QClass qclass, uint8_t opcode, uint16_t qid) + bool recurse, QClass qclass, uint8_t opcode, uint16_t qid, const std::optional& cookie) { DNSPacketWriter pw(packet, DNSName(q), DNSRecordContent::TypeToNumber(t), qclass, opcode); - if (dnssec || ednsnm || getenv("SDIGBUFSIZE")) { + if (dnssec || ednsnm || getenv("SDIGBUFSIZE") || cookie) { char* sbuf = getenv("SDIGBUFSIZE"); int bufsize; if (sbuf) @@ -74,7 +76,19 @@ static void fillPacket(vector& packet, const string& q, const string& t eo.setSource(*ednsnm); opts.emplace_back(EDNSOptionCode::ECS, eo.makeOptString()); } - + if (cookie) { + EDNSCookiesOpt cookieOpt; + if (*cookie == "-") { + cookieOpt.makeClientCookie(); + } + else { + string unhex = makeBytesFromHex(*cookie); + if (!cookieOpt.makeFromString(unhex)) { + cerr << "Malformed cookie in argument list, adding anyway" << endl; + } + } + opts.emplace_back(EDNSOptionCode::COOKIE, cookieOpt.makeOptString()); + } pw.addOpt(bufsize, 0, dnssec ? EDNSOpts::DNSSECOK : 0, opts); pw.commit(); } @@ -104,8 +118,18 @@ static void printReply(const string& reply, bool showflags, bool hidesoadetails, } cout << endl; - cout << "Rcode: " << mdp.d_header.rcode << " (" - << RCode::to_s(mdp.d_header.rcode) << "), RD: " << mdp.d_header.rd + EDNSOpts edo{}; + bool hasEDNS = getEDNSOpts(mdp, &edo); + + if (hasEDNS) { + uint16_t ercode = edo.d_extRCode << 4 | mdp.d_header.rcode; + cout << "Rcode: " << ercode << " (" << ERCode::to_s(ercode); + } + else { + cout << "Rcode: " << mdp.d_header.rcode << " (" << RCode::to_s(mdp.d_header.rcode); + } + + cout << "), RD: " << mdp.d_header.rd << ", QR: " << mdp.d_header.qr; cout << ", TC: " << mdp.d_header.tc << ", AA: " << mdp.d_header.aa << ", opcode: " << mdp.d_header.opcode << endl; @@ -162,28 +186,37 @@ static void printReply(const string& reply, bool showflags, bool hidesoadetails, cout << "\t" << i->getContent()->getZoneRepresentation() << "\n"; } - EDNSOpts edo; - if (getEDNSOpts(mdp, &edo)) { - // cerr<<"Have "<>::const_iterator iter = edo.d_options.begin(); - iter != edo.d_options.end(); ++iter) { - if (iter->first == EDNSOptionCode::ECS) { // 'EDNS subnet' + if (hasEDNS) { + for (const auto& iter : edo.d_options) { + if (iter.first == EDNSOptionCode::ECS) { // 'EDNS subnet' EDNSSubnetOpts reso; - if (EDNSSubnetOpts::getFromString(iter->second, &reso)) { + if (EDNSSubnetOpts::getFromString(iter.second, &reso)) { cerr << "EDNS Subnet response: " << reso.getSource().toString() << ", scope: " << reso.getScope().toString() << ", family = " << std::to_string(reso.getFamily()) << endl; } - } else if (iter->first == EDNSOptionCode::PADDING) { - cerr << "EDNS Padding size: " << (iter->second.size()) << endl; - } else if (iter->first == EDNSOptionCode::EXTENDEDERROR) { + } + else if (iter.first == EDNSOptionCode::COOKIE) { + EDNSCookiesOpt cookie(iter.second); + auto client = cookie.getClient(); + auto server = cookie.getServer(); + auto dump = makeHexDump(client, "") + makeHexDump(server, ""); + if (cookie.isWellFormed()) { + cerr << "EDNS Cookie response: " << dump << endl; + } + else { + cerr << "EDNS Cookie response malformed: " << dump << endl; + } + } else if (iter.first == EDNSOptionCode::PADDING) { + cerr << "EDNS Padding size: " << iter.second.size() << endl; + } else if (iter.first == EDNSOptionCode::EXTENDEDERROR) { EDNSExtendedError eee; - if (getEDNSExtendedErrorOptFromString(iter->second, eee)) { + if (getEDNSExtendedErrorOptFromString(iter.second, eee)) { cerr << "EDNS Extended Error response: " << eee.infoCode << "/" << eee.extraText << endl; } } else { - cerr << "Have unknown option " << (int)iter->first << endl; + cerr << "Have unknown option " << (int)iter.first << endl; } } } @@ -211,6 +244,7 @@ try { string caStore; string tlsProvider = "openssl"; bool dumpluaraw = false; + std::optional cookie; for (int i = 1; i < argc; i++) { if ((string)argv[i] == "--help") { @@ -303,6 +337,13 @@ try { ComboAddress dest(argv[++i]); proxyheader = makeProxyHeader(ptcp, src, dest, {}); } + else if (strcmp(argv[i], "cookie") == 0) { + if (argc < i + 2) { + cerr << "cookie needs an argument"< packet; s_expectedIDs.insert(0); - fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0); + fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie); MiniCurl mc; MiniCurl::MiniCurlHeaders mch; mch.emplace("Content-Type", "application/dns-message"); @@ -410,7 +451,7 @@ try { for (const auto& it : questions) { vector packet; s_expectedIDs.insert(counter); - fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, qclass, opcode, counter); + fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, qclass, opcode, counter, cookie); counter++; // Prefer to do a single write, so that fastopen can send all the data on SYN @@ -440,7 +481,7 @@ try { { vector packet; s_expectedIDs.insert(0); - fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0); + fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie); string question(packet.begin(), packet.end()); Socket sock(dest.sin4.sin_family, SOCK_DGRAM); question = proxyheader + question;