Add 'keepalive' test and supporting changes to getdns library.

Checking for server support for keepalive means we need to know if the server did send a keepalive option to the client. This information is not currently exposed in getdns, so add a flag 'server_keepalive_received' to call_reporting. This is 0 if not received, 1 if received. If received, the actual timeout is in 'idle timeout in ms', though watch out for the overflow alternative.
This commit is contained in:
Jim Hague 2018-01-16 15:08:22 +00:00
parent a4ff6de985
commit 3666d994a7
5 changed files with 116 additions and 8 deletions

View File

@ -1011,6 +1011,7 @@ upstream_init(getdns_upstream *upstream,
upstream->responses_timeouts = 0; upstream->responses_timeouts = 0;
upstream->keepalive_shutdown = 0; upstream->keepalive_shutdown = 0;
upstream->keepalive_timeout = 0; upstream->keepalive_timeout = 0;
upstream->server_keepalive_received = 0;
/* How is this upstream doing on UDP? */ /* How is this upstream doing on UDP? */
upstream->to_retry = 1; upstream->to_retry = 1;
upstream->back_off = 1; upstream->back_off = 1;

View File

@ -193,6 +193,7 @@ typedef struct getdns_upstream {
size_t responses_timeouts; size_t responses_timeouts;
size_t keepalive_shutdown; size_t keepalive_shutdown;
uint64_t keepalive_timeout; uint64_t keepalive_timeout;
int server_keepalive_received;
/* Management of outstanding requests on stateful transports */ /* Management of outstanding requests on stateful transports */
getdns_network_req *write_queue; getdns_network_req *write_queue;

View File

@ -341,6 +341,7 @@ process_keepalive(
} }
return; return;
} }
upstream->server_keepalive_received = 1;
/* Use server sent value unless the client specified a shorter one. /* Use server sent value unless the client specified a shorter one.
Convert to ms first (wire value has units of 100ms) */ Convert to ms first (wire value has units of 100ms) */
uint64_t server_keepalive = ((uint64_t)gldns_read_uint16(position))*100; uint64_t server_keepalive = ((uint64_t)gldns_read_uint16(position))*100;

View File

@ -183,9 +183,12 @@ static void usage()
"\n" "\n"
"Tests:\n" "Tests:\n"
" lookup [<name> [<type>]] Check lookup on server\n" " lookup [<name> [<type>]] Check lookup on server\n"
" keepalive <timeout-ms> [<name> [<type>]]\n"
" Check server support for EDNS0 keepalive in TCP/TLS\n"
" Timeout of 0 is off.\n"
" qname-min Check whether server supports QNAME minimisation\n"
" rtt [warn-ms,crit-ms] [<name> [<type>]]\n" " rtt [warn-ms,crit-ms] [<name> [<type>]]\n"
" Check server round trip time (default 500,250)\n" " Check server round trip time (default 500,250)\n"
" qname-min Check whether server supports QNAME minimisation\n"
"\n" "\n"
" tls-auth [<name> [<type>]] Check authentication of TLS server\n" " tls-auth [<name> [<type>]] Check authentication of TLS server\n"
" If both a SPKI pin and authentication name are\n" " If both a SPKI pin and authentication name are\n"
@ -938,20 +941,109 @@ no_padding:
return EXIT_CRITICAL; return EXIT_CRITICAL;
} }
static exit_value_t test_keepalive(const struct test_info_s *test_info,
char ** av)
{
getdns_dict *response;
exit_value_t xit;
long long timeout;
char *endptr;
const char USAGE[] = "keepalive takes arguments <timeout-ms> [<name> [<type>]]";
if (!*av || (timeout = strtoll(*av, &endptr, 10), *endptr != '\0' || timeout < 0)) {
fputs(USAGE, test_info->errout);
return EXIT_USAGE;
}
++av;
getdns_return_t ret;
if ((ret = getdns_context_set_idle_timeout(test_info->context, (uint64_t) timeout)) != GETDNS_RETURN_GOOD) {
fprintf(test_info->errout,
"Cannot set keepalive timeout: %s (%d)",
getdns_get_errorstr_by_id(ret),
ret);
return EXIT_UNKNOWN;
}
if ((xit = parse_search_check(test_info,
av,
USAGE,
&response,
NULL,
NULL,
NULL)) != EXIT_OK)
return xit;
getdns_list *l;
if ((ret = getdns_dict_get_list(response, "call_reporting", &l)) != GETDNS_RETURN_GOOD) {
fprintf(test_info->errout,
"Cannot get call report: %s (%d)",
getdns_get_errorstr_by_id(ret),
ret);
return EXIT_UNKNOWN;
}
getdns_dict *d;
if ((ret = getdns_list_get_dict(l, 0, &d)) != GETDNS_RETURN_GOOD) {
fprintf(test_info->errout,
"Cannot get call report first item: %s (%d)",
getdns_get_errorstr_by_id(ret),
ret);
return EXIT_UNKNOWN;
}
/* Search is forced to be TCP or TLS, so server keepalive flag must exist. */
uint32_t server_keepalive_received;
if ((ret = getdns_dict_get_int(d, "server_keepalive_received", &server_keepalive_received)) != GETDNS_RETURN_GOOD) {
fprintf(test_info->errout,
"Cannot get server keepalive flag: %s (%d)",
getdns_get_errorstr_by_id(ret),
ret);
return EXIT_UNKNOWN;
}
if (server_keepalive_received) {
uint32_t t;
bool overflow = false;
if (!((ret = getdns_dict_get_int(d, "idle timeout in ms", &t)) == GETDNS_RETURN_GOOD ||
(overflow = true, ret = getdns_dict_get_int(d, "idle timeout in ms (overflow)", &t)) == GETDNS_RETURN_GOOD)) {
fprintf(test_info->errout,
"Cannot get idle timeout: %s (%d)",
getdns_get_errorstr_by_id(ret),
ret);
return EXIT_UNKNOWN;
}
if (overflow) {
fputs("Server sent keepalive, idle timeout now (overflow)", test_info->errout);
} else {
fprintf(test_info->errout, "Server sent keepalive, idle timeout now %ums", t);
}
return EXIT_OK;
} else {
fputs("Server did not send keepalive", test_info->errout);
return EXIT_CRITICAL;
}
}
static struct test_funcs_s static struct test_funcs_s
{ {
const char *name; const char *name;
bool implies_tls; bool implies_tls;
bool implies_tcp;
exit_value_t (*func)(const struct test_info_s *test_info, char **av); exit_value_t (*func)(const struct test_info_s *test_info, char **av);
} TESTS[] = } TESTS[] =
{ {
{ "lookup", false, test_lookup }, { "lookup", false, false, test_lookup },
{ "rtt", false, test_rtt }, { "rtt", false, false, test_rtt },
{ "qname-min", false, test_qname_minimisation }, { "qname-min", false, false, test_qname_minimisation },
{ "tls-auth", true, test_authenticate }, { "tls-auth", true, false, test_authenticate },
{ "tls-cert-valid", true, test_certificate_valid }, { "tls-cert-valid", true, false, test_certificate_valid },
{ "tls-padding", true, test_padding }, { "tls-padding", true, false, test_padding },
{ NULL, false, NULL } { "keepalive", false, true, test_keepalive },
{ NULL, false, false, NULL }
}; };
int main(int ac, char *av[]) int main(int ac, char *av[])
@ -1151,6 +1243,15 @@ int main(int ac, char *av[])
exit(EXIT_UNKNOWN); exit(EXIT_UNKNOWN);
} }
if (f->implies_tcp) {
if (use_udp) {
fputs("Test requires TCP or TLS\n", test_info.errout);
exit(EXIT_UNKNOWN);
}
if (!use_tls)
use_tcp = true;
}
if (f->implies_tls) { if (f->implies_tls) {
if (use_udp | use_tcp) { if (use_udp | use_tcp) {
fputs("Test requires TLS, or TLS authentication specified\n", test_info.errout); fputs("Test requires TLS, or TLS authentication specified\n", test_info.errout);

View File

@ -880,6 +880,10 @@ _getdns_create_call_reporting_dict(
return NULL; return NULL;
} }
} }
if (getdns_dict_set_int( netreq_debug, "server_keepalive_received", netreq->upstream->server_keepalive_received)) {
getdns_dict_destroy(netreq_debug);
return NULL;
}
/* The running totals are only updated when a connection is closed. /* The running totals are only updated when a connection is closed.
Since it is open as we have just used it, calcualte the value on the fly */ Since it is open as we have just used it, calcualte the value on the fly */
if (getdns_dict_set_int( netreq_debug, "responses_on_this_connection", if (getdns_dict_set_int( netreq_debug, "responses_on_this_connection",