1 /*
2 * Copyright (c) 2018, NLNet Labs, Sinodun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the names of the copyright holders nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "config.h"
29 #include <ctype.h>
30 #include <stdarg.h>
31 #include <stdbool.h>
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38
39 #ifdef USE_GNUTLS
40 #include <gnutls/x509.h>
41 #else
42 #include <openssl/x509.h>
43 #include <openssl/x509v3.h>
44 #include <openssl/bio.h>
45 #endif
46
47 #include <getdns/getdns.h>
48 #include <getdns/getdns_extra.h>
49
50 #define APP_NAME "getdns_server_mon"
51
52 #define RTT_CRITICAL_MS 500
53 #define RTT_WARNING_MS 250
54
55 #define CERT_EXPIRY_CRITICAL_DAYS 7
56 #define CERT_EXPIRY_WARNING_DAYS 14
57
58 #define DEFAULT_LOOKUP_NAME "getdnsapi.net"
59 #define DEFAULT_LOOKUP_TYPE GETDNS_RRTYPE_AAAA
60
61 #define EDNS0_PADDING_CODE 12
62
63 static const char TLS13_CIPHER_SUITE[] =
64 "TLS13-AES-256-GCM-SHA384:"
65 "TLS13-CHACHA20-POLY1305-SHA256:"
66 "TLS13-AES-128-GCM-SHA256:"
67 "TLS13-AES-128-CCM-8-SHA256:"
68 "TLS13-AES-128-CCM-SHA256";
69
70 #define EXAMPLE_PIN "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\""
71
72 /* Plugin exit values */
73 typedef enum {
74 EXIT_OK = 0,
75 EXIT_WARNING,
76 EXIT_CRITICAL,
77 EXIT_UNKNOWN,
78 EXIT_USAGE /* Special case - internal only. */
79 } exit_value;
80
81 /* Plugin verbosity values */
82 typedef enum {
83 VERBOSITY_MINIMAL = 0,
84 VERBOSITY_ADDITIONAL,
85 VERBOSITY_CONFIG,
86 VERBOSITY_DEBUG
87 } plugin_verbosity;
88
89 #define MAX_BASE_OUTPUT_LEN 256
90 #define MAX_PERF_OUTPUT_LEN 256
91
92 static struct test_info_s
93 {
94 getdns_context *context;
95
96 /* Output */
97 bool monitoring;
98 FILE *errout;
99 plugin_verbosity verbosity;
100 bool debug_output;
101 char base_output[MAX_BASE_OUTPUT_LEN + 1];
102 char perf_output[MAX_PERF_OUTPUT_LEN + 1];
103
104 /* Test config info */
105 bool fail_on_dns_errors;
106 } test_info;
107
snprintcat(char * buf,size_t buflen,const char * format,...)108 static void snprintcat(char *buf, size_t buflen, const char *format, ...)
109 {
110 va_list ap;
111 int l = strlen(buf);
112
113 buf += l;
114 buflen -= l;
115
116 va_start(ap, format);
117 vsnprintf(buf, buflen, format, ap);
118 va_end(ap);
119 buf[buflen] = '\0';
120 }
121
get_rrtype(const char * t)122 static int get_rrtype(const char *t)
123 {
124 char buf[128] = "GETDNS_RRTYPE_";
125 uint32_t rrtype;
126 long int l;
127 size_t i;
128 char *endptr;
129
130 if (strlen(t) > sizeof(buf) - 15)
131 return -1;
132 for (i = 14; *t && i < sizeof(buf) - 1; i++, t++)
133 buf[i] = *t == '-' ? '_' : toupper((unsigned char)*t);
134 buf[i] = '\0';
135
136 if (!getdns_str2int(buf, &rrtype))
137 return (int)rrtype;
138
139 if (strncasecmp(buf + 14, "TYPE", 4) == 0) {
140 l = strtol(buf + 18, &endptr, 10);
141 if (!*endptr && l >= 0 && l < 65536)
142 return l;
143 }
144 return -1;
145 }
146
getdns_intval_text(int val,const char * name,const char * prefix)147 static const char *getdns_intval_text(int val, const char *name, const char *prefix)
148 {
149 getdns_dict *d = getdns_dict_create();
150 char buf[128];
151 static char res[20];
152
153 if (getdns_dict_set_int(d, name, val) != GETDNS_RETURN_GOOD)
154 goto err;
155
156 getdns_pretty_snprint_dict(buf, sizeof(buf), d);
157
158 const char *p = strstr(buf, prefix);
159
160 if (!p)
161 goto err;
162 p += strlen(prefix);
163
164 char *q = res;
165
166 while (*p && *p != '\n')
167 *q++ = *p++;
168
169 getdns_dict_destroy(d);
170 return res;
171
172 err:
173 getdns_dict_destroy(d);
174 snprintf(res, sizeof(res), "%d", val);
175 return res;
176 }
177
rrtype_text(int rrtype)178 static const char *rrtype_text(int rrtype)
179 {
180 return getdns_intval_text(rrtype, "type", "GETDNS_RRTYPE_");
181 }
182
rcode_text(int rcode)183 static const char *rcode_text(int rcode)
184 {
185 return getdns_intval_text(rcode, "rcode", "GETDNS_RCODE_");
186 }
187
188 #if !defined(USE_GNUTLS) && (OPENSSL_VERSION_NUMBER < 0x10002000 || defined(LIBRESSL_VERSION_NUMBER))
189 /*
190 * Convert date to Julian day.
191 * See https://en.wikipedia.org/wiki/Julian_day
192 */
julian_day(const struct tm * tm)193 static long julian_day(const struct tm *tm)
194 {
195 long dd, mm, yyyy;
196
197 dd = tm->tm_mday;
198 mm = tm->tm_mon + 1;
199 yyyy = tm->tm_year + 1900;
200
201 return (1461 * (yyyy + 4800 + (mm - 14) / 12)) / 4 +
202 (367 * (mm - 2 - 12 * ((mm - 14) / 12))) / 12 -
203 (3 * ((yyyy + 4900 + (mm - 14) / 12) / 100)) / 4 +
204 dd - 32075;
205 }
206
secs_in_day(const struct tm * tm)207 static long secs_in_day(const struct tm *tm)
208 {
209 return ((tm->tm_hour * 60) + tm->tm_min) * 60 + tm->tm_sec;
210 }
211 #endif
212
213 /*
214 * Thanks to:
215 * https://zakird.com/2013/10/13/certificate-parsing-with-openssl
216 */
extract_cert_expiry(const unsigned char * data,size_t len,time_t * t)217 static bool extract_cert_expiry(const unsigned char *data, size_t len, time_t *t)
218 {
219 #ifdef USE_GNUTLS
220 gnutls_x509_crt_t cert;
221 gnutls_datum_t datum;
222 bool res = false;
223
224 datum.data = (unsigned char*) data;
225 datum.size = len;
226
227 if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
228 return false;
229
230 if (gnutls_x509_crt_import(cert, &datum, GNUTLS_X509_FMT_DER) == GNUTLS_E_SUCCESS) {
231 time_t expiry = gnutls_x509_crt_get_expiration_time(cert);
232 if (expiry != GNUTLS_X509_NO_WELL_DEFINED_EXPIRATION) {
233 res = true;
234 *t = expiry;
235 }
236 }
237 gnutls_x509_crt_deinit(cert);
238 return res;
239 #else
240 X509 *cert = d2i_X509(NULL, &data, len);
241 if (!cert)
242 return false;
243
244 int day_diff, sec_diff;
245 const long SECS_IN_DAY = 60 * 60 * 24;
246 #if defined(X509_get_notAfter) || defined(HAVE_X509_GET_NOTAFTER)
247 const ASN1_TIME *not_after = X509_get_notAfter(cert);
248 #elif defined(X509_get0_notAfter) || defined(HAVE_X509_GET0_NOTAFTER)
249 const ASN1_TIME *not_after = X509_get0_notAfter(cert);
250 #endif
251 *t = time(NULL);
252
253 #if OPENSSL_VERSION_NUMBER < 0x10002000 || defined(LIBRESSL_VERSION_NUMBER)
254 /*
255 * OpenSSL before 1.0.2 does not support ASN1_TIME_diff().
256 * So work around by using ASN1_TIME_print() to print to a buffer
257 * and parsing that. This does not do any kind of sane format,
258 * but 'Mar 15 11:58:50 2018 GMT'. Note the month name is not
259 * locale-dependent but always English, so strptime() to parse
260 * isn't going to work. It also *appears* to always end 'GMT'.
261 * Ideally one could then convert this UTC time to a time_t, but
262 * there's no way to do that in standard C/POSIX. So follow the
263 * lead of OpenSSL, convert to Julian days and use the difference.
264 */
265
266 char buf[40];
267 BIO *b = BIO_new(BIO_s_mem());
268 if (ASN1_TIME_print(b, not_after) <= 0) {
269 BIO_free(b);
270 X509_free(cert);
271 return false;
272 }
273 if (BIO_gets(b, buf, sizeof(buf)) <= 0) {
274 BIO_free(b);
275 X509_free(cert);
276 return false;
277 }
278 BIO_free(b);
279 X509_free(cert);
280
281 struct tm tm;
282 char month[4];
283 char tz[4];
284 memset(&tm, 0, sizeof(tm));
285 if (sscanf(buf,
286 "%3s %d %d:%d:%d %d %3s",
287 month,
288 &tm.tm_mday,
289 &tm.tm_hour,
290 &tm.tm_min,
291 &tm.tm_sec,
292 &tm.tm_year,
293 tz) != 7)
294 return false;
295 tm.tm_year -= 1900;
296 if (strcmp(tz, "GMT") != 0)
297 return false;
298
299 const char *mon[] = {
300 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
301 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
302 };
303
304 while(tm.tm_mon < 12 && strcmp(mon[tm.tm_mon], month) != 0)
305 ++tm.tm_mon;
306 if (tm.tm_mon > 11)
307 return false;
308
309 struct tm tm_now;
310 gmtime_r(t, &tm_now);
311
312 day_diff = julian_day(&tm) - julian_day(&tm_now);
313 sec_diff = secs_in_day(&tm) - secs_in_day(&tm_now);
314 if (sec_diff < 0) {
315 sec_diff += SECS_IN_DAY;
316 --day_diff;
317 }
318 #else
319 /*
320 * Use ASN1_TIME_diff to get a time delta between now and expiry.
321 * This is much easier than trying to parse the time.
322 */
323 ASN1_TIME_diff(&day_diff, &sec_diff, NULL, not_after);
324 X509_free(cert);
325 #endif
326 *t += day_diff * SECS_IN_DAY + sec_diff;
327 #endif /* USE_GNUTLS */
328 return true;
329 }
330
exit_tidy()331 static void exit_tidy()
332 {
333 if (test_info.context)
334 getdns_context_destroy(test_info.context);
335 }
336
usage()337 static void usage()
338 {
339 fputs(
340 "Usage: " APP_NAME " [-M] [-E] [(-u|-t|-T)] [-S] [-K <spki-pin>]\n"
341 " [-v [-v [-v]]] [-V] @upstream testname [<test args>]\n"
342 " -M|--monitoring Make output suitable for monitoring tools\n"
343 " -E|--fail-on-dns-errors Fail on DNS error (NXDOMAIN, SERVFAIL)\n"
344 " -u|--udp Use UDP transport\n"
345 " -t|--tcp Use TCP transport\n"
346 " -T|--tls Use TLS transport\n"
347 " -S|--strict-usage-profile Use strict profile (require authentication)\n"
348 " -K|--spki-pin <spki-pin> SPKI pin for TLS connections (can repeat)\n"
349 " -v|--verbose Increase output verbosity\n"
350 " -D|--debug Enable debugging output\n"
351 " -V|--version Report GetDNS version\n"
352 "\n"
353 "spki-pin: Should look like '" EXAMPLE_PIN "'\n"
354 "\n"
355 "upstream: @<ip>[%<scope_id][@<port>][#<tls_port>][~tls name>][^<tsig spec>]\n"
356 " <ip>@<port> may be given as <IPv4>:<port> or\n"
357 " '['<IPv6>[%<scope_id>]']':<port>\n"
358 "\n"
359 "tsig spec: [<algorithm>:]<name>:<secret in Base64>\n"
360 "\n"
361 "Tests:\n"
362 " lookup [<name> [<type>]] Check lookup on server\n"
363 " keepalive [<name> [<type>]] Check server support for EDNS0 keepalive in\n"
364 " TCP or TLS connections\n"
365 " Timeout of 0 is off.\n"
366 " OOOR Check whether server delivers responses out of\n"
367 " query order on a TCP or TLS connection\n"
368 " qname-min Check whether server supports QNAME minimisation\n"
369 " rtt [warn-ms,crit-ms] [<name> [<type>]]\n"
370 " Check if server round trip time exceeds\n"
371 " thresholds (default 250,500)\n"
372 "\n"
373 " dnssec-validate Check whether server does DNSSEC validation\n"
374 "\n"
375 " tls-auth [<name> [<type>]] Check authentication of TLS server\n"
376 " If both a SPKI pin and authentication name are\n"
377 " provided, both must authenticate for this test\n"
378 " to pass.\n"
379 " tls-cert-valid [warn-days,crit-days] [<name> [type]]\n"
380 " Check server certificate validity, report\n"
381 " warning or critical if days to expiry at\n"
382 " or below thresholds (default 14,7).\n"
383 " tls-padding <blocksize> [<name> [<type>]]\n"
384 " Check server support for EDNS0 padding in TLS\n"
385 " Special blocksize values are 0 = off,\n"
386 " 1 = sensible default.\n"
387 " tls-1.3 Check whether server supports TLS 1.3\n"
388 "\n"
389 "Enabling monitoring mode ensures output messages and exit statuses conform\n"
390 "to the requirements of monitoring plugins (www.monitoring-plugins.org).\n",
391 test_info.errout);
392 exit(EXIT_UNKNOWN);
393 }
394
version()395 static void version()
396 {
397 fprintf(test_info.errout,
398 APP_NAME ": getdns version %s, API version '%s'.\n",
399 getdns_get_version(),
400 getdns_get_api_version());
401 exit(EXIT_UNKNOWN);
402 }
403
404 /**
405 ** Functions used by tests.
406 **/
407
get_thresholds(char *** av,int * critical,int * warning)408 static void get_thresholds(char ***av,
409 int *critical,
410 int *warning)
411 {
412 if (**av) {
413 char *comma = strchr(**av, ',');
414 if (!comma)
415 return;
416
417 char *end;
418 long w,c;
419
420 w = strtol(**av, &end, 10);
421 /*
422 * If the number doesn't end at a comma, this isn't a
423 * properly formatted thresholds arg. Pass over it.
424 */
425 if (end != comma)
426 return;
427
428 /*
429 * Similarly, if the number doesn't end at the end of the
430 * argument, this isn't a properly formatted arg.
431 */
432 c = strtol(comma + 1, &end, 10);
433 if (*end != '\0')
434 return;
435
436 /* Got two numbers, so consume the argument. */
437 *critical = (int) c;
438 *warning = (int) w;
439 ++*av;
440 return;
441 }
442
443 return;
444 }
445
get_name_type_args(struct test_info_s * test_info,char *** av,const char ** lookup_name,uint32_t * lookup_type)446 static exit_value get_name_type_args(struct test_info_s *test_info,
447 char ***av,
448 const char **lookup_name,
449 uint32_t *lookup_type)
450 {
451 if (**av) {
452 if (strlen(**av) > 0) {
453 *lookup_name = **av;
454 } else {
455 strcpy(test_info->base_output, "Empty name not valid");
456 return EXIT_UNKNOWN;
457 }
458 ++*av;
459
460 if (**av) {
461 int rrtype = get_rrtype(**av);
462 if (rrtype >= 0) {
463 *lookup_type = (uint32_t) rrtype;
464 ++*av;
465 }
466 }
467 }
468
469 return EXIT_OK;
470 }
471
search(struct test_info_s * test_info,const char * name,uint16_t type,getdns_dict * extensions,getdns_dict ** response,getdns_return_t * getdns_return)472 static exit_value search(struct test_info_s *test_info,
473 const char *name,
474 uint16_t type,
475 getdns_dict *extensions,
476 getdns_dict **response,
477 getdns_return_t *getdns_return)
478 {
479 getdns_return_t ret;
480 getdns_dict *search_extensions =
481 (extensions) ? extensions : getdns_dict_create();
482
483 /* We always turn on the return_call_reporting extension. */
484 if ((ret = getdns_dict_set_int(search_extensions, "return_call_reporting", GETDNS_EXTENSION_TRUE)) != GETDNS_RETURN_GOOD) {
485 if (getdns_return)
486 *getdns_return = ret;
487 snprintf(test_info->base_output,
488 MAX_BASE_OUTPUT_LEN,
489 "Cannot set return call reporting: %s (%d)",
490 getdns_get_errorstr_by_id(ret),
491 ret);
492 if (!extensions)
493 getdns_dict_destroy(search_extensions);
494 return EXIT_UNKNOWN;
495 }
496
497 if (test_info->verbosity >= VERBOSITY_ADDITIONAL &&
498 !test_info->monitoring) {
499 printf("DNS Lookup name:\t%s\n", name);
500 printf("DNS Lookup RR type:\t%s\n", rrtype_text(type));
501 }
502
503 if (test_info->debug_output) {
504 printf("Context: %s\n",
505 getdns_pretty_print_dict(getdns_context_get_api_information(test_info->context)));
506 }
507
508 ret = getdns_general_sync(test_info->context,
509 name,
510 type,
511 search_extensions,
512 response);
513 if (!extensions)
514 getdns_dict_destroy(search_extensions);
515 if (ret != GETDNS_RETURN_GOOD) {
516 if (getdns_return) {
517 *getdns_return = ret;
518 } else {
519 snprintf(test_info->base_output,
520 MAX_BASE_OUTPUT_LEN,
521 "Error resolving '%s': %s (%d)",
522 name,
523 getdns_get_errorstr_by_id(ret),
524 ret);
525 }
526 return EXIT_CRITICAL;
527 }
528
529 if (test_info->debug_output) {
530 printf("Response: %s\n",
531 getdns_pretty_print_dict(*response));
532 }
533
534 if (getdns_return)
535 *getdns_return = ret;
536 return EXIT_OK;
537 }
538
get_result(struct test_info_s * test_info,const getdns_dict * response,uint32_t * error_id,uint32_t * rcode)539 static exit_value get_result(struct test_info_s *test_info,
540 const getdns_dict *response,
541 uint32_t *error_id,
542 uint32_t *rcode)
543 {
544 getdns_return_t ret;
545
546 if ((ret = getdns_dict_get_int(response, "status", error_id)) != GETDNS_RETURN_GOOD) {
547 snprintf(test_info->base_output,
548 MAX_BASE_OUTPUT_LEN,
549 "Cannot get result status: %s (%d)",
550 getdns_get_errorstr_by_id(ret),
551 ret);
552 return EXIT_UNKNOWN;
553 }
554
555 if (*error_id != GETDNS_RESPSTATUS_GOOD && *error_id != GETDNS_RESPSTATUS_NO_NAME) {
556 *rcode = 0;
557 return EXIT_OK;
558 }
559
560 if ((ret = getdns_dict_get_int(response, "/replies_tree/0/header/rcode", rcode)) != GETDNS_RETURN_GOOD) {
561 snprintf(test_info->base_output,
562 MAX_BASE_OUTPUT_LEN,
563 "Cannot get DNS return code: %s (%d)",
564 getdns_get_errorstr_by_id(ret),
565 ret);
566 return EXIT_UNKNOWN;
567 }
568
569 return EXIT_OK;
570 }
571
check_result(struct test_info_s * test_info,const getdns_dict * response)572 static exit_value check_result(struct test_info_s *test_info,
573 const getdns_dict *response)
574 {
575 exit_value xit;
576 uint32_t error_id, rcode;
577
578 if ((xit = get_result(test_info, response, &error_id, &rcode)) != EXIT_OK)
579 return xit;
580
581 switch(error_id) {
582 case GETDNS_RESPSTATUS_ALL_TIMEOUT:
583 strcpy(test_info->base_output, "Search timed out");
584 return EXIT_CRITICAL;
585
586 case GETDNS_RESPSTATUS_NO_SECURE_ANSWERS:
587 strcpy(test_info->base_output, "No secure answers");
588 return EXIT_CRITICAL;
589
590 case GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS:
591 strcpy(test_info->base_output, "All answers are bogus");
592 return EXIT_CRITICAL;
593
594 default:
595 break;
596 }
597
598 if (test_info->verbosity >= VERBOSITY_ADDITIONAL){
599 if (test_info->monitoring) {
600 snprintcat(test_info->perf_output,
601 MAX_PERF_OUTPUT_LEN,
602 "getdns=%d;",
603 error_id);
604 } else {
605 printf("getdns result:\t\t%s (%d)\n",
606 getdns_get_errorstr_by_id(error_id),
607 error_id);
608 }
609 }
610
611 if (test_info->fail_on_dns_errors && rcode > 0) {
612 snprintf(test_info->base_output,
613 MAX_BASE_OUTPUT_LEN,
614 "DNS error %s (%d)",
615 rcode_text(rcode),
616 rcode);
617 return EXIT_CRITICAL;
618 }
619
620 return EXIT_OK;
621 }
622
get_report_info(struct test_info_s * test_info,const getdns_dict * response,uint32_t * rtt,getdns_bindata ** auth_status,time_t * cert_expire_time)623 static exit_value get_report_info(struct test_info_s *test_info,
624 const getdns_dict *response,
625 uint32_t *rtt,
626 getdns_bindata **auth_status,
627 time_t *cert_expire_time)
628 {
629 getdns_return_t ret;
630 getdns_list *l;
631 uint32_t rtt_val;
632 getdns_bindata *auth_status_val = NULL;
633 time_t cert_expire_time_val = 0;
634
635 if ((ret = getdns_dict_get_list(response, "call_reporting", &l)) != GETDNS_RETURN_GOOD) {
636 snprintf(test_info->base_output,
637 MAX_BASE_OUTPUT_LEN,
638 "Cannot get call report: %s (%d)",
639 getdns_get_errorstr_by_id(ret),
640 ret);
641 return EXIT_UNKNOWN;
642 }
643
644 getdns_dict *d;
645
646 if ((ret = getdns_list_get_dict(l, 0, &d)) != GETDNS_RETURN_GOOD) {
647 snprintf(test_info->base_output,
648 MAX_BASE_OUTPUT_LEN,
649 "Cannot get call report first item: %s (%d)",
650 getdns_get_errorstr_by_id(ret),
651 ret);
652 return EXIT_UNKNOWN;
653 }
654 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
655 uint32_t transport;
656 const char *transport_text = "???";
657
658 if ((ret = getdns_dict_get_int(d, "transport", &transport)) != GETDNS_RETURN_GOOD) {
659 snprintf(test_info->base_output,
660 MAX_BASE_OUTPUT_LEN,
661 "Cannot get transport: %s (%d)",
662 getdns_get_errorstr_by_id(ret),
663 ret);
664 return EXIT_UNKNOWN;
665 }
666
667 switch(transport) {
668 case GETDNS_TRANSPORT_UDP:
669 transport_text = "UDP";
670 break;
671
672 case GETDNS_TRANSPORT_TCP:
673 transport_text = "TCP";
674 break;
675
676 case GETDNS_TRANSPORT_TLS:
677 transport_text = "TLS";
678 break;
679 }
680
681 if (test_info->monitoring) {
682 snprintcat(test_info->perf_output,
683 MAX_PERF_OUTPUT_LEN,
684 "transport=%s;",
685 transport_text);
686 } else {
687 printf("Transport:\t\t%s\n", transport_text);
688 }
689 }
690
691 if ((ret = getdns_dict_get_int(d, "run_time/ms", &rtt_val)) != GETDNS_RETURN_GOOD) {
692 snprintf(test_info->base_output,
693 MAX_BASE_OUTPUT_LEN,
694 "Cannot get RTT: %s (%d)",
695 getdns_get_errorstr_by_id(ret),
696 ret);
697 return EXIT_UNKNOWN;
698 }
699 if (rtt)
700 *rtt = rtt_val;
701 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
702 if (test_info->monitoring) {
703 snprintcat(test_info->perf_output,
704 MAX_PERF_OUTPUT_LEN,
705 "rtt=%dms;",
706 rtt_val);
707 } else {
708 printf("RTT:\t\t\t%dms\n", rtt_val);
709 }
710 }
711
712 if (getdns_dict_get_bindata(d, "tls_auth_status", &auth_status_val) == GETDNS_RETURN_GOOD) {
713 const char *auth_status_text = (char *) auth_status_val->data;
714
715 /* Just in case - not sure this is necessary */
716 auth_status_val->data[auth_status_val->size] = '\0';
717 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
718 if (test_info->monitoring) {
719 snprintcat(test_info->perf_output,
720 MAX_PERF_OUTPUT_LEN,
721 "auth=%s;",
722 auth_status_text);
723 } else {
724 printf("Authentication:\t\t%s\n", auth_status_text);
725 }
726 }
727 }
728 if (auth_status)
729 *auth_status = auth_status_val;
730
731 getdns_bindata *cert;
732
733 if (getdns_dict_get_bindata(d, "tls_peer_cert", &cert) == GETDNS_RETURN_GOOD) {
734 if (!extract_cert_expiry(cert->data, cert->size, &cert_expire_time_val)) {
735 strcpy(test_info->base_output, "Cannot parse PKIX certificate");
736 return EXIT_UNKNOWN;
737 }
738 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
739 struct tm *tm = gmtime(&cert_expire_time_val);
740 char buf[25];
741 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
742 if (test_info->monitoring) {
743 snprintcat(test_info->perf_output,
744 MAX_PERF_OUTPUT_LEN,
745 "expire=%s;",
746 buf);
747 } else {
748 printf("Certificate expires:\t%s UTC\n", buf);
749 }
750 }
751 }
752 if (cert_expire_time)
753 *cert_expire_time = cert_expire_time_val;
754
755 getdns_bindata *tls_version;
756
757 if (getdns_dict_get_bindata(d, "tls_version", &tls_version) == GETDNS_RETURN_GOOD) {
758 const char *version_text = (char *) tls_version->data;
759
760 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
761 if (test_info->monitoring) {
762 snprintcat(test_info->perf_output,
763 MAX_PERF_OUTPUT_LEN,
764 "tls=%s;",
765 version_text);
766 } else {
767 printf("TLS version:\t\t%s\n", version_text);
768 }
769 }
770
771 /*
772 * This is always in the context, so show only if we got
773 * TLS info in the call reporting.
774 */
775 uint32_t tls_auth;
776 getdns_dict *context_dict = getdns_context_get_api_information(test_info->context);
777
778 if (getdns_dict_get_int(context_dict, "/all_context/tls_authentication", &tls_auth) == GETDNS_RETURN_GOOD) {
779 const char *auth_text = "???";
780
781 switch(tls_auth) {
782 case GETDNS_AUTHENTICATION_NONE:
783 auth_text = "Opportunistic";
784 break;
785
786 case GETDNS_AUTHENTICATION_REQUIRED:
787 auth_text = "Strict";
788 break;
789 }
790
791 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
792 if (test_info->monitoring) {
793 snprintcat(test_info->perf_output,
794 MAX_PERF_OUTPUT_LEN,
795 "%s;",
796 auth_text);
797 } else {
798 printf("TLS authentication:\t%s\n", auth_text);
799 }
800 }
801 }
802 }
803
804 return EXIT_OK;
805 }
806
get_answers(struct test_info_s * test_info,const getdns_dict * response,const char * section,getdns_list ** answers,size_t * no_answers)807 static exit_value get_answers(struct test_info_s *test_info,
808 const getdns_dict *response,
809 const char *section,
810 getdns_list **answers,
811 size_t *no_answers)
812 {
813 getdns_return_t ret;
814 char buf[40];
815
816 snprintf(buf, sizeof(buf), "/replies_tree/0/%s", section);
817
818 if ((ret = getdns_dict_get_list(response, buf, answers)) != GETDNS_RETURN_GOOD) {
819 snprintf(test_info->base_output,
820 MAX_BASE_OUTPUT_LEN,
821 "Cannot get section '%s': %s (%d)",
822 section,
823 getdns_get_errorstr_by_id(ret),
824 ret);
825 return EXIT_UNKNOWN;
826 }
827
828 if ((ret = getdns_list_get_length(*answers, no_answers)) != GETDNS_RETURN_GOOD) {
829 snprintf(test_info->base_output,
830 MAX_BASE_OUTPUT_LEN,
831 "Cannot get number of items in '%s': %s (%d)",
832 section,
833 getdns_get_errorstr_by_id(ret),
834 ret);
835 return EXIT_UNKNOWN;
836 }
837 if (*no_answers <= 0) {
838 snprintf(test_info->base_output,
839 MAX_BASE_OUTPUT_LEN,
840 "Zero entries in '%s'",
841 section);
842 return EXIT_WARNING;
843 }
844
845 return EXIT_OK;
846 }
847
check_answer_type(struct test_info_s * test_info,const getdns_dict * response,uint32_t rrtype)848 static exit_value check_answer_type(struct test_info_s *test_info,
849 const getdns_dict *response,
850 uint32_t rrtype)
851 {
852 getdns_list *answers;
853 size_t no_answers;
854 exit_value xit;
855
856 if ((xit = get_answers(test_info, response, "answer", &answers, &no_answers)) != EXIT_OK)
857 return xit;
858
859 for (size_t i = 0; i < no_answers; ++i) {
860 getdns_dict *answer;
861 getdns_return_t ret;
862
863 if ((ret = getdns_list_get_dict(answers, i, &answer)) != GETDNS_RETURN_GOOD) {
864 snprintf(test_info->base_output,
865 MAX_BASE_OUTPUT_LEN,
866 "Cannot get answer number %zu: %s (%d)",
867 i,
868 getdns_get_errorstr_by_id(ret),
869 ret);
870 return EXIT_UNKNOWN;
871 }
872
873 uint32_t rtype;
874
875 if ((ret = getdns_dict_get_int(answer, "type", &rtype)) != GETDNS_RETURN_GOOD) {
876 snprintf(test_info->base_output,
877 MAX_BASE_OUTPUT_LEN,
878 "Cannot get answer type: %s (%d)",
879 getdns_get_errorstr_by_id(ret),
880 ret);
881 return EXIT_UNKNOWN;
882 }
883 if (rtype == rrtype)
884 return EXIT_OK;
885 }
886
887 strcpy(test_info->base_output, "Answer does not contain expected type");
888 return EXIT_UNKNOWN;
889 }
890
search_check(struct test_info_s * test_info,const char * lookup_name,uint16_t lookup_type,getdns_dict ** response,uint32_t * rtt,getdns_bindata ** auth_status,time_t * cert_expire_time)891 static exit_value search_check(struct test_info_s *test_info,
892 const char *lookup_name,
893 uint16_t lookup_type,
894 getdns_dict **response,
895 uint32_t *rtt,
896 getdns_bindata **auth_status,
897 time_t *cert_expire_time)
898 {
899 exit_value xit;
900 getdns_dict *resp;
901
902 if ((xit = search(test_info, lookup_name, lookup_type, NULL, &resp, NULL)) != EXIT_OK)
903 return xit;
904
905 if ((xit = check_result(test_info, resp)) != EXIT_OK)
906 return xit;
907
908 if ((xit = get_report_info(test_info, resp, rtt, auth_status, cert_expire_time)) != EXIT_OK)
909 return xit;
910
911 if ((xit = check_answer_type(test_info, resp, lookup_type)) != EXIT_OK)
912 return xit;
913
914 if (response)
915 *response = resp;
916 return xit;
917 }
918
parse_search_check(struct test_info_s * test_info,char ** av,const char * usage,getdns_dict ** response,uint32_t * rtt,getdns_bindata ** auth_status,time_t * cert_expire_time)919 static exit_value parse_search_check(struct test_info_s *test_info,
920 char **av,
921 const char *usage,
922 getdns_dict **response,
923 uint32_t *rtt,
924 getdns_bindata **auth_status,
925 time_t *cert_expire_time)
926 {
927 const char *lookup_name = DEFAULT_LOOKUP_NAME;
928 uint32_t lookup_type = DEFAULT_LOOKUP_TYPE;
929 exit_value xit;
930
931 if ((xit = get_name_type_args(test_info, &av, &lookup_name, &lookup_type)) != EXIT_OK)
932 return xit;
933
934 if (*av) {
935 strcpy(test_info->base_output, usage);
936 return EXIT_USAGE;
937 }
938
939 return search_check(test_info,
940 lookup_name, lookup_type,
941 response,
942 rtt, auth_status, cert_expire_time);
943 }
944
945 /**
946 ** Test routines.
947 **/
948
test_lookup(struct test_info_s * test_info,char ** av)949 static exit_value test_lookup(struct test_info_s *test_info,
950 char ** av)
951 {
952 exit_value xit;
953
954 if ((xit = parse_search_check(test_info,
955 av,
956 "lookup takes arguments [<name> [<type>]]",
957 NULL,
958 NULL,
959 NULL,
960 NULL)) != EXIT_OK)
961 return xit;
962
963 strcpy(test_info->base_output, "Lookup succeeded");
964 return EXIT_OK;
965 }
966
test_rtt(struct test_info_s * test_info,char ** av)967 static exit_value test_rtt(struct test_info_s *test_info,
968 char ** av)
969 {
970 exit_value xit;
971 int critical_ms = RTT_CRITICAL_MS;
972 int warning_ms = RTT_WARNING_MS;
973 uint32_t rtt_val;
974
975 get_thresholds(&av, &critical_ms, &warning_ms);
976
977 if ((xit = parse_search_check(test_info,
978 av,
979 "rtt takes arguments [warn-ms,crit-ms] [<name> [<type>]]",
980 NULL,
981 &rtt_val,
982 NULL,
983 NULL)) != EXIT_OK)
984 return xit;
985
986 snprintf(test_info->base_output,
987 MAX_BASE_OUTPUT_LEN,
988 "RTT lookup succeeded in %dms",
989 rtt_val);
990
991 if ((int) rtt_val > critical_ms)
992 return EXIT_CRITICAL;
993 else if ((int) rtt_val > warning_ms)
994 return EXIT_WARNING;
995 return EXIT_OK;
996 }
997
test_authenticate(struct test_info_s * test_info,char ** av)998 static exit_value test_authenticate(struct test_info_s *test_info,
999 char ** av)
1000 {
1001 exit_value xit;
1002 getdns_bindata *auth_status;
1003
1004 if ((xit = parse_search_check(test_info,
1005 av,
1006 "auth takes arguments [<name> [<type>]]",
1007 NULL,
1008 NULL,
1009 &auth_status,
1010 NULL)) != EXIT_OK)
1011 return xit;
1012
1013 if (!auth_status || strcmp((char *) auth_status->data, "Success") != 0) {
1014 strcpy(test_info->base_output, "Authentication failed");
1015 return EXIT_CRITICAL;
1016 } else {
1017 strcpy(test_info->base_output, "Authentication succeeded");
1018 return EXIT_OK;
1019 }
1020 }
1021
test_certificate_valid(struct test_info_s * test_info,char ** av)1022 static exit_value test_certificate_valid(struct test_info_s *test_info,
1023 char **av)
1024 {
1025 exit_value xit;
1026 int warning_days = CERT_EXPIRY_WARNING_DAYS;
1027 int critical_days = CERT_EXPIRY_CRITICAL_DAYS;
1028 time_t expire_time;
1029
1030 get_thresholds(&av, &critical_days, &warning_days);
1031
1032 if ((xit = parse_search_check(test_info,
1033 av,
1034 "cert-valid takes arguments [warn-days,crit-days] [<name> [<type>]]",
1035 NULL,
1036 NULL,
1037 NULL,
1038 &expire_time)) != EXIT_OK)
1039 return xit;
1040
1041
1042 if (expire_time == 0) {
1043 strcpy(test_info->base_output, "No PKIX certificate");
1044 return EXIT_CRITICAL;
1045 }
1046
1047 time_t now = time(NULL);
1048 int days_to_expiry = (expire_time - now) / 86400;
1049
1050 if (days_to_expiry < 0) {
1051 snprintf(test_info->base_output,
1052 MAX_BASE_OUTPUT_LEN,
1053 "Certificate expired %d day%s ago",
1054 -days_to_expiry,
1055 (days_to_expiry < -1) ? "s" : "");
1056 return EXIT_CRITICAL;
1057 }
1058 if (days_to_expiry == 0) {
1059 strcpy(test_info->base_output, "Certificate expires today");
1060 } else {
1061 snprintf(test_info->base_output,
1062 MAX_BASE_OUTPUT_LEN,
1063 "Certificate will expire in %d day%s",
1064 days_to_expiry,
1065 (days_to_expiry > 1) ? "s" : "");
1066 }
1067 if (days_to_expiry <= critical_days) {
1068 return EXIT_CRITICAL;
1069 }
1070 if (days_to_expiry <= warning_days) {
1071 return EXIT_WARNING;
1072 }
1073 return EXIT_OK;
1074 }
1075
test_qname_minimisation(struct test_info_s * test_info,char ** av)1076 static exit_value test_qname_minimisation(struct test_info_s *test_info,
1077 char ** av)
1078 {
1079 if (*av) {
1080 strcpy(test_info->base_output, "qname-min takes no arguments");
1081 return EXIT_USAGE;
1082 }
1083
1084 getdns_dict *response;
1085 exit_value xit;
1086
1087 if ((xit = search_check(test_info,
1088 "qnamemintest.internet.nl",
1089 GETDNS_RRTYPE_TXT,
1090 &response,
1091 NULL,
1092 NULL,
1093 NULL)) != EXIT_OK)
1094 return xit;
1095
1096 getdns_list *answers;
1097 size_t no_answers;
1098
1099 if ((xit = get_answers(test_info, response, "answer", &answers, &no_answers)) != EXIT_OK)
1100 return xit;
1101
1102 for (size_t i = 0; i < no_answers; ++i) {
1103 getdns_dict *answer;
1104 getdns_return_t ret;
1105
1106 if ((ret = getdns_list_get_dict(answers, i, &answer)) != GETDNS_RETURN_GOOD) {
1107 snprintf(test_info->base_output,
1108 MAX_BASE_OUTPUT_LEN,
1109 "Cannot get answer number %zu: %s (%d)",
1110 i,
1111 getdns_get_errorstr_by_id(ret),
1112 ret);
1113 return EXIT_UNKNOWN;
1114 }
1115
1116 uint32_t rtype;
1117
1118 if ((ret = getdns_dict_get_int(answer, "type", &rtype)) != GETDNS_RETURN_GOOD) {
1119 snprintf(test_info->base_output,
1120 MAX_BASE_OUTPUT_LEN,
1121 "Cannot get answer type: %s (%d)",
1122 getdns_get_errorstr_by_id(ret),
1123 ret);
1124 return EXIT_UNKNOWN;
1125 }
1126 if (rtype != GETDNS_RRTYPE_TXT)
1127 continue;
1128
1129 getdns_bindata *rtxt;
1130
1131 if ((ret = getdns_dict_get_bindata(answer, "/rdata/txt_strings/0", &rtxt)) != GETDNS_RETURN_GOOD) {
1132 strcpy(test_info->base_output, "No answer text");
1133 return EXIT_UNKNOWN;
1134 }
1135
1136 if (rtxt->size > 0 ) {
1137 switch(rtxt->data[0]) {
1138 case 'H':
1139 strcpy(test_info->base_output, "QNAME minimisation ON");
1140 return EXIT_OK;
1141
1142 case 'N':
1143 strcpy(test_info->base_output, "QNAME minimisation OFF");
1144 return EXIT_CRITICAL;
1145
1146 default:
1147 /* Unrecognised message. */
1148 break;
1149 }
1150 }
1151 }
1152
1153 strcpy(test_info->base_output, "No valid QNAME minimisation data");
1154 return EXIT_UNKNOWN;
1155 }
1156
test_padding(struct test_info_s * test_info,char ** av)1157 static exit_value test_padding(struct test_info_s *test_info,
1158 char ** av)
1159 {
1160 getdns_dict *response;
1161 exit_value xit;
1162 long blocksize;
1163 char *endptr;
1164 const char USAGE[] = "padding takes arguments <blocksize> [<name> [<type>]]";
1165
1166 if (!*av || (blocksize = strtol(*av, &endptr, 10), *endptr != '\0' || blocksize < 0)) {
1167 strcpy(test_info->base_output, USAGE);
1168 return EXIT_USAGE;
1169 }
1170 ++av;
1171
1172 getdns_return_t ret;
1173 if ((ret = getdns_context_set_tls_query_padding_blocksize(test_info->context, (uint16_t) blocksize)) != GETDNS_RETURN_GOOD) {
1174 snprintf(test_info->base_output,
1175 MAX_BASE_OUTPUT_LEN,
1176 "Cannot set padding blocksize: %s (%d)",
1177 getdns_get_errorstr_by_id(ret),
1178 ret);
1179 return EXIT_UNKNOWN;
1180 }
1181
1182 if ((xit = parse_search_check(test_info,
1183 av,
1184 USAGE,
1185 &response,
1186 NULL,
1187 NULL,
1188 NULL)) != EXIT_OK)
1189 return xit;
1190
1191 getdns_list *answers;
1192 size_t no_answers;
1193
1194 if ((xit = get_answers(test_info, response, "additional", &answers, &no_answers)) != EXIT_OK)
1195 return xit;
1196
1197 for (size_t i = 0; i < no_answers; ++i) {
1198 getdns_dict *answer;
1199 getdns_return_t ret;
1200
1201 if ((ret = getdns_list_get_dict(answers, i, &answer)) != GETDNS_RETURN_GOOD) {
1202 snprintf(test_info->base_output,
1203 MAX_BASE_OUTPUT_LEN,
1204 "Cannot get answer number %zu: %s (%d)",
1205 i,
1206 getdns_get_errorstr_by_id(ret),
1207 ret);
1208 return EXIT_UNKNOWN;
1209 }
1210
1211 uint32_t rtype;
1212
1213 if ((ret = getdns_dict_get_int(answer, "type", &rtype)) != GETDNS_RETURN_GOOD) {
1214 snprintf(test_info->base_output,
1215 MAX_BASE_OUTPUT_LEN,
1216 "Cannot get answer type: %s (%d)",
1217 getdns_get_errorstr_by_id(ret),
1218 ret);
1219 return EXIT_UNKNOWN;
1220 }
1221 if (rtype != GETDNS_RRTYPE_OPT)
1222 continue;
1223
1224 getdns_list *options;
1225 size_t no_options;
1226
1227 if ((ret = getdns_dict_get_list(answer, "/rdata/options", &options)) != GETDNS_RETURN_GOOD) {
1228 goto no_padding;
1229 }
1230 if ((ret = getdns_list_get_length(options, &no_options)) != GETDNS_RETURN_GOOD) {
1231 snprintf(test_info->base_output,
1232 MAX_BASE_OUTPUT_LEN,
1233 "Cannot get number of options: %s (%d)",
1234 getdns_get_errorstr_by_id(ret),
1235 ret);
1236 return EXIT_UNKNOWN;
1237 }
1238
1239 for (size_t j = 0; j < no_options; ++j) {
1240 getdns_dict *option;
1241 uint32_t code;
1242
1243 if ((ret = getdns_list_get_dict(options, j, &option)) != GETDNS_RETURN_GOOD) {
1244 snprintf(test_info->base_output,
1245 MAX_BASE_OUTPUT_LEN,
1246 "Cannot get option number %zu: %s (%d)",
1247 j,
1248 getdns_get_errorstr_by_id(ret),
1249 ret);
1250 return EXIT_UNKNOWN;
1251 }
1252 if ((ret = getdns_dict_get_int(option, "option_code", &code)) != GETDNS_RETURN_GOOD) {
1253 snprintf(test_info->base_output,
1254 MAX_BASE_OUTPUT_LEN,
1255 "Cannot get option code: %s (%d)",
1256 getdns_get_errorstr_by_id(ret),
1257 ret);
1258 return EXIT_UNKNOWN;
1259 }
1260
1261 if (code != EDNS0_PADDING_CODE)
1262 continue;
1263
1264 /* Yes, we have padding! */
1265 getdns_bindata *data;
1266
1267 if ((ret = getdns_dict_get_bindata(option, "option_data", &data)) != GETDNS_RETURN_GOOD) {
1268 snprintf(test_info->base_output,
1269 MAX_BASE_OUTPUT_LEN,
1270 "Cannot get option code: %s (%d)",
1271 getdns_get_errorstr_by_id(ret),
1272 ret);
1273 return EXIT_UNKNOWN;
1274 }
1275
1276 snprintf(test_info->base_output,
1277 MAX_BASE_OUTPUT_LEN,
1278 "Padding found, length %zu",
1279 data->size);
1280 return EXIT_OK;
1281 }
1282 }
1283
1284 no_padding:
1285 strcpy(test_info->base_output, "No padding found");
1286 return EXIT_CRITICAL;
1287 }
1288
test_keepalive(struct test_info_s * test_info,char ** av)1289 static exit_value test_keepalive(struct test_info_s *test_info,
1290 char ** av)
1291 {
1292 getdns_dict *response;
1293 exit_value xit;
1294 const uint64_t MAX_TIMEOUT_VALUE = 0xffff * 10000;
1295
1296 getdns_return_t ret;
1297 if ((ret = getdns_context_set_idle_timeout(test_info->context, MAX_TIMEOUT_VALUE)) != GETDNS_RETURN_GOOD) {
1298 snprintf(test_info->base_output,
1299 MAX_BASE_OUTPUT_LEN,
1300 "Cannot set keepalive timeout: %s (%d)",
1301 getdns_get_errorstr_by_id(ret),
1302 ret);
1303 return EXIT_UNKNOWN;
1304 }
1305
1306 if ((xit = parse_search_check(test_info,
1307 av,
1308 "keepalive takes arguments [<name> [<type>]]",
1309 &response,
1310 NULL,
1311 NULL,
1312 NULL)) != EXIT_OK)
1313 return xit;
1314
1315 getdns_list *l;
1316
1317 if ((ret = getdns_dict_get_list(response, "call_reporting", &l)) != GETDNS_RETURN_GOOD) {
1318 snprintf(test_info->base_output,
1319 MAX_BASE_OUTPUT_LEN,
1320 "Cannot get call report: %s (%d)",
1321 getdns_get_errorstr_by_id(ret),
1322 ret);
1323 return EXIT_UNKNOWN;
1324 }
1325
1326 getdns_dict *d;
1327 if ((ret = getdns_list_get_dict(l, 0, &d)) != GETDNS_RETURN_GOOD) {
1328 snprintf(test_info->base_output,
1329 MAX_BASE_OUTPUT_LEN,
1330 "Cannot get call report first item: %s (%d)",
1331 getdns_get_errorstr_by_id(ret),
1332 ret);
1333 return EXIT_UNKNOWN;
1334 }
1335
1336 /* Search is forced to be TCP or TLS, so server keepalive flag must exist. */
1337 uint32_t server_keepalive_received;
1338 if ((ret = getdns_dict_get_int(d, "server_keepalive_received", &server_keepalive_received)) != GETDNS_RETURN_GOOD) {
1339 snprintf(test_info->base_output,
1340 MAX_BASE_OUTPUT_LEN,
1341 "Cannot get server keepalive flag: %s (%d)",
1342 getdns_get_errorstr_by_id(ret),
1343 ret);
1344 return EXIT_UNKNOWN;
1345 }
1346
1347 if (server_keepalive_received) {
1348 uint32_t t;
1349 bool overflow = false;
1350
1351 if (!((ret = getdns_dict_get_int(d, "idle timeout in ms", &t)) == GETDNS_RETURN_GOOD ||
1352 (overflow = true, ret = getdns_dict_get_int(d, "idle timeout in ms (overflow)", &t)) == GETDNS_RETURN_GOOD)) {
1353 snprintf(test_info->base_output,
1354 MAX_BASE_OUTPUT_LEN,
1355 "Cannot get idle timeout: %s (%d)",
1356 getdns_get_errorstr_by_id(ret),
1357 ret);
1358 return EXIT_UNKNOWN;
1359 }
1360
1361 if (overflow) {
1362 strcpy(test_info->base_output, "Server sent keepalive, idle timeout now (overflow)");
1363 } else {
1364 snprintf(test_info->base_output,
1365 MAX_BASE_OUTPUT_LEN,
1366 "Server sent keepalive, idle timeout now %ums",
1367 t);
1368 }
1369 return EXIT_OK;
1370 } else {
1371 strcpy(test_info->base_output, "Server did not send keepalive");
1372 return EXIT_CRITICAL;
1373 }
1374 }
1375
test_dnssec_validate(struct test_info_s * test_info,char ** av)1376 static exit_value test_dnssec_validate(struct test_info_s *test_info,
1377 char ** av)
1378 {
1379 if (*av) {
1380 strcpy(test_info->base_output, "dnssec-validate takes no arguments");
1381 return EXIT_USAGE;
1382 }
1383
1384 getdns_dict *response;
1385 exit_value xit;
1386
1387 if ((xit = search(test_info,
1388 "dnssec-failed.org",
1389 GETDNS_RRTYPE_A,
1390 NULL,
1391 &response,
1392 NULL)) != EXIT_OK)
1393 return xit;
1394
1395 uint32_t error_id, rcode;
1396
1397 if ((xit = get_result(test_info, response, &error_id, &rcode)) != EXIT_OK)
1398 return xit;
1399
1400 if (error_id == GETDNS_RESPSTATUS_ALL_TIMEOUT) {
1401 strcpy(test_info->base_output, "Search timed out");
1402 return EXIT_CRITICAL;
1403 }
1404
1405 if (rcode != GETDNS_RCODE_SERVFAIL) {
1406 strcpy(test_info->base_output, "Server does NOT validate DNSSEC");
1407 return EXIT_CRITICAL;
1408 }
1409
1410 /*
1411 * Rerun the query, but this time set the CD bit. The lookup should
1412 * succeed.
1413 */
1414 getdns_return_t ret;
1415 getdns_dict *response2;
1416 getdns_dict *extensions = getdns_dict_create();
1417
1418 if ((ret = getdns_dict_set_int(extensions, "/header/cd", 1)) != GETDNS_RETURN_GOOD) {
1419 snprintf(test_info->base_output,
1420 MAX_BASE_OUTPUT_LEN,
1421 "Cannot set CD bit: %s (%d)",
1422 getdns_get_errorstr_by_id(ret),
1423 ret);
1424 getdns_dict_destroy(extensions);
1425 return EXIT_UNKNOWN;
1426 }
1427
1428 if ((xit = search(test_info,
1429 "dnssec-failed.org",
1430 GETDNS_RRTYPE_A,
1431 extensions,
1432 &response2,
1433 NULL)) != EXIT_OK)
1434 return xit;
1435
1436 getdns_dict_destroy(extensions);
1437
1438 /*
1439 * Only now get report info from the first search, so that any
1440 * verbose output appears after the context/response dumps.
1441 */
1442 if ((xit = get_report_info(test_info, response, NULL, NULL, NULL)) != EXIT_OK)
1443 return xit;
1444
1445 if ((xit = get_result(test_info, response2, &error_id, &rcode)) != EXIT_OK)
1446 return xit;
1447
1448 if (error_id == GETDNS_RESPSTATUS_ALL_TIMEOUT) {
1449 strcpy(test_info->base_output, "Search timed out");
1450 return EXIT_CRITICAL;
1451 }
1452
1453 if (error_id != GETDNS_RESPSTATUS_GOOD || rcode != GETDNS_RCODE_NOERROR) {
1454 strcpy(test_info->base_output, "Server error - cannot determine DNSSEC status");
1455 return EXIT_UNKNOWN;
1456 }
1457
1458 strcpy(test_info->base_output, "Server validates DNSSEC");
1459 return EXIT_OK;
1460 }
1461
test_tls13(struct test_info_s * test_info,char ** av)1462 static exit_value test_tls13(struct test_info_s *test_info,
1463 char ** av)
1464 {
1465 exit_value xit;
1466 getdns_return_t ret;
1467
1468 if (*av) {
1469 strcpy(test_info->base_output, "tls-1.3 takes no arguments");
1470 return EXIT_USAGE;
1471 }
1472
1473 /*
1474 * Set cipher list to TLS 1.3-only ciphers. If we are using
1475 * an OpenSSL version that doesn't support TLS 1.3 this will cause
1476 * a Bad Context error on the lookup.
1477 */
1478 if ((ret = getdns_context_set_tls_cipher_list(test_info->context, TLS13_CIPHER_SUITE)) != GETDNS_RETURN_GOOD) {
1479 snprintf(test_info->base_output,
1480 MAX_BASE_OUTPUT_LEN,
1481 "Cannot set TLS 1.3 cipher list: %s (%d)",
1482 getdns_get_errorstr_by_id(ret),
1483 ret);
1484 return EXIT_UNKNOWN;
1485 }
1486
1487 getdns_dict *response;
1488
1489 if ((xit = search(test_info,
1490 DEFAULT_LOOKUP_NAME,
1491 DEFAULT_LOOKUP_TYPE,
1492 NULL,
1493 &response,
1494 &ret)) != EXIT_OK) {
1495 if (xit == EXIT_CRITICAL) {
1496 if (ret == GETDNS_RETURN_BAD_CONTEXT) {
1497 strcpy(test_info->base_output, "Your version of OpenSSL does not support TLS 1.3 ciphers. You need at least OpenSSL 1.1.1.");
1498 return EXIT_UNKNOWN;
1499 } else {
1500 strcpy(test_info->base_output, "Cannot establish TLS 1.3 connection.");
1501 return EXIT_CRITICAL;
1502 }
1503 } else {
1504 return xit;
1505 }
1506 }
1507
1508 if ((xit = get_report_info(test_info, response, NULL, NULL, NULL)) != EXIT_OK)
1509 return xit;
1510
1511 getdns_list *l;
1512
1513 if ((ret = getdns_dict_get_list(response, "call_reporting", &l)) != GETDNS_RETURN_GOOD) {
1514 snprintf(test_info->base_output,
1515 MAX_BASE_OUTPUT_LEN,
1516 "Cannot get call report: %s (%d)",
1517 getdns_get_errorstr_by_id(ret),
1518 ret);
1519 return EXIT_UNKNOWN;
1520 }
1521
1522 getdns_dict *d;
1523
1524 if ((ret = getdns_list_get_dict(l, 0, &d)) != GETDNS_RETURN_GOOD) {
1525 snprintf(test_info->base_output,
1526 MAX_BASE_OUTPUT_LEN,
1527 "Cannot get call report first item: %s (%d)",
1528 getdns_get_errorstr_by_id(ret),
1529 ret);
1530 return EXIT_UNKNOWN;
1531 }
1532
1533 /* Search is forced TLS, so tls_version flag must exist. */
1534 getdns_bindata *version;
1535
1536 if ((ret = getdns_dict_get_bindata(d, "tls_version", &version)) != GETDNS_RETURN_GOOD) {
1537 snprintf(test_info->base_output,
1538 MAX_BASE_OUTPUT_LEN,
1539 "Cannot get TLS version: %s (%d)",
1540 getdns_get_errorstr_by_id(ret),
1541 ret);
1542 return EXIT_UNKNOWN;
1543 }
1544
1545 const char TLS_1_3_SIG[] = "TLSv1.3";
1546
1547 if (strncmp((const char *)version->data, TLS_1_3_SIG, sizeof(TLS_1_3_SIG) - 1) != 0) {
1548 strcpy(test_info->base_output, "Server does not support TLS 1.3");
1549 return EXIT_CRITICAL;
1550 }
1551
1552 if ((xit = check_result(test_info, response)) != EXIT_OK)
1553 return xit;
1554
1555 strcpy(test_info->base_output, "Server supports TLS 1.3");
1556 return EXIT_OK;
1557 }
1558
1559 struct async_query
1560 {
1561 const char *name;
1562 unsigned done_order;
1563 bool error;
1564 };
1565
out_of_order_callback(getdns_context * context,getdns_callback_type_t callback_type,getdns_dict * response,void * userarg,getdns_transaction_t transaction_id)1566 static void out_of_order_callback(getdns_context *context,
1567 getdns_callback_type_t callback_type,
1568 getdns_dict *response,
1569 void *userarg,
1570 getdns_transaction_t transaction_id)
1571 {
1572 (void) context;
1573 (void) callback_type;
1574 (void) response;
1575 (void) transaction_id;
1576
1577 struct async_query *query = (struct async_query *) userarg;
1578 static unsigned callback_no;
1579
1580 query->done_order = ++callback_no;
1581 query->error = (callback_type != GETDNS_CALLBACK_COMPLETE);
1582
1583 if (test_info.verbosity >= VERBOSITY_ADDITIONAL) {
1584 if (test_info.monitoring) {
1585 snprintcat(test_info.perf_output,
1586 MAX_PERF_OUTPUT_LEN,
1587 "->%s;",
1588 query->name);
1589 } else {
1590 printf("Result:\t\t\t%s", query->name);
1591 if (query->error)
1592 printf(" (callback %d)\n", callback_type);
1593 else
1594 putchar('\n');
1595 }
1596 }
1597
1598 }
1599
test_out_of_order(struct test_info_s * test_info,char ** av)1600 static exit_value test_out_of_order(struct test_info_s *test_info,
1601 char ** av)
1602 {
1603 if (*av) {
1604 strcpy(test_info->base_output, "OOOR takes no arguments");
1605 return EXIT_USAGE;
1606 }
1607
1608 /*
1609 * A set of asynchronous queries to send. One exists.
1610 *
1611 * Replies from delay.getdns.api come back with a 1s TTL. It turns out
1612 * that delay.getdns.api will ignore all leftmost labels bar the first,
1613 * so to further mitigate any cache effects insert a random string
1614 * into the name.
1615 */
1616 const char GOOD_NAME[] = "getdnsapi.net";
1617 char delay_name[50];
1618 srand(time(NULL));
1619 snprintf(delay_name, sizeof(delay_name) - 1, "400.n%04d.delay.getdnsapi.net", rand() % 10000);
1620 struct async_query async_queries[] = {
1621 { delay_name, 0, false },
1622 { GOOD_NAME, 0, false }
1623 };
1624 unsigned NQUERIES = sizeof(async_queries) / sizeof(async_queries[0]);
1625
1626 /* Pre-load the cache with result of the good query. */
1627 getdns_dict *response;
1628 exit_value xit;
1629
1630 if ((xit = search_check(test_info,
1631 GOOD_NAME,
1632 GETDNS_RRTYPE_AAAA,
1633 &response,
1634 NULL,
1635 NULL,
1636 NULL)) != EXIT_OK)
1637 return xit;
1638
1639 /* Now launch async searches for all the above. */
1640 for (unsigned i = 0; i < NQUERIES; ++i) {
1641 getdns_return_t ret;
1642
1643 if (test_info->verbosity >= VERBOSITY_ADDITIONAL) {
1644 if (test_info->monitoring) {
1645 snprintcat(test_info->perf_output,
1646 MAX_PERF_OUTPUT_LEN,
1647 "%s->;",
1648 async_queries[i].name);
1649 } else {
1650 printf("Search:\t\t\t%s\n", async_queries[i].name);
1651 }
1652 }
1653
1654 if ((ret = getdns_general(test_info->context,
1655 async_queries[i].name,
1656 GETDNS_RRTYPE_AAAA,
1657 NULL,
1658 &async_queries[i],
1659 NULL,
1660 out_of_order_callback)) != GETDNS_RETURN_GOOD) {
1661 snprintf(test_info->base_output,
1662 MAX_BASE_OUTPUT_LEN,
1663 "Async search failed: %s (%d)",
1664 getdns_get_errorstr_by_id(ret),
1665 ret);
1666 return EXIT_UNKNOWN;
1667 }
1668 }
1669
1670 /* And wait for them to complete. */
1671 getdns_context_run(test_info->context);
1672
1673 for (unsigned i = 0; i < NQUERIES; ++i) {
1674 if (async_queries[i].error) {
1675 strcpy(test_info->base_output, "Query did not complete");
1676 return EXIT_UNKNOWN;
1677 }
1678 }
1679
1680 if (async_queries[NQUERIES - 1].done_order == NQUERIES) {
1681 strcpy(test_info->base_output, "Responses are in query order");
1682 return EXIT_CRITICAL;
1683 } else {
1684 strcpy(test_info->base_output, "Responses are not in query order");
1685 return EXIT_OK;
1686 }
1687 }
1688
1689 static struct test_funcs_s
1690 {
1691 const char *name;
1692 bool implies_tls;
1693 bool implies_tcp;
1694 exit_value (*func)(struct test_info_s *test_info, char **av);
1695 } TESTS[] =
1696 {
1697 { "lookup", false, false, test_lookup },
1698 { "rtt", false, false, test_rtt },
1699 { "qname-min", false, false, test_qname_minimisation },
1700 { "tls-auth", true, false, test_authenticate },
1701 { "tls-cert-valid", true, false, test_certificate_valid },
1702 { "tls-padding", true, false, test_padding },
1703 { "keepalive", false, true, test_keepalive },
1704 { "dnssec-validate", false, false, test_dnssec_validate },
1705 { "tls-1.3", true, false, test_tls13 },
1706 { "OOOR", false, true, test_out_of_order },
1707 { NULL, false, false, NULL }
1708 };
1709
main(int ac,char * av[])1710 int main(int ac, char *av[])
1711 {
1712 getdns_return_t ret;
1713 getdns_list *pinset = NULL;
1714 size_t pinset_size = 0;
1715 bool strict_usage_profile = false;
1716 bool use_udp = false;
1717 bool use_tcp = false;
1718 bool use_tls = false;
1719
1720 (void) ac;
1721
1722 test_info.errout = stderr;
1723 atexit(exit_tidy);
1724 if ((ret = getdns_context_create(&test_info.context, 1)) != GETDNS_RETURN_GOOD) {
1725 fprintf(test_info.errout,
1726 "Create context failed: %s (%d)\n",
1727 getdns_get_errorstr_by_id(ret),
1728 ret);
1729 exit(EXIT_UNKNOWN);
1730 }
1731
1732 for (++av; *av && *av[0] == '-'; ++av) {
1733 if (strcmp(*av, "-M") == 0 ||
1734 strcmp(*av, "--monitoring") == 0) {
1735 test_info.monitoring = true;
1736 test_info.errout = stdout;
1737 } else if (strcmp(*av, "-D") == 0 ||
1738 strcmp(*av, "--debug") == 0) {
1739 test_info.debug_output = true;
1740 } else if (strcmp(*av, "-E") == 0 ||
1741 strcmp(*av, "--fail-on-dns-errors") == 0) {
1742 test_info.fail_on_dns_errors = true;
1743 } else if (strcmp(*av, "-u") == 0 ||
1744 strcmp(*av, "--udp") == 0 ) {
1745 use_udp = true;
1746 } else if (strcmp(*av, "-t") == 0 ||
1747 strcmp(*av, "--tcp") == 0 ) {
1748 use_tcp = true;
1749 } else if (strcmp(*av, "-T") == 0 ||
1750 strcmp(*av, "--tls") == 0 ) {
1751 use_tls = true;
1752 } else if (strcmp(*av, "-S") == 0 ||
1753 strcmp(*av, "--strict-usage-profile") == 0 ) {
1754 strict_usage_profile = true;
1755 use_tls = true;
1756 } else if (strcmp(*av, "-K") == 0 ||
1757 strcmp(*av, "--spki-pin") == 0 ) {
1758 ++av;
1759 if (!*av) {
1760 fputs("pin string of the form " EXAMPLE_PIN "expected after -K|--pin\n", test_info.errout);
1761 exit(EXIT_UNKNOWN);
1762 }
1763
1764 getdns_dict *pin;
1765
1766 pin = getdns_pubkey_pin_create_from_string(test_info.context, *av);
1767 if (!pin) {
1768 fprintf(test_info.errout,
1769 "Could not convert '%s' into a public key pin.\n"
1770 "Good pins look like: " EXAMPLE_PIN "\n"
1771 "Please see RFC 7469 for details about the format.\n", *av);
1772 exit(EXIT_UNKNOWN);
1773 }
1774 if (!pinset)
1775 pinset = getdns_list_create_with_context(test_info.context);
1776 ret = getdns_list_set_dict(pinset,
1777 pinset_size++,
1778 pin);
1779 getdns_dict_destroy(pin);
1780 if (ret != GETDNS_RETURN_GOOD) {
1781 fprintf(test_info.errout,
1782 "Could not add pin '%s' to pin set.\n",
1783 *av);
1784 getdns_list_destroy(pinset);
1785 exit(EXIT_UNKNOWN);
1786
1787 }
1788 use_tls = true;
1789 } else if (strcmp(*av, "-v") == 0 ||
1790 strcmp(*av, "--verbose") == 0) {
1791 ++test_info.verbosity;
1792 } else if (strcmp(*av, "-V") == 0 ||
1793 strcmp(*av, "--version") == 0) {
1794 version();
1795 } else {
1796 usage();
1797 }
1798 }
1799
1800 if (*av == NULL || *av[0] != '@')
1801 usage();
1802
1803 /* Non-monitoring output is chatty by default. */
1804 if (!test_info.monitoring)
1805 ++test_info.verbosity;
1806
1807 const char *upstream = *av++;
1808 getdns_dict *resolver;
1809 getdns_bindata *address;
1810
1811 if ((ret = getdns_str2dict(&upstream[1], &resolver)) != GETDNS_RETURN_GOOD) {
1812 fprintf(test_info.errout,
1813 "Could not convert \"%s\" to an IP dict: %s (%d)\n",
1814 &upstream[1],
1815 getdns_get_errorstr_by_id(ret),
1816 ret);
1817 exit(EXIT_UNKNOWN);
1818 }
1819
1820 /* If the resolver info include TLS auth name, use TLS. */
1821 getdns_bindata *tls_auth_name;
1822 if (getdns_dict_get_bindata(resolver, "tls_auth_name", &tls_auth_name) == GETDNS_RETURN_GOOD)
1823 use_tls = true;
1824
1825 if ((ret = getdns_dict_get_bindata(resolver, "address_data", &address)) != GETDNS_RETURN_GOOD) {
1826 fprintf(test_info.errout,
1827 "\"%s\" did not translate to an IP dict: %s (%d)\n",
1828 &upstream[1],
1829 getdns_get_errorstr_by_id(ret),
1830 ret);
1831 getdns_dict_destroy(resolver);
1832 exit(EXIT_UNKNOWN);
1833 }
1834
1835 /* Set parameters on the resolver. */
1836 if (pinset) {
1837 ret = getdns_dict_set_list(resolver,
1838 "tls_pubkey_pinset",
1839 pinset);
1840 getdns_list_destroy(pinset);
1841 if (ret != GETDNS_RETURN_GOOD) {
1842 fprintf(test_info.errout,
1843 "Cannot set keys for \"%s\": %s (%d)\n",
1844 &upstream[1],
1845 getdns_get_errorstr_by_id(ret),
1846 ret);
1847 exit(EXIT_UNKNOWN);
1848 }
1849 }
1850
1851 /* Set getdns context to use the indicated resolver. */
1852 getdns_list *l = getdns_list_create();
1853 ret = getdns_list_set_dict(l, 0, resolver);
1854 getdns_dict_destroy(resolver);
1855 if (ret != GETDNS_RETURN_GOOD) {
1856 fprintf(test_info.errout,
1857 "Unable to add upstream '%s' to list: %s (%d)\n",
1858 upstream,
1859 getdns_get_errorstr_by_id(ret),
1860 ret);
1861 getdns_list_destroy(l);
1862 exit(EXIT_UNKNOWN);
1863 }
1864 ret = getdns_context_set_upstream_recursive_servers(test_info.context, l);
1865 getdns_list_destroy(l);
1866 if (ret != GETDNS_RETURN_GOOD) {
1867 fprintf(test_info.errout,
1868 "Unable to set upstream resolver to '%s': %s (%d)\n",
1869 upstream,
1870 getdns_get_errorstr_by_id(ret),
1871 ret);
1872 exit(EXIT_UNKNOWN);
1873 }
1874
1875 /* Set context to stub mode. */
1876 if ((ret = getdns_context_set_resolution_type(test_info.context, GETDNS_RESOLUTION_STUB)) != GETDNS_RETURN_GOOD) {
1877 fprintf(test_info.errout,
1878 "Unable to set stub mode: %s (%d)\n",
1879 getdns_get_errorstr_by_id(ret),
1880 ret);
1881 exit(EXIT_UNKNOWN);
1882 }
1883
1884 /* Set other context parameters. */
1885 if (strict_usage_profile) {
1886 ret = getdns_context_set_tls_authentication(test_info.context, GETDNS_AUTHENTICATION_REQUIRED);
1887 if (ret != GETDNS_RETURN_GOOD) {
1888 fprintf(test_info.errout,
1889 "Unable to set strict profile: %s (%d)\n",
1890 getdns_get_errorstr_by_id(ret),
1891 ret);
1892 exit(EXIT_UNKNOWN);
1893 }
1894 }
1895
1896 /* Choose and run test */
1897 const char *testname = *av;
1898
1899 if (!testname)
1900 usage();
1901 ++av;
1902
1903 const struct test_funcs_s *f;
1904 for (f = TESTS; f->name != NULL; ++f) {
1905 if (strcmp(testname, f->name) == 0)
1906 break;
1907 }
1908
1909 if (f->name == NULL) {
1910 fprintf(test_info.errout, "Unknown test %s\n", testname);
1911 exit(EXIT_UNKNOWN);
1912 }
1913
1914 if (f->implies_tcp) {
1915 if (use_udp) {
1916 fputs("Test requires TCP or TLS\n", test_info.errout);
1917 exit(EXIT_UNKNOWN);
1918 }
1919 if (!use_tls)
1920 use_tcp = true;
1921 }
1922
1923 if (f->implies_tls) {
1924 if (use_udp | use_tcp) {
1925 fputs("Test requires TLS, or TLS authentication specified\n", test_info.errout);
1926 exit(EXIT_UNKNOWN);
1927 }
1928 use_tls = true;
1929 }
1930
1931 if ((use_tls + use_udp + use_tcp) > 1) {
1932 fputs("Specify one only of -u, -t, -T\n", test_info.errout);
1933 exit(EXIT_UNKNOWN);
1934 }
1935
1936 if (use_tls || use_udp || use_tcp) {
1937 getdns_transport_list_t udp[] = { GETDNS_TRANSPORT_UDP };
1938 getdns_transport_list_t tcp[] = { GETDNS_TRANSPORT_TCP };
1939 getdns_transport_list_t tls[] = { GETDNS_TRANSPORT_TLS };
1940 getdns_transport_list_t *transport =
1941 (use_tls) ? tls : (use_tcp) ? tcp : udp;
1942 if ((ret = getdns_context_set_dns_transport_list(test_info.context, 1, transport)) != GETDNS_RETURN_GOOD) {
1943 fprintf(test_info.errout,
1944 "Unable to set %s transport: %s (%d)\n",
1945 (use_tls) ? "TLS" : (use_tcp) ? "TCP" : "UDP",
1946 getdns_get_errorstr_by_id(ret),
1947 ret);
1948 exit(EXIT_UNKNOWN);
1949 }
1950 }
1951
1952 exit_value xit = f->func(&test_info, av);
1953 const char *xit_text = "(\?\?\?)";
1954 FILE *out = stdout;
1955
1956 switch(xit) {
1957 case EXIT_OK:
1958 xit_text = "OK";
1959 break;
1960
1961 case EXIT_WARNING:
1962 xit_text = "WARNING";
1963 break;
1964
1965 case EXIT_CRITICAL:
1966 xit_text = "CRITICAL";
1967 break;
1968
1969 case EXIT_UNKNOWN:
1970 case EXIT_USAGE:
1971 xit = EXIT_UNKNOWN;
1972 xit_text = "UNKNOWN";
1973 out = test_info.errout;
1974 break;
1975 }
1976
1977 if (test_info.monitoring)
1978 fprintf(out, "DNS SERVER %s - ", xit_text);
1979 fputs(test_info.base_output, out);
1980
1981 if (test_info.verbosity >= VERBOSITY_ADDITIONAL &&
1982 test_info.monitoring &&
1983 strlen(test_info.perf_output) > 0) {
1984 fprintf(out, "|%s", test_info.perf_output);
1985 }
1986
1987 fputc('\n', out);
1988 exit(xit);
1989 }
1990