Commit fd5c79f8 authored by Daniel Salzman's avatar Daniel Salzman

kdig: add EDNS0 Padding and query alignment support

parent fefd23ed
......@@ -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
......
......@@ -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.
......
......@@ -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;
}
/*! @} */
......@@ -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);
......
......@@ -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.
......
......@@ -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)
......
......@@ -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"
......
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment