Commit 58043325 authored by Evan Hunt's avatar Evan Hunt
Browse files

[master] EDNS padding and keepalive support

4549.	[func]		Added support for the EDNS TCP Keepalive option
			(RFC 7828). [RT #42126]

4548.	[func]		Added support for the EDNS Padding option (RFC 7830).
			[RT #42094]
parent e0d4e0ee
4549. [func] Added support for the EDNS TCP Keepalive option
(RFC 7828). [RT #42126]
4548. [func] Added support for the EDNS Padding option (RFC 7830).
[RT #42094]
4547. [port] Add support for --enable-native-pkcs11 on the AEP
Keyper HSM. [RT #42463]
......
......@@ -191,6 +191,7 @@ help(void) {
" +[no]identify (ID responders in short answers)\n"
" +[no]idnout (convert IDN response)\n"
" +[no]ignore (Don't revert to TCP for TC responses.)\n"
" +[no]keepalive (Request EDNS TCP keepalive)\n"
" +[no]keepopen (Keep the TCP socket open between queries)\n"
" +[no]mapped (Allow mapped IPv4 over IPv6)\n"
" +[no]multiline (Print records in an expanded format)\n"
......@@ -199,6 +200,7 @@ help(void) {
" +[no]nssearch (Search all authoritative nameservers)\n"
" +[no]onesoa (AXFR prints only one soa record)\n"
" +[no]opcode=### (Set the opcode of the request)\n"
" +padding=### (Set padding block size [0])\n"
" +[no]qr (Print question before sending)\n"
" +[no]question (Control display of question section)\n"
" +[no]rdflag (Recursive mode (+[no]recurse))\n"
......@@ -1084,8 +1086,36 @@ plus_option(const char *option, isc_boolean_t is_batchfile,
}
break;
case 'k':
FULLCHECK("keepopen");
keep_open = state;
switch (cmd[1]) {
case 'e':
switch (cmd[2]) {
case 'e':
switch (cmd[3]) {
case 'p':
switch (cmd[4]) {
case 'a':
FULLCHECK("keepalive");
lookup->tcp_keepalive = state;
break;
case 'o':
FULLCHECK("keepopen");
keep_open = state;
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
case 'm': /* multiline */
switch (cmd[1]) {
......@@ -1187,6 +1217,17 @@ plus_option(const char *option, isc_boolean_t is_batchfile,
goto invalid_option;
}
break;
case 'p':
FULLCHECK("padding");
if (state && lookup->edns == -1)
lookup->edns = 0;
if (value == NULL)
goto need_value;
result = parse_uint(&num, value, 512, "padding");
if (result != ISC_R_SUCCESS)
fatal("Couldn't parse padding");
lookup->padding = (isc_uint16_t)num;
break;
case 'q':
switch (cmd[1]) {
case 'r': /* qr */
......
......@@ -876,6 +876,23 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>+padding=value</option></term>
<listitem>
<para>
Pad the size of the query packet using the EDNS Padding option
to blocks of <parameter>value</parameter> bytes. For example,
<option>+padding=32</option> would cause a 48-byte query to
be padded to 64 bytes. The default block size is 0, which
disables padding. The maximum is 512. Values are
ordinarily expected to be powers of two, such as 128;
however, this is not mandatory. Responses to
padded queries may also be padded, but only if the query
uses TCP or DNS COOKIE.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>+[no]qr</option></term>
<listitem>
......@@ -1029,13 +1046,13 @@
specified IP address or network prefix.
</para>
<para>
<command>dig +subnet=0.0.0.0/0</command>, or simply
<command>dig +subnet=0</command> for short, sends an EDNS
CLIENT-SUBNET option with an empty address and a source
prefix-length of zero, which signals a resolver that
the client's address information must
<emphasis>not</emphasis> be used when resolving
this query.
<command>dig +subnet=0.0.0.0/0</command>, or simply
<command>dig +subnet=0</command> for short, sends an EDNS
CLIENT-SUBNET option with an empty address and a source
prefix-length of zero, which signals a resolver that
the client's address information must
<emphasis>not</emphasis> be used when resolving
this query.
</para>
</listitem>
</varlistentry>
......
......@@ -775,6 +775,8 @@ make_empty_lookup(void) {
looknew->opcode = dns_opcode_query;
looknew->expire = ISC_FALSE;
looknew->nsid = ISC_FALSE;
looknew->tcp_keepalive = ISC_FALSE;
looknew->padding = 0;
looknew->header_only = ISC_FALSE;
looknew->sendcookie = ISC_FALSE;
looknew->seenbadcookie = ISC_FALSE;
......@@ -889,6 +891,7 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
looknew->opcode = lookold->opcode;
looknew->expire = lookold->expire;
looknew->nsid = lookold->nsid;
looknew->tcp_keepalive = lookold->tcp_keepalive;
looknew->header_only = lookold->header_only;
looknew->sendcookie = lookold->sendcookie;
looknew->seenbadcookie = lookold->seenbadcookie;
......@@ -897,6 +900,7 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
looknew->ednsopts = lookold->ednsopts;
looknew->ednsoptscnt = lookold->ednsoptscnt;
looknew->ednsneg = lookold->ednsneg;
looknew->padding = lookold->padding;
looknew->mapped = lookold->mapped;
looknew->multiline = lookold->multiline;
looknew->nottl = lookold->nottl;
......@@ -1579,8 +1583,11 @@ setup_libs(void) {
check_result(result, "isc_mutex_init");
}
#define EDNSOPTS 100U
static dns_ednsopt_t ednsopts[EDNSOPTS];
/*
* Array of up to 100 options configured by +ednsopt
*/
#define EDNSOPT_OPTIONS 100U
static dns_ednsopt_t ednsopts[EDNSOPT_OPTIONS];
static unsigned char ednsoptscnt = 0;
void
......@@ -1589,7 +1596,7 @@ save_opt(dig_lookup_t *lookup, char *code, char *value) {
isc_buffer_t b;
isc_result_t result;
if (ednsoptscnt == EDNSOPTS)
if (ednsoptscnt == EDNSOPT_OPTIONS)
fatal("too many ednsopts");
result = parse_uint(&num, code, 65535, "ednsopt");
......@@ -2520,17 +2527,24 @@ setup_lookup(dig_lookup_t *lookup) {
if (lookup->udpsize > 0 || lookup->dnssec ||
lookup->edns > -1 || lookup->ecs_addr != NULL)
{
dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS];
#define MAXOPTS (EDNSOPT_OPTIONS + DNS_EDNSOPTIONS)
dns_ednsopt_t opts[MAXOPTS];
unsigned int flags;
int i = 0;
unsigned int i = 0;
/*
* There can't be more than MAXOPTS options to send:
* a maximum of EDNSOPT_OPTIONS set by +ednsopt
* and DNS_EDNSOPTIONS set by other arguments
* (+nsid, +cookie, etc).
*/
if (lookup->udpsize == 0)
lookup->udpsize = 4096;
if (lookup->edns < 0)
lookup->edns = 0;
if (lookup->nsid) {
INSIST(i < DNS_EDNSOPTIONS);
INSIST(i < MAXOPTS);
opts[i].code = DNS_OPT_NSID;
opts[i].length = 0;
opts[i].value = NULL;
......@@ -2552,7 +2566,7 @@ setup_lookup(dig_lookup_t *lookup) {
/* Round up prefix len to a multiple of 8 */
addrl = (plen + 7) / 8;
INSIST(i < DNS_EDNSOPTIONS);
INSIST(i < MAXOPTS);
opts[i].code = DNS_OPT_CLIENT_SUBNET;
opts[i].length = (isc_uint16_t) addrl + 4;
check_result(result, "isc_buffer_allocate");
......@@ -2621,7 +2635,7 @@ setup_lookup(dig_lookup_t *lookup) {
}
if (lookup->sendcookie) {
INSIST(i < DNS_EDNSOPTIONS);
INSIST(i < MAXOPTS);
opts[i].code = DNS_OPT_COOKIE;
if (lookup->cookie != NULL) {
isc_buffer_init(&b, cookiebuf,
......@@ -2640,19 +2654,43 @@ setup_lookup(dig_lookup_t *lookup) {
}
if (lookup->expire) {
INSIST(i < DNS_EDNSOPTIONS);
INSIST(i < MAXOPTS);
opts[i].code = DNS_OPT_EXPIRE;
opts[i].length = 0;
opts[i].value = NULL;
i++;
}
if (lookup->tcp_keepalive) {
INSIST(i < MAXOPTS);
opts[i].code = DNS_OPT_TCP_KEEPALIVE;
opts[i].length = 0;
opts[i].value = NULL;
i++;
}
if (lookup->ednsoptscnt != 0) {
INSIST(i + lookup->ednsoptscnt <= MAXOPTS);
memmove(&opts[i], lookup->ednsopts,
sizeof(dns_ednsopt_t) * lookup->ednsoptscnt);
i += lookup->ednsoptscnt;
}
if (lookup->padding && (i >= MAXOPTS)) {
debug("turned off padding because of EDNS overflow");
lookup->padding = 0;
}
if (lookup->padding) {
INSIST(i < MAXOPTS);
opts[i].code = DNS_OPT_PAD;
opts[i].length = 0;
opts[i].value = NULL;
i++;
dns_message_setpadding(lookup->sendmsg,
lookup->padding);
}
flags = lookup->ednsflags;
flags &= ~DNS_MESSAGEEXTFLAG_DO;
if (lookup->dnssec)
......
......@@ -126,6 +126,7 @@ struct dig_lookup {
seenbadcookie,
badcookie,
nsid, /*% Name Server ID (RFC 5001) */
tcp_keepalive,
header_only,
ednsneg,
mapped,
......@@ -184,6 +185,7 @@ isc_boolean_t sigchase;
int nsfound;
isc_uint16_t udpsize;
isc_int16_t edns;
isc_int16_t padding;
isc_uint32_t ixfr_serial;
isc_buffer_t rdatabuf;
char rdatastore[MXNAME];
......
......@@ -118,6 +118,8 @@
#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
#define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0)
#define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
#define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
/*% nameserver client manager structure */
struct ns_clientmgr {
......@@ -228,7 +230,8 @@ struct ns_clientmgr {
unsigned int ns_client_requests;
static void client_read(ns_client_t *client);
static void read_settimeout(ns_client_t *client, isc_boolean_t newconn);
static void client_read(ns_client_t *client, isc_boolean_t newconn);
static void client_accept(ns_client_t *client);
static void client_udprecv(ns_client_t *client);
static void clientmgr_destroy(ns_clientmgr_t *manager);
......@@ -291,6 +294,32 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
}
}
static void
read_settimeout(ns_client_t *client, isc_boolean_t newconn) {
isc_result_t result;
isc_interval_t interval;
unsigned int ds;
if (newconn)
ds = ns_g_initialtimo;
else if (USEKEEPALIVE(client))
ds = ns_g_keepalivetimo;
else
ds = ns_g_idletimo;
isc_interval_set(&interval, ds / 10, 100000000 * (ds % 10));
result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
&interval, ISC_FALSE);
client->timerset = ISC_TRUE;
if (result != ISC_R_SUCCESS) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
"setting timeout: %s",
isc_result_totext(result));
/* Continue anyway. */
}
}
/*%
* Check for a deactivation or shutdown request and take appropriate
* action. Returns ISC_TRUE if either is in progress; in this case
......@@ -381,7 +410,7 @@ exit_check(ns_client_t *client) {
if (NS_CLIENTSTATE_READING == client->newstate) {
if (!client->pipelined) {
client_read(client);
client_read(client, ISC_FALSE);
client->newstate = NS_CLIENTSTATE_MAX;
return (ISC_TRUE); /* We're done. */
} else if (client->mortal) {
......@@ -636,7 +665,7 @@ client_start(isc_task_t *task, isc_event_t *event) {
if (TCP_CLIENT(client)) {
if (client->pipelined) {
client_read(client);
client_read(client, ISC_FALSE);
} else {
client_accept(client);
}
......@@ -1548,6 +1577,7 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message,
int count = 0;
unsigned int flags;
unsigned char expire[4];
unsigned char advtimo[2];
REQUIRE(NS_CLIENT_VALID(client));
REQUIRE(opt != NULL && *opt == NULL);
......@@ -1565,7 +1595,8 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message,
/* Set EDNS options if applicable */
if (WANTNSID(client) &&
(ns_g_server->server_id != NULL ||
ns_g_server->server_usehostname)) {
ns_g_server->server_usehostname))
{
if (ns_g_server->server_usehostname) {
result = ns_os_gethostname(nsid, sizeof(nsid));
if (result != ISC_R_SUCCESS) {
......@@ -1670,6 +1701,43 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message,
ednsopts[count].value = ecs;
count++;
}
if (TCP_CLIENT(client) && USEKEEPALIVE(client)) {
isc_buffer_t buf;
INSIST(count < DNS_EDNSOPTIONS);
isc_buffer_init(&buf, advtimo, sizeof(advtimo));
isc_buffer_putuint16(&buf, (isc_uint16_t) ns_g_advertisedtimo);
ednsopts[count].code = DNS_OPT_TCP_KEEPALIVE;
ednsopts[count].length = 2;
ednsopts[count].value = advtimo;
count++;
}
/* Padding must be added last */
if ((view != NULL) && (view->padding > 0) && WANTPAD(client) &&
(TCP_CLIENT(client) ||
((client->attributes & NS_CLIENTATTR_HAVECOOKIE) != 0)))
{
isc_netaddr_t netaddr;
int match;
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
result = dns_acl_match(&netaddr, NULL,
view->pad_acl,
&ns_g_server->aclenv,
&match, NULL);
if (result == ISC_R_SUCCESS && match > 0) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_PAD;
ednsopts[count].length = 0;
ednsopts[count].value = NULL;
count++;
dns_message_setpadding(message, view->padding);
}
}
result = dns_message_buildopt(message, opt, 0, udpsize, flags,
ednsopts, count);
......@@ -2159,6 +2227,21 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) {
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_ecsopt);
break;
case DNS_OPT_TCP_KEEPALIVE:
if (!USEKEEPALIVE(client))
isc_stats_increment(
ns_g_server->nsstats,
dns_nsstatscounter_keepaliveopt);
client->attributes |=
NS_CLIENTATTR_USEKEEPALIVE;
isc_buffer_forward(&optbuf, optlen);
break;
case DNS_OPT_PAD:
client->attributes |= NS_CLIENTATTR_WANTPAD;
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_padopt);
isc_buffer_forward(&optbuf, optlen);
break;
default:
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_otheropt);
......@@ -3064,7 +3147,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
}
static void
client_read(ns_client_t *client) {
client_read(ns_client_t *client, isc_boolean_t newconn) {
isc_result_t result;
CTRACE("read");
......@@ -3078,7 +3161,7 @@ client_read(ns_client_t *client) {
* Set a timeout to limit the amount of time we will wait
* for a request on this TCP connection.
*/
ns_client_settimeout(client, 30);
read_settimeout(client, newconn);
client->state = client->newstate = NS_CLIENTSTATE_READING;
INSIST(client->nreads == 0);
......@@ -3196,7 +3279,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
client->pipelined = ISC_TRUE;
}
client_read(client);
client_read(client, ISC_TRUE);
}
freeevent:
......
......@@ -101,7 +101,11 @@ options {\n\
startup-notify-rate 20;\n\
statistics-file \"named.stats\";\n\
# statistics-interval <obsolete>;\n\
tcp-advertised-timeout 300;\n\
tcp-clients 150;\n\
tcp-idle-timeout 300;\n\
tcp-initial-timeout 300;\n\
tcp-keepalive-timeout 300;\n\
tcp-listen-queue 10;\n\
# tkey-dhkey <none>\n\
# tkey-gssapi-credential <none>\n\
......
......@@ -275,6 +275,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_boolean_t readonly,
} else if (command_compare(command, NS_COMMAND_DNSTAP) ||
command_compare(command, NS_COMMAND_DNSTAPREOPEN)) {
result = ns_server_dnstap(ns_g_server, lex, text);
} else if (command_compare(command, NS_COMMAND_TCPTIMEOUTS)) {
result = ns_server_tcptimeouts(lex, text);
} else {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
......
......@@ -187,8 +187,10 @@ typedef ISC_LIST(ns_client_t) client_list_t;
#define NS_CLIENTATTR_HAVEEXPIRE 0x1000 /*%< return seconds to expire */
#define NS_CLIENTATTR_WANTOPT 0x2000 /*%< add opt to reply */
#define NS_CLIENTATTR_HAVEECS 0x4000 /*%< received an ECS option */
#define NS_CLIENTATTR_WANTPAD 0x8000 /*%< pad reply */
#define NS_CLIENTATTR_USEKEEPALIVE 0x10000 /*%< use TCP keepalive */
#define NS_CLIENTATTR_NOSETFC 0x8000 /*%< don't set servfail cache */
#define NS_CLIENTATTR_NOSETFC 0x20000 /*%< don't set servfail cache */
/*
* Flag to use with the SERVFAIL cache to indicate
......
......@@ -65,6 +65,7 @@
#define NS_COMMAND_MKEYS "managed-keys"
#define NS_COMMAND_DNSTAPREOPEN "dnstap-reopen"
#define NS_COMMAND_DNSTAP "dnstap"
#define NS_COMMAND_TCPTIMEOUTS "tcp-timeouts"
isc_result_t
ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);
......
......@@ -179,6 +179,11 @@ EXTERN isc_boolean_t ns_g_disable6 INIT(ISC_FALSE);
EXTERN isc_boolean_t ns_g_disable4 INIT(ISC_FALSE);
EXTERN unsigned int ns_g_tat_interval INIT(24*3600);
EXTERN unsigned int ns_g_initialtimo INIT(300);
EXTERN unsigned int ns_g_idletimo INIT(300);
EXTERN unsigned int ns_g_keepalivetimo INIT(300);
EXTERN unsigned int ns_g_advertisedtimo INIT(300);
#ifdef HAVE_GEOIP
EXTERN dns_geoip_databases_t *ns_g_geoip INIT(NULL);
#endif
......
......@@ -193,19 +193,21 @@ enum {
dns_nsstatscounter_expireopt = 44,
dns_nsstatscounter_otheropt = 45,
dns_nsstatscounter_ecsopt = 46,
dns_nsstatscounter_padopt = 47,
dns_nsstatscounter_keepaliveopt = 48,
dns_nsstatscounter_nxdomainredirect = 47,
dns_nsstatscounter_nxdomainredirect_rlookup = 48,
dns_nsstatscounter_nxdomainredirect = 49,
dns_nsstatscounter_nxdomainredirect_rlookup = 50,
dns_nsstatscounter_cookiein = 49,
dns_nsstatscounter_cookiebadsize = 50,
dns_nsstatscounter_cookiebadtime = 51,
dns_nsstatscounter_cookienomatch = 52,
dns_nsstatscounter_cookiematch = 53,
dns_nsstatscounter_cookienew = 54,
dns_nsstatscounter_badcookie = 55,
dns_nsstatscounter_cookiein = 51,
dns_nsstatscounter_cookiebadsize = 52,
dns_nsstatscounter_cookiebadtime = 53,
dns_nsstatscounter_cookienomatch = 54,
dns_nsstatscounter_cookiematch = 55,
dns_nsstatscounter_cookienew = 56,
dns_nsstatscounter_badcookie = 57,
dns_nsstatscounter_max = 56
dns_nsstatscounter_max = 58
};
/*%
......@@ -744,4 +746,10 @@ ns_server_mkeys(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
isc_result_t
ns_server_dnstap(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
/*%
* Display or update tcp-{initial,idle,keepalive,advertised}-timeout options.
*/
isc_result_t
ns_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text);
#endif /* NAMED_SERVER_H */
......@@ -108,7 +108,9 @@ server ( <replaceable>ipv4_address<optional>/prefixlen</optional></replaceable>
edns <replaceable>boolean</replaceable>;
edns-udp-size <replaceable>integer</replaceable>;
max-udp-size <replaceable>integer</replaceable>;
padding <replaceable>integer</replaceable>;
tcp-only <replaceable>boolean</replaceable>;
tcp-keepalive <replaceable>boolean</replaceable>;
provide-ixfr <replaceable>boolean</replaceable>;
request-ixfr <replaceable>boolean</replaceable>;
keys <replaceable>server_key</replaceable>;
......@@ -404,6 +406,10 @@ options {
send-cookie <replaceable>boolean</replaceable>;
nocookie-udp-size <replaceable>integer</replaceable>;
response-padding {
<replaceable>address_match_list</replaceable>
} block-size <replaceable>integer</replaceable>;
deny-answer-addresses {
<replaceable>address_match_list</replaceable>
} <optional> except-from { <replaceable>namelist</replaceable> } </optional>;
......
......@@ -1282,10 +1282,10 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
(void)cfg_map_get(cpeer, "edns-udp-size", &obj);
if (obj != NULL) {
isc_uint32_t udpsize = cfg_obj_asuint32(obj);
if (udpsize < 512)
udpsize = 512;
if (udpsize > 4096)
udpsize = 4096;
if (udpsize < 512U)
udpsize = 512U;
if (udpsize > 4096U)
udpsize = 4096U;
CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
}
......@@ -1293,8 +1293,8 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
(void)cfg_map_get(cpeer, "edns-version", &obj);
if (obj != NULL) {
isc_uint32_t ednsversion = cfg_obj_asuint32(obj);
if (ednsversion > 255)
ednsversion = 255;
if (ednsversion > 255U)
ednsversion = 255U;
CHECK(dns_peer_setednsversion(peer, (isc_uint8_t)ednsversion));
}
......@@ -1302,18 +1302,36 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
(void)cfg_map_get(cpeer, "max-udp-size", &obj);
if (obj != NULL) {
isc_uint32_t udpsize = cfg_obj_asuint32(obj);
if (udpsize < 512)
udpsize = 512;
if (udpsize > 4096)
udpsize = 4096;
if (udpsize < 512U)