]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
kdig: implemented EDNS presentation format
authorLibor Peltan <libor.peltan@nic.cz>
Tue, 18 Jul 2023 15:39:20 +0000 (17:39 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Mon, 21 Aug 2023 13:59:42 +0000 (15:59 +0200)
distro/pkg/deb-noxdp/libknot14.symbols
distro/pkg/deb/libknot14.symbols
doc/man/kdig.1in
doc/man_kdig.rst
src/libknot/codes.c
src/libknot/codes.h
src/libknot/rrset-dump.c
src/libknot/rrset-dump.h
src/utils/common/exec.c
src/utils/common/params.h
src/utils/kdig/kdig_params.c

index 26f2c82ed127bab77c32defa64a1de6d7cd4448a..40b8265339ea2d81ccbd7984ef395c736cf2be6e 100644 (file)
@@ -132,6 +132,7 @@ libknot.so.14 libknot14 #MINVER#
  knot_rrset_to_wire_extra@Base 3.3.0
  knot_rrset_txt_dump@Base 3.3.0
  knot_rrset_txt_dump_data@Base 3.3.0
+ knot_rrset_txt_dump_edns@Base 3.3.0
  knot_rrset_txt_dump_header@Base 3.3.0
  knot_rrtype_additional_needed@Base 3.3.0
  knot_rrtype_from_string@Base 3.3.0
index 62b66aa496fa6bd68c0fec04e06b1de91f12693d..c1f05ea8afd41f73969997ddaaefe8d6756ceced 100644 (file)
@@ -162,6 +162,7 @@ libknot.so.14 libknot14 #MINVER#
  knot_rrset_to_wire_extra@Base 3.3.0
  knot_rrset_txt_dump@Base 3.3.0
  knot_rrset_txt_dump_data@Base 3.3.0
+ knot_rrset_txt_dump_edns@Base 3.3.0
  knot_rrset_txt_dump_header@Base 3.3.0
  knot_rrtype_additional_needed@Base 3.3.0
  knot_rrtype_from_string@Base 3.3.0
index 714eaf92fcd030966fc39d0639f7c2df67e550fb..12d874e6eedc6e7a7c7555e5c044da06f51720fc 100644 (file)
@@ -208,6 +208,10 @@ Show the EDNS pseudosection.
 \fB+\fP[\fBno\fP]\fBopttext\fP
 Try to show unknown EDNS options as text.
 .TP
+\fB+\fP[\fBno\fP]\fBoptpresent\fP
+Show EDNS in presentation format according to the specification in version
+\fI\%draft\-peltan\-edns\-presentation\-format\-01\fP\&.
+.TP
 \fB+\fP[\fBno\fP]\fBquestion\fP
 Show the question section.
 .TP
index 122fefa49ed97b3ea2a5c95e6ccbec851ede6e66..d40efe8e10cd8bc9d3b80baa6ef8344989cfaca0 100644 (file)
@@ -187,6 +187,10 @@ Options
 **+**\ [\ **no**\ ]\ **opttext**
   Try to show unknown EDNS options as text.
 
+**+**\ [\ **no**\ ]\ **optpresent**
+  Show EDNS in presentation format according to the specification in version
+  `draft-peltan-edns-presentation-format-01 <https://www.ietf.org/archive/id/draft-peltan-edns-presentation-format-01.html>`_.
+
 **+**\ [\ **no**\ ]\ **question**
   Show the question section.
 
index e6b38cf6c07922e375904cccab3cfc9c7538122c..9027e6f52522b4651247063057d73bb25af6762b 100644 (file)
@@ -17,6 +17,7 @@
 #include "libknot/attribute.h"
 #include "libknot/codes.h"
 #include "libknot/consts.h"
+#include "libknot/rrtype/opt.h"
 #include "libdnssec/key.h"
 
 _public_
@@ -127,3 +128,15 @@ const knot_lookup_t knot_svcb_param_names[] = {
        { KNOT_SVCB_PARAM_IPV6HINT,  "ipv6hint" },
        { 0, NULL }
 };
+
+_public_
+const knot_lookup_t knot_edns_opt_names[] = {
+       { KNOT_EDNS_OPTION_NSID,          "NSID" },
+       { KNOT_EDNS_OPTION_CLIENT_SUBNET, "ECS" },
+       { KNOT_EDNS_OPTION_EXPIRE,        "EXPIRE" },
+       { KNOT_EDNS_OPTION_COOKIE,        "COOKIE" },
+       { KNOT_EDNS_OPTION_TCP_KEEPALIVE, "KEEPALIVE" },
+       { KNOT_EDNS_OPTION_PADDING,       "PADDING" },
+       { KNOT_EDNS_OPTION_CHAIN,         "CHAIN" },
+       { KNOT_EDNS_OPTION_EDE,           "EDE" },
+};
index 2d3a548f0e21994cb317ae4d4bda6656c245cc82..60b5bd42f4692ec1177adf053fc857951eb98387 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -57,4 +57,9 @@ extern const knot_lookup_t knot_dnssec_alg_names[];
  */
 extern const knot_lookup_t knot_svcb_param_names[];
 
+/*!
+ * \brief EDNS option names.
+ */
+extern const knot_lookup_t knot_edns_opt_names[];
+
 /*! @} */
index dd814dea77423daccd767bb4bfd45534b34c8de2..657ee20efce2faa7c5b4dd1b9c1e940bd42afd48 100644 (file)
@@ -35,6 +35,7 @@
 #include "libknot/descriptor.h"
 #include "libknot/errcode.h"
 #include "libknot/lookup.h"
+#include "libknot/rrtype/opt.h"
 #include "libknot/rrtype/rrsig.h"
 #include "libknot/wire.h"
 #include "contrib/base32hex.h"
@@ -42,6 +43,7 @@
 #include "contrib/color.h"
 #include "contrib/ctype.h"
 #include "contrib/musl/inet_ntop.h"
+#include "contrib/sockaddr.h"
 #include "contrib/time.h"
 #include "contrib/wire_ctx.h"
 
@@ -74,6 +76,12 @@ typedef struct {
        size_t        out_max;
        size_t        total;
        int           ret;
+       struct {
+               uint32_t      rrset_ttl;
+               uint16_t      rrset_class;
+               uint16_t      hdr_rcode;
+               bool          present;
+       } opt;
 } rrset_dump_params_t;
 
 _public_
@@ -116,27 +124,57 @@ static void dump_string(rrset_dump_params_t *p, const char *str)
        p->total += in_len;
 }
 
+static void dump_str_len(rrset_dump_params_t *p, const uint8_t *str, size_t len)
+{
+       CHECK_PRET
+
+       if (len >= p->out_max) {
+               p->ret = -1;
+               return;
+       }
+
+       if (memcpy(p->out, str, len) == NULL) {
+               p->ret = -1;
+               return;
+       }
+
+       p->out += len;
+       p->out_max -= len;
+       p->total += len;
+
+       p->out[0] = '\0';
+}
+
+static void dump_str_uint(rrset_dump_params_t *p, const char *str, uint64_t num)
+{
+       CHECK_PRET
+
+       int ret = snprintf(p->out, p->out_max, "%s%"PRIu64"", str, num);
+       CHECK_RET_OUTMAX_SNPRINTF
+
+       p->out += ret;
+       p->out_max -= ret;
+       p->total += ret;
+}
+
+static void dump_uint(rrset_dump_params_t *p, uint64_t num)
+{
+       dump_str_uint(p, "", num);
+}
+
 static void wire_num8_to_str(rrset_dump_params_t *p)
 {
        CHECK_PRET
 
        uint8_t data = *(p->in);
        size_t  in_len = sizeof(data);
-       size_t  out_len = 0;
 
        CHECK_INMAX(in_len)
 
-       // Write number.
-       int ret = snprintf(p->out, p->out_max, "%u", data);
-       CHECK_RET_OUTMAX_SNPRINTF
-       out_len = ret;
+       dump_uint(p, data);
 
-       // Fill in output.
        p->in += in_len;
        p->in_max -= in_len;
-       p->out += out_len;
-       p->out_max -= out_len;
-       p->total += out_len;
 }
 
 static void wire_num16_to_str(rrset_dump_params_t *p)
@@ -145,24 +183,15 @@ static void wire_num16_to_str(rrset_dump_params_t *p)
 
        uint16_t data;
        size_t   in_len = sizeof(data);
-       size_t   out_len = 0;
 
        CHECK_INMAX(in_len)
 
-       // Fill in input data.
        data = knot_wire_read_u16(p->in);
 
-       // Write number.
-       int ret = snprintf(p->out, p->out_max, "%u", data);
-       CHECK_RET_OUTMAX_SNPRINTF
-       out_len = ret;
+       dump_uint(p, data);
 
-       // Fill in output.
        p->in += in_len;
        p->in_max -= in_len;
-       p->out += out_len;
-       p->out_max -= out_len;
-       p->total += out_len;
 }
 
 static void wire_num32_to_str(rrset_dump_params_t *p)
@@ -171,24 +200,15 @@ static void wire_num32_to_str(rrset_dump_params_t *p)
 
        uint32_t data;
        size_t   in_len = sizeof(data);
-       size_t   out_len = 0;
 
        CHECK_INMAX(in_len)
 
-       // Fill in input data.
        data = knot_wire_read_u32(p->in);
 
-       // Write number.
-       int ret = snprintf(p->out, p->out_max, "%u", data);
-       CHECK_RET_OUTMAX_SNPRINTF
-       out_len = ret;
+       dump_uint(p, data);
 
-       // Fill in output.
        p->in += in_len;
        p->in_max -= in_len;
-       p->out += out_len;
-       p->out_max -= out_len;
-       p->total += out_len;
 }
 
 static void wire_num48_to_str(rrset_dump_params_t *p)
@@ -197,24 +217,15 @@ static void wire_num48_to_str(rrset_dump_params_t *p)
 
        uint64_t data;
        size_t   in_len = 6;
-       size_t   out_len = 0;
 
        CHECK_INMAX(in_len)
 
-       // Fill in input data.
        data = knot_wire_read_u48(p->in);
 
-       // Write number.
-       int ret = snprintf(p->out, p->out_max, "%"PRIu64"", data);
-       CHECK_RET_OUTMAX_SNPRINTF
-       out_len = ret;
+       dump_uint(p, data);
 
-       // Fill in output.
        p->in += in_len;
        p->in_max -= in_len;
-       p->out += out_len;
-       p->out_max -= out_len;
-       p->total += out_len;
 }
 
 static void wire_ipv4_to_str(rrset_dump_params_t *p)
@@ -356,6 +367,22 @@ static int num48_encode(const uint8_t  *in,
        return ret;
 }
 
+static void wire_data_to_hex(rrset_dump_params_t *p, size_t len)
+{
+       CHECK_PRET
+
+       p->ret = hex_encode(p->in, len, (uint8_t *)(p->out), p->out_max);
+       CHECK_PRET
+       size_t out_len = p->ret;
+       p->ret = 0;
+
+       p->in += len;
+       p->in_max -= len;
+       p->out += out_len;
+       p->out_max -= out_len;
+       p->total += out_len;
+}
+
 typedef int (*encode_t)(const uint8_t *in, const uint32_t in_len,
                         uint8_t *out, const uint32_t out_len);
 
@@ -584,29 +611,11 @@ static void wire_unknown_to_str(rrset_dump_params_t *p)
        }
 }
 
-static void wire_text_to_str(rrset_dump_params_t *p, bool quote,
-                             unsigned with_header_len, bool alpn_mode)
+static void wire_text_to_str(rrset_dump_params_t *p, size_t in_len,
+                             const char *prefix, bool quote, bool alpn_mode)
 {
        CHECK_PRET
 
-       size_t in_len = 0;
-
-       CHECK_INMAX(with_header_len)
-       switch (with_header_len) {
-       case 0:
-               in_len = p->in_max;
-               break;
-       case 1:
-               in_len = *(p->in);
-               break;
-       case 2:
-               in_len = knot_wire_read_u16(p->in);
-               break;
-       default:
-               assert(0);
-       }
-       p->in += with_header_len;
-       p->in_max -= with_header_len;
        CHECK_INMAX(in_len)
 
        // Check if quotation can ever be disabled (parser protection fallback).
@@ -625,6 +634,11 @@ static void wire_text_to_str(rrset_dump_params_t *p, bool quote,
                CHECK_PRET
        }
 
+       if (prefix != NULL) {
+               dump_string(p, prefix);
+               CHECK_PRET
+       }
+
        // Loop over all characters.
        for (size_t i = 0; i < in_len; i++) {
                uint8_t ch = p->in[i];
@@ -943,12 +957,228 @@ static void wire_apl_to_str(rrset_dump_params_t *p)
        p->out_max -= out_len;
        p->total += out_len;
 
-       // Write prefix length with forward slash.
-       ret = snprintf(p->out, p->out_max, "/%u", prefix);
-       CHECK_RET_OUTMAX_SNPRINTF
-       p->out += ret;
-       p->out_max -= ret;
-       p->total += ret;
+       dump_str_uint(p, "/", prefix);
+}
+
+static void wire_ednsflags_to_str(rrset_dump_params_t *p)
+{
+       CHECK_PRET
+
+       uint16_t flags = p->opt.rrset_ttl & 0xffff, mask = (1 << 15);
+       bool hit = false;
+       for (int i = 0; i < 16; i++) {
+               if ((flags & mask)) {
+                       if (hit) {
+                               dump_string(p, ",");
+                               CHECK_PRET
+                       }
+                       hit = true;
+
+                       if ((mask & KNOT_EDNS_DO_MASK)) {
+                               dump_string(p, "DO");
+                       } else {
+                               dump_str_uint(p, "BIT", i);
+                       }
+               }
+               mask >>= 1;
+       }
+       if (!hit) {
+               dump_string(p, "0");
+       }
+}
+
+static void wire_ednsrcode_to_str(rrset_dump_params_t *p)
+{
+       CHECK_PRET
+
+       uint16_t opt_rc = (p->opt.rrset_ttl >> 24) & 0xff;
+
+       if (p->opt.hdr_rcode == 0xffff) {
+               dump_str_uint(p, "UNKNOWNRCODE", opt_rc << 4);
+       } else {
+               uint16_t rc = knot_edns_whole_rcode(opt_rc, p->opt.hdr_rcode);
+               const knot_lookup_t *item = knot_lookup_by_id(knot_rcode_names, rc);
+               if (item == NULL) {
+                       dump_str_uint(p, "RCODE", rc);
+               } else {
+                       dump_string(p, item->name);
+               }
+       }
+}
+
+static void wire_ednsudpsize_to_str(rrset_dump_params_t *p)
+{
+       CHECK_PRET
+
+       uint16_t udpsize = p->opt.rrset_class;
+       dump_uint(p, udpsize);
+}
+
+static bool all_zero(const uint8_t * const str, const size_t len)
+{
+       for (const uint8_t *p = str; p != str + len; p++) {
+               if (*p != 0) {
+                       return false;
+               }
+       }
+       return true;
+}
+
+static bool all_print(const uint8_t * const str, const size_t len)
+{
+       for (const uint8_t *p = str; p != str + len; p++) {
+               if (!is_print(*p)) {
+                       return false;
+               }
+       }
+       return true;
+}
+
+static void wire_ecs_to_str(rrset_dump_params_t *p, uint16_t optlen)
+{
+       knot_edns_client_subnet_t ecs;
+       struct sockaddr_storage addr = { 0 };
+       int ret = knot_edns_client_subnet_parse(&ecs, p->in, optlen);
+       if (ret == KNOT_EOK) {
+               ret = knot_edns_client_subnet_get_addr(&addr, &ecs);
+       }
+       if (ret == KNOT_EOK) {
+               ret = sockaddr_tostr(p->out, p->out_max, &addr);
+               CHECK_RET_OUTMAX_SNPRINTF
+               p->out += ret;
+               p->out_max -= ret;
+               p->total += ret;
+               p->in += optlen;
+               p->in_max -= optlen;
+               dump_str_uint(p, "/", ecs.source_len);
+               CHECK_PRET
+               if (ecs.scope_len != 0) {
+                       dump_str_uint(p, "/", ecs.scope_len);
+               }
+       } else {
+               wire_data_to_hex(p, optlen);
+       }
+}
+
+static void wire_ednsoptval_to_str(rrset_dump_params_t *p, uint16_t opt, uint16_t len)
+{
+       CHECK_PRET
+       assert(p->in_max >= len); // ensured by wire_ednsopt_to_str()
+
+       switch (opt) {
+       case KNOT_EDNS_OPTION_NSID:
+               wire_data_to_hex(p, len);
+               if (len > 0 && p->ret >= 0 &&
+                   p->style->wrap && p->style->verbose &&
+                   all_print(p->in - len, len)) {
+                       dump_string(p, " ; ");
+                       CHECK_PRET
+                       dump_str_len(p, p->in - len, len);
+               }
+               break;
+       case KNOT_EDNS_OPTION_CLIENT_SUBNET:
+               wire_ecs_to_str(p, len);
+               break;
+       case KNOT_EDNS_OPTION_EXPIRE:
+               if (len == sizeof(uint32_t)) {
+                       uint32_t tstamp = knot_wire_read_u32(p->in);
+                       wire_num32_to_str(p);
+
+                       char comment[64] = " ; ", comlen = strlen(comment);
+                       if (knot_time_print_human(tstamp, comment + comlen, sizeof(comment) - comlen, false) > 0) {
+                               dump_string(p, comment);
+                       }
+               } else {
+                       wire_data_to_hex(p, len); // includes correct empty output for zero-length
+               }
+               break;
+       case KNOT_EDNS_OPTION_COOKIE:
+               if (len <= KNOT_EDNS_COOKIE_CLNT_SIZE) {
+                       wire_data_to_hex(p, len);
+               } else {
+                       wire_data_to_hex(p, KNOT_EDNS_COOKIE_CLNT_SIZE);
+                       CHECK_PRET
+                       dump_string(p, ",");
+                       CHECK_PRET
+                       wire_data_to_hex(p, len - KNOT_EDNS_COOKIE_CLNT_SIZE);
+               }
+               break;
+       case KNOT_EDNS_OPTION_TCP_KEEPALIVE:
+               if (len != sizeof(uint16_t)) {
+                       wire_data_to_hex(p, len);
+               } else {
+                       uint16_t timeout = knot_wire_read_u16(p->in);
+                       p->in += sizeof(timeout);
+                       p->in_max -= sizeof(timeout);
+                       dump_uint(p, timeout / 10);
+                       CHECK_PRET
+                       dump_str_uint(p, ".", timeout % 10);
+               }
+               break;
+       case KNOT_EDNS_OPTION_PADDING:
+               if (all_zero(p->in, len)) {
+                       dump_str_uint(p, "[", len);
+                       CHECK_PRET
+                       dump_string(p, "]");
+                       p->in += len;
+                       p->in_max -= len;
+               } else {
+                       wire_data_to_hex(p, len);
+               }
+               break;
+       case KNOT_EDNS_OPTION_CHAIN:
+               wire_dname_to_str(p);
+               break;
+       case KNOT_EDNS_OPTION_EDE:
+               wire_num16_to_str(p);
+               if (p->ret >= 0 && p->style->wrap && p->style->verbose) {
+                       uint16_t ede = knot_wire_read_u16(p->in - sizeof(ede));
+                       const knot_lookup_t *item = knot_lookup_by_id(knot_edns_ede_names, ede);
+                       if (item != NULL) {
+                               dump_string(p, " ; ");
+                               CHECK_PRET
+                               dump_string(p, item->name);
+                       }
+               }
+               if (len > sizeof(uint16_t)) {
+                       dump_string(p, p->style->wrap ? BLOCK_INDENT : " ");
+                       CHECK_PRET
+                       wire_text_to_str(p, len - sizeof(uint16_t), "EDETXT=", false, false);
+               }
+               break;
+       default:
+               assert(0); // this should be handled in wire_ednsopt_to_str() by generic OPT##=hex
+               break;
+       }
+}
+
+static void wire_ednsopt_to_str(rrset_dump_params_t *p)
+{
+       CHECK_PRET
+
+       CHECK_INMAX(KNOT_EDNS_OPTION_HDRLEN)
+       uint16_t opt = knot_wire_read_u16(p->in);
+       uint16_t len = knot_wire_read_u16(p->in + sizeof(opt));
+       p->in += KNOT_EDNS_OPTION_HDRLEN;
+       p->in_max -= KNOT_EDNS_OPTION_HDRLEN;
+       CHECK_INMAX(len)
+
+       const knot_lookup_t *item = knot_lookup_by_id(knot_edns_opt_names, opt);
+       if (item == NULL) {
+               dump_str_uint(p, "OPT", opt);
+               CHECK_PRET
+               dump_string(p, "=");
+               CHECK_PRET
+               wire_data_to_hex(p, len);
+       } else {
+               dump_string(p, item->name);
+               if (len != 0) {
+                       CHECK_PRET
+                       dump_string(p, "=");
+               }
+               CHECK_PRET
+               wire_ednsoptval_to_str(p, opt, len);
+       }
 }
 
 static void wire_loc_to_str(rrset_dump_params_t *p)
@@ -1241,9 +1471,18 @@ static void wire_value_list_to_str(rrset_dump_params_t *p,
        }
 }
 
-static void wire_text_to_str1(rrset_dump_params_t *p)
+static void wire_text_to_str1(rrset_dump_params_t *p, bool quote, bool alpn_mode)
+{
+       CHECK_INMAX(1)
+       uint8_t len = *p->in;
+       p->in++;
+       p->in_max--;
+       wire_text_to_str(p, len, NULL, quote, alpn_mode);
+}
+
+static void wire_text_to_str_alpn(rrset_dump_params_t *p)
 {
-       wire_text_to_str(p, false, 1, true);
+       wire_text_to_str1(p, false, true);
 }
 
 static void wire_ech_to_base64(rrset_dump_params_t *p, unsigned ech_len)
@@ -1288,7 +1527,7 @@ static void wire_svcparam_to_str(rrset_dump_params_t *p)
                        wire_value_list_to_str(p, wire_svcb_paramkey_to_str, p->in + val_len);
                        break;
                case KNOT_SVCB_PARAM_ALPN:
-                       wire_value_list_to_str(p, wire_text_to_str1, p->in + val_len);
+                       wire_value_list_to_str(p, wire_text_to_str_alpn, p->in + val_len);
                        break;
                case KNOT_SVCB_PARAM_NDALPN:
                        p->ret = -1; // must not have value
@@ -1310,9 +1549,7 @@ static void wire_svcparam_to_str(rrset_dump_params_t *p)
                        wire_value_list_to_str(p, wire_ipv6_to_str, p->in + val_len);
                        break;
                default:
-                       p->in -= sizeof(val_len); // Rollback to where the string length resides.
-                       p->in_max += sizeof(val_len);
-                       wire_text_to_str(p, true, sizeof(val_len), false);
+                       wire_text_to_str(p, val_len, NULL, true, false);
                }
        }
 }
@@ -1512,10 +1749,14 @@ static void dnskey_info(const uint8_t *rdata,
                                2, true, ""); CHECK_RET(p);
 #define DUMP_OMIT      wire_data_omit(p); CHECK_RET(p);
 #define DUMP_KEY_OMIT  wire_dnskey_to_tag(p); CHECK_RET(p);
-#define DUMP_TEXT      wire_text_to_str(p, true,  1, false); CHECK_RET(p);
-#define DUMP_LONG_TEXT wire_text_to_str(p, true,  0, false); CHECK_RET(p);
-#define DUMP_UNQUOTED  wire_text_to_str(p, false, 1, false); CHECK_RET(p);
+#define DUMP_TEXT      wire_text_to_str1(p, true, false); CHECK_RET(p);
+#define DUMP_LONG_TEXT wire_text_to_str(p, p->in_max, NULL, true, false); CHECK_RET(p);
+#define DUMP_UNQUOTED  wire_text_to_str1(p, false, false); CHECK_RET(p);
 #define DUMP_BITMAP    wire_bitmap_to_str(p); CHECK_RET(p);
+#define DUMP_EDNS_FL   dump_string(p, "FLAGS="); CHECK_RET(p); wire_ednsflags_to_str(p); CHECK_RET(p);
+#define DUMP_EDNS_RC   dump_string(p, "RCODE="); CHECK_RET(p); wire_ednsrcode_to_str(p); CHECK_RET(p);
+#define DUMP_EDNS_US   dump_string(p, "UDPSIZE="); CHECK_RET(p); wire_ednsudpsize_to_str(p); CHECK_RET(p);
+#define DUMP_EDNS_OPT  wire_ednsopt_to_str(p); CHECK_RET(p);
 #define DUMP_APL       wire_apl_to_str(p); CHECK_RET(p);
 #define DUMP_LOC       wire_loc_to_str(p); CHECK_RET(p);
 #define DUMP_GATEWAY   wire_gateway_to_str(p); CHECK_RET(p);
@@ -1525,6 +1766,19 @@ static void dnskey_info(const uint8_t *rdata,
 #define DUMP_SVCPARAM  wire_svcparam_to_str(p); CHECK_RET(p);
 #define DUMP_UNKNOWN   wire_unknown_to_str(p); CHECK_RET(p);
 
+static int dump_unknown(DUMP_PARAMS)
+{
+       if (p->style->wrap) {
+               WRAP_INIT;
+               DUMP_UNKNOWN;
+               WRAP_END;
+       } else {
+               DUMP_UNKNOWN;
+       }
+
+       DUMP_END;
+}
+
 static int dump_a(DUMP_PARAMS)
 {
        DUMP_IPV4;
@@ -1686,6 +1940,32 @@ static int dump_cert(DUMP_PARAMS)
        DUMP_END;
 }
 
+static int dump_opt(DUMP_PARAMS)
+{
+       if (!p->opt.present) {
+               return dump_unknown(p);
+       }
+
+       if (p->style->wrap) {
+               WRAP_INIT;
+               DUMP_EDNS_FL; WRAP_LINE;
+               DUMP_EDNS_RC; WRAP_LINE;
+               DUMP_EDNS_US;
+               while (p->in_max > 0) {
+                       WRAP_LINE; DUMP_EDNS_OPT;
+               }
+               WRAP_END;
+       } else {
+               DUMP_EDNS_FL; DUMP_SPACE;
+               DUMP_EDNS_RC; DUMP_SPACE;
+               DUMP_EDNS_US;
+               while (p->in_max > 0) {
+                       DUMP_SPACE; DUMP_EDNS_OPT;
+               }
+       }
+       DUMP_END;
+}
+
 static int dump_apl(DUMP_PARAMS)
 {
        // Print list of APLs (empty list is allowed).
@@ -1963,19 +2243,6 @@ static int dump_svcb(DUMP_PARAMS)
        DUMP_END;
 }
 
-static int dump_unknown(DUMP_PARAMS)
-{
-       if (p->style->wrap) {
-               WRAP_INIT;
-               DUMP_UNKNOWN;
-               WRAP_END;
-       } else {
-               DUMP_UNKNOWN;
-       }
-
-       DUMP_END;
-}
-
 static int txt_dump_data(rrset_dump_params_t *p, uint16_t type)
 {
        switch (type) {
@@ -2016,6 +2283,8 @@ static int txt_dump_data(rrset_dump_params_t *p, uint16_t type)
                        return dump_naptr(p);
                case KNOT_RRTYPE_CERT:
                        return dump_cert(p);
+               case KNOT_RRTYPE_OPT:
+                       return dump_opt(p);
                case KNOT_RRTYPE_APL:
                        return dump_apl(p);
                case KNOT_RRTYPE_DS:
@@ -2114,6 +2383,58 @@ int knot_rrset_txt_dump_data(const knot_rrset_t      *rrset,
        return ret;
 }
 
+_public_
+int knot_rrset_txt_dump_edns(const knot_rrset_t      *rrset,
+                             const uint16_t          hdr_rcode,
+                             char                    *dst,
+                             const size_t            maxlen,
+                             const knot_dump_style_t *style)
+{
+       if (rrset == NULL || dst == NULL || style == NULL) {
+               return KNOT_EINVAL;
+       }
+
+       knot_rdata_t *rr_data = knot_rdataset_at(&rrset->rrs, 0);
+       if (rr_data == NULL) {
+               return KNOT_EINVAL; /* bad pos or rrset->rrs */
+       }
+
+       uint8_t *data = rr_data->data;
+       uint16_t data_len = rr_data->len;
+
+       rrset_dump_params_t p = {
+               .style = style,
+               .in = data,
+               .in_max = data_len,
+               .out = dst,
+               .out_max = maxlen,
+               .total = 0,
+               .ret = 0,
+               .opt = {
+                       .rrset_ttl = rrset->ttl,
+                       .rrset_class = rrset->rclass,
+                       .hdr_rcode = hdr_rcode,
+                       .present = true,
+               }
+       };
+
+       int ret;
+
+       if (style->generic) {
+               ret = dump_unknown(&p);
+       } else {
+               ret = txt_dump_data(&p, rrset->type);
+       }
+
+       // Terminate the string just in case.
+       if (ret < 0 || ret >= maxlen) {
+               return KNOT_ESPACE;
+       }
+       dst[ret] = '\0';
+
+       return ret;
+}
+
 _public_
 int knot_rrset_txt_dump_header(const knot_rrset_t      *rrset,
                                const uint32_t          ttl,
index 4631531758d94ffe43d9de3c4c77f3228768ab1c..d15e9908cfc87770842a41151e61edd6698ecacd 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -99,6 +99,25 @@ int knot_rrset_txt_dump_data(const knot_rrset_t      *rrset,
                              const size_t            maxlen,
                              const knot_dump_style_t *style);
 
+/*!
+ * \brief Dumps ENDS(0) OPT record.
+ *
+ * \param rrset                OPT record.
+ * \param hdr_rcode    The four lower bits of RCODE from the message header.
+ *                     If set to 0xffff, UNKNOWNRCODE### is dumped.
+ * \param dst          Output buffer.
+ * \param maxlen       Output buffer size.
+ * \param style                Output style.
+ *
+ * \retval output length       if success.
+ * \retval < 0                 if error.
+ */
+int knot_rrset_txt_dump_edns(const knot_rrset_t      *rrset,
+                             const uint16_t          hdr_rcode,
+                             char                    *dst,
+                             const size_t            maxlen,
+                             const knot_dump_style_t *style);
+
 /*!
  * \brief Dumps rrset, re-allocates dst to double (4x, 8x, ...) if too small.
  *
index d2cbe4e66e93a2567cbdcddcd0de5733d660c155..23f7b975f6f94aa5b44127c3bae0678de8abc7fa 100644 (file)
@@ -297,6 +297,21 @@ static void print_expire(const uint8_t *data, uint16_t len)
 
 static void print_section_opt(const knot_pkt_t *packet, const style_t *style)
 {
+       if (style->present_edns) {
+               size_t buflen = 8192;
+               char *buf = calloc(buflen, 1);
+               int ret = knot_rrset_txt_dump_edns(packet->opt_rr,
+                                                  knot_wire_get_rcode(packet->wire),
+                                                  buf, buflen, &style->style);
+               if (ret < 0) {
+                       WARN("can't print OPT record (%s)", knot_strerror(ret));
+               } else {
+                       printf(". 0 ANY EDNS0\t\t\t%s\n", buf);
+               }
+               free(buf);
+               return;
+       }
+
        char unknown_ercode[64] = "";
        const char *ercode_str = NULL;
 
@@ -314,7 +329,7 @@ static void print_section_opt(const knot_pkt_t *packet, const style_t *style)
                ercode_str = unknown_ercode;
        }
 
-       printf("Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n",
+       printf(";; Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n",
               knot_edns_get_version(packet->opt_rr),
               (knot_edns_do(packet->opt_rr) != 0) ? "do" : "",
               knot_edns_get_payload(packet->opt_rr),
@@ -908,7 +923,7 @@ void print_packet(const knot_pkt_t *packet,
 
        // Print EDNS section.
        if (style->show_edns && knot_pkt_has_edns(packet)) {
-               printf("%s", style->show_section ? "\n;; EDNS PSEUDOSECTION:\n;; " : ";;");
+               printf("%s", style->show_section ? "\n;; EDNS PSEUDOSECTION:\n" : ";;");
                print_section_opt(packet, style);
        }
 
index d70d3e028db5d9fa848c31b5840a7fed875287d5..8b7565e98aef600f55fd6bc50a214abfd6e34056 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -94,6 +94,8 @@ typedef struct {
        bool    show_tsig;
        /*!< Show footer info. */
        bool    show_footer;
+       /*!< Display EDNS in Presentation format. */
+       bool    present_edns;
 
        /*!< KHOST - Hide CNAME record in answer (duplicity reduction). */
        bool    hide_cname;
index 58ab5c52ec307fb0d3ca307d87090c62ba7c96d9..f72fbd2b5350b72071e6c3325902dab1a06b3928 100644 (file)
@@ -432,6 +432,24 @@ static int opt_noquestion(const char *arg, void *query)
        return KNOT_EOK;
 }
 
+static int opt_optpresent(const char *arg, void *query)
+{
+       query_t *q = query;
+
+       q->style.present_edns = true;
+
+       return KNOT_EOK;
+}
+
+static int opt_nooptpresent(const char *arg, void *query)
+{
+       query_t *q = query;
+
+       q->style.present_edns = false;
+
+       return KNOT_EOK;
+}
+
 static int opt_answer(const char *arg, void *query)
 {
        query_t *q = query;
@@ -1513,6 +1531,9 @@ static const param_t kdig_opts2[] = {
        { "opttext",        ARG_NONE,     opt_opttext },
        { "noopttext",      ARG_NONE,     opt_noopttext },
 
+       { "optpresent",     ARG_NONE,     opt_optpresent },
+       { "nooptpresent",   ARG_NONE,     opt_nooptpresent },
+
        { "question",       ARG_NONE,     opt_question },
        { "noquestion",     ARG_NONE,     opt_noquestion },
 
@@ -2323,6 +2344,7 @@ static void print_help(void)
               "       +[no]comments              Show commented section names.\n"
               "       +[no]opt                   Show EDNS pseudosection.\n"
               "       +[no]opttext               Try to show unknown EDNS options as text.\n"
+              "       +[no]optpresent            Show EDNS in presenatation format.\n"
               "       +[no]question              Show question section.\n"
               "       +[no]answer                Show answer section.\n"
               "       +[no]authority             Show authority section.\n"