diff --git a/doc/man/kdig.1in b/doc/man/kdig.1in index 7670f9f1b3f4affe3b2a59bbf14f09e98ae60cbf..f4472bde42ccfb4901ed981596c8e75cbca43c9f 100644 --- a/doc/man/kdig.1in +++ b/doc/man/kdig.1in @@ -207,6 +207,13 @@ Request the nameserver identifier (NSID). \fB+\fP[\fBno\fP]\fBbufsize\fP=\fIB\fP Set the EDNS buffer size in bytes (default is 512 bytes). .TP +\fB+\fP[\fBno\fP]\fBpadding\fP=\fIB\fP +Set EDNS(0) padding option data length (default is no). +.TP +\fB+\fP[\fBno\fP]\fBalignment\fP[=\fIB\fP] +Align the query to B\-byte\-block message using the EDNS(0) padding option +(default is no or 128 if no argument is specified). +.TP \fB+\fP[\fBno\fP]\fBclient\fP=\fISUBN\fP Set the EDNS client subnet SUBN=IP/prefix. .TP diff --git a/doc/man_kdig.rst b/doc/man_kdig.rst index 52e33fb292b0f56b3a49b35155f8f658f39085ee..0ada1822ccb61baf38b8ad34b7f7835bc90710fe 100644 --- a/doc/man_kdig.rst +++ b/doc/man_kdig.rst @@ -184,6 +184,13 @@ Options **+**\ [\ **no**\ ]\ **bufsize**\ =\ *B* Set the EDNS buffer size in bytes (default is 512 bytes). +**+**\ [\ **no**\ ]\ **padding**\ =\ *B* + Set EDNS(0) padding option data length (default is no). + +**+**\ [\ **no**\ ]\ **alignment**\[\ =\ *B*\] + Align the query to B\-byte-block message using the EDNS(0) padding option + (default is no or 128 if no argument is specified). + **+**\ [\ **no**\ ]\ **client**\ =\ *SUBN* Set the EDNS client subnet SUBN=IP/prefix. diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h index b16eb5384ca6bdfb1d762a7eda1bb7cfd1749962..76a1305b6ea7ee2030b7369789ec1e756f0d4615 100644 --- a/src/libknot/rrtype/opt.h +++ b/src/libknot/rrtype/opt.h @@ -24,6 +24,8 @@ #pragma once +#include <assert.h> + #include "libknot/consts.h" #include "libknot/rrset.h" @@ -53,7 +55,9 @@ enum knot_edns_const { /*! \brief EDNS client subnet option code. */ KNOT_EDNS_OPTION_CLIENT_SUBNET = 8, /*! \brief EDNS DNS Cookie option code. */ - KNOT_EDNS_OPTION_COOKIE = 10 + KNOT_EDNS_OPTION_COOKIE = 10, + /*! \brief EDNS Padding option code. */ + KNOT_EDNS_OPTION_PADDING = 12 }; /* Helpers for splitting extended RCODE. */ @@ -385,4 +389,31 @@ int knot_edns_client_subnet_parse(const uint8_t *data, uint16_t *addr_len, uint8_t *src_mask, uint8_t *dst_mask); + +/*! + * \brief Computes additional Padding data length for required packet alignment. + * + * \param current_pkt_size Current packet size. + * \param current_opt_size Current OPT rrset size (OPT must be used). + * \param block_size Required packet block length (must be non-zero). + * + * \return Required padding length or -1 if padding not required. + */ +static inline int knot_edns_alignment_size(size_t current_pkt_size, + size_t current_opt_size, + size_t block_size) +{ + assert(current_opt_size > 0); + assert(block_size > 0); + + size_t current_size = current_pkt_size + current_opt_size; + if (current_size % block_size == 0) { + return -1; + } + + size_t modulo = (current_size + KNOT_EDNS_OPTION_HDRLEN) % block_size; + + return (modulo == 0) ? 0 : block_size - modulo; +} + /*! @} */ diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c index eb00fc1e803463e5fa39d285fee8ae3efacac96d..1362c4155f011d3c598f7e7fe1982c322b4d3f41 100644 --- a/src/utils/common/exec.c +++ b/src/utils/common/exec.c @@ -262,6 +262,9 @@ static void print_section_opt(const knot_rrset_t *rr, const uint8_t rcode) printf(";; CLIENT-SUBNET: "); print_edns_client_subnet(opt_data, opt_len); break; + case KNOT_EDNS_OPTION_PADDING: + printf(";; PADDING: %u B\n", opt_len); + break; default: printf(";; Option (%u): ", opt_code); short_hex_print(opt_data, opt_len); @@ -462,7 +465,7 @@ static void print_error_host(const uint16_t code, free(owner); } -knot_pkt_t *create_empty_packet(const size_t max_size) +knot_pkt_t *create_empty_packet(const uint16_t max_size) { // Create packet skeleton. knot_pkt_t *packet = knot_pkt_new(NULL, max_size, NULL); diff --git a/src/utils/common/exec.h b/src/utils/common/exec.h index 90e5aea54baa37a710fa1580d208fd4ae3b6a986..0c5ad5dfb9d8ffdad6ea5ee3ac140c6b24f92e60 100644 --- a/src/utils/common/exec.h +++ b/src/utils/common/exec.h @@ -38,7 +38,7 @@ * \retval packet if success. * \retval NULL if error. */ -knot_pkt_t *create_empty_packet(const size_t max_size); +knot_pkt_t *create_empty_packet(const uint16_t max_size); /*! * \brief Prints information header for transfer. diff --git a/src/utils/kdig/kdig_exec.c b/src/utils/kdig/kdig_exec.c index d2821c9d3f48f84e50ded48b51b97511a656da79..5b9ecc3ceef7abee25bcd41b2c56b9b5f8ec4b81 100644 --- a/src/utils/kdig/kdig_exec.c +++ b/src/utils/kdig/kdig_exec.c @@ -270,6 +270,25 @@ static int add_query_edns(knot_pkt_t *packet, const query_t *query, uint16_t max } } + /* Append EDNS Padding. */ + int padding = query->padding; + if (query->alignment > 0) { + padding = knot_edns_alignment_size(packet->size, + knot_rrset_size(&opt_rr), + query->alignment); + } + if (padding > -1) { + uint8_t zeros[padding]; + memset(zeros, 0, sizeof(zeros)); + + ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_PADDING, + padding, zeros, &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + } + /* Add prepared OPT to packet. */ ret = knot_pkt_put(packet, KNOT_COMPR_HINT_NONE, &opt_rr, KNOT_PF_FREE); if (ret != KNOT_EOK) { @@ -282,7 +301,8 @@ static int add_query_edns(knot_pkt_t *packet, const query_t *query, uint16_t max static bool use_edns(const query_t *query) { return query->edns > -1 || query->udp_size > -1 || query->nsid || - query->flags.do_flag || query->subnet != NULL; + query->flags.do_flag || query->subnet != NULL || + query->padding > -1 || query->alignment > 0; } static knot_pkt_t *create_query_packet(const query_t *query) diff --git a/src/utils/kdig/kdig_params.c b/src/utils/kdig/kdig_params.c index 6a298f7de8609f56dbd15b2ec91654adfdea0862..ebeb0eacf90f0d90ba950a27e79ba3e8d496ecf4 100644 --- a/src/utils/kdig/kdig_params.c +++ b/src/utils/kdig/kdig_params.c @@ -35,6 +35,7 @@ #define DEFAULT_RETRIES_DIG 2 #define DEFAULT_TIMEOUT_DIG 5 +#define DEFAULT_ALIGNMENT_SIZE 128 static const flags_t DEFAULT_FLAGS_DIG = { .aa_flag = false, @@ -596,6 +597,58 @@ static int opt_nobufsize(const char *arg, void *query) return KNOT_EOK; } +static int opt_padding(const char *arg, void *query) +{ + query_t *q = query; + + uint16_t num; + if (str_to_u16(arg, &num) != KNOT_EOK) { + ERR("invalid +padding=%s\n", arg); + return KNOT_EINVAL; + } + + q->padding = num; + + return KNOT_EOK; +} + +static int opt_nopadding(const char *arg, void *query) +{ + query_t *q = query; + + q->padding = -1; + + return KNOT_EOK; +} + +static int opt_alignment(const char *arg, void *query) +{ + query_t *q = query; + + if (arg == NULL) { + q->alignment = DEFAULT_ALIGNMENT_SIZE; + return KNOT_EOK; + } else { + uint16_t num; + if (str_to_u16(arg, &num) != KNOT_EOK || num < 2) { + ERR("invalid +alignment=%s\n", arg); + return KNOT_EINVAL; + } + + q->alignment = num; + return KNOT_EOK; + } +} + +static int opt_noalignment(const char *arg, void *query) +{ + query_t *q = query; + + q->alignment = 0; + + return KNOT_EOK; +} + static int opt_client(const char *arg, void *query) { query_t *q = query; @@ -693,6 +746,8 @@ static int opt_noedns(const char *arg, void *query) opt_nodoflag(arg, query); opt_nonsid(arg, query); opt_nobufsize(arg, query); + opt_nopadding(arg, query); + opt_noalignment(arg, query); opt_noclient(arg, query); return KNOT_EOK; @@ -835,6 +890,12 @@ static const param_t kdig_opts2[] = { { "bufsize", ARG_REQUIRED, opt_bufsize }, { "nobufsize", ARG_NONE, opt_nobufsize }, + { "padding", ARG_REQUIRED, opt_padding }, + { "nopadding", ARG_NONE, opt_nopadding }, + + { "alignment", ARG_OPTIONAL, opt_alignment }, + { "noalignment", ARG_NONE, opt_noalignment }, + { "client", ARG_REQUIRED, opt_client }, { "noclient", ARG_NONE, opt_noclient }, @@ -895,6 +956,8 @@ query_t *query_create(const char *owner, const query_t *conf) query->idn = true; query->nsid = false; query->edns = -1; + query->padding = -1; + query->alignment = 0; //query->tsig_key query->subnet = NULL; #if USE_DNSTAP @@ -930,6 +993,8 @@ query_t *query_create(const char *owner, const query_t *conf) query->idn = conf->idn; query->nsid = conf->nsid; query->edns = conf->edns; + query->padding = conf->padding; + query->alignment = conf->alignment; if (conf->tsig_key.name != NULL) { int ret = knot_tsig_key_copy(&query->tsig_key, &conf->tsig_key); @@ -1410,6 +1475,8 @@ static void print_help(void) " +[no]ignore Don't use TCP automatically if truncated.\n" " +[no]nsid Request NSID.\n" " +[no]bufsize=B Set EDNS buffer size.\n" + " +[no]padding=N Padding block size EDNS(0) padding.\n" + " +[no]alignment(=N) Set packet alignment with EDNS(0) padding.\n" " +[no]client=SUBN Set EDNS(0) client subnet IP/prefix.\n" " +[no]edns(=N) Use EDNS (=version).\n" " +[no]time=T Set wait for reply interval in seconds.\n" diff --git a/src/utils/kdig/kdig_params.h b/src/utils/kdig/kdig_params.h index f08c69d33ae5bd20df23ba55d7962493cb0bddcc..5b5d5e7165db325206594220b7f09520dc480043 100644 --- a/src/utils/kdig/kdig_params.h +++ b/src/utils/kdig/kdig_params.h @@ -132,6 +132,10 @@ struct query { knot_tsig_key_t tsig_key; /*!< EDNS client subnet. */ subnet_t *subnet; + /*!< EDNS0 padding (16unsigned + -1 uninitialized). */ + int32_t padding; + /*!< Query alignment with EDNS0 padding (0 ~ uninitialized). */ + uint16_t alignment; #if USE_DNSTAP /*!< Context for dnstap reader input. */ dt_reader_t *dt_reader; diff --git a/tests/libknot/test_edns.c b/tests/libknot/test_edns.c index bb67ccad08799375b3c5dbd2272e55221a762983..e52360d2830f36199a183d736734b3ac319d69b7 100644 --- a/tests/libknot/test_edns.c +++ b/tests/libknot/test_edns.c @@ -826,6 +826,26 @@ static void test_client_subnet() "EDNS-client-subnet: parse (cmp addr)"); } +static void test_alignment() +{ + int ret; + + ret = knot_edns_alignment_size(1, 1, 1); + ok(ret == -1, "no alignment"); + + ret = knot_edns_alignment_size(1, 1, 2); + ok(ret == -1, "no alignment"); + + ret = knot_edns_alignment_size(1, 1, 3); + ok(ret == (6 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret); + + ret = knot_edns_alignment_size(1, 1, 4); + ok(ret == (8 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret); + + ret = knot_edns_alignment_size(1, 1, 512); + ok(ret == (512 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret); +} + int main(int argc, char *argv[]) { plan_lazy(); @@ -842,6 +862,7 @@ int main(int argc, char *argv[]) test_remove(); test_unique(); test_client_subnet(); + test_alignment(); knot_rrset_clear(&opt_rr, NULL);