1 1.14 christos /* $NetBSD: mdig.c,v 1.15 2026/01/29 18:37:47 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.10 christos * SPDX-License-Identifier: MPL-2.0 7 1.10 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.7 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.3 christos #include <inttypes.h> 17 1.3 christos #include <stdbool.h> 18 1.1 christos #include <stdlib.h> 19 1.1 christos #include <string.h> 20 1.1 christos #include <unistd.h> 21 1.1 christos 22 1.12 christos #include <isc/attributes.h> 23 1.1 christos #include <isc/base64.h> 24 1.14 christos #include <isc/getaddresses.h> 25 1.1 christos #include <isc/hash.h> 26 1.1 christos #include <isc/hex.h> 27 1.1 christos #include <isc/log.h> 28 1.14 christos #include <isc/loop.h> 29 1.9 christos #include <isc/managers.h> 30 1.1 christos #include <isc/mem.h> 31 1.1 christos #include <isc/net.h> 32 1.12 christos #include <isc/netmgr.h> 33 1.3 christos #include <isc/nonce.h> 34 1.1 christos #include <isc/parseint.h> 35 1.10 christos #include <isc/portset.h> 36 1.3 christos #include <isc/random.h> 37 1.12 christos #include <isc/result.h> 38 1.1 christos #include <isc/sockaddr.h> 39 1.1 christos #include <isc/string.h> 40 1.12 christos #include <isc/time.h> 41 1.1 christos #include <isc/util.h> 42 1.1 christos 43 1.1 christos #include <dns/byaddr.h> 44 1.1 christos #include <dns/dispatch.h> 45 1.1 christos #include <dns/fixedname.h> 46 1.1 christos #include <dns/message.h> 47 1.1 christos #include <dns/name.h> 48 1.1 christos #include <dns/rdata.h> 49 1.6 christos #include <dns/rdataclass.h> 50 1.1 christos #include <dns/rdataset.h> 51 1.1 christos #include <dns/rdatatype.h> 52 1.1 christos #include <dns/request.h> 53 1.6 christos #include <dns/types.h> 54 1.1 christos #include <dns/view.h> 55 1.1 christos 56 1.15 christos #define CHECKM(str, x) \ 57 1.6 christos { \ 58 1.6 christos if ((x) != ISC_R_SUCCESS) { \ 59 1.6 christos fprintf(stderr, "mdig: %s failed with %s\n", (str), \ 60 1.6 christos isc_result_totext(x)); \ 61 1.13 christos exit(EXIT_FAILURE); \ 62 1.6 christos } \ 63 1.6 christos } 64 1.1 christos 65 1.1 christos #define RUNCHECK(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS) 66 1.1 christos 67 1.6 christos #define ADD_STRING(b, s) \ 68 1.6 christos { \ 69 1.6 christos if (strlen(s) >= isc_buffer_availablelength(b)) \ 70 1.6 christos return ((ISC_R_NOSPACE)); \ 71 1.6 christos else \ 72 1.6 christos isc_buffer_putstr(b, s); \ 73 1.6 christos } 74 1.1 christos 75 1.6 christos #define MXNAME (DNS_NAME_MAXTEXT + 1) 76 1.6 christos #define COMMSIZE 0xffff 77 1.6 christos #define OUTPUTBUF 32767 78 1.6 christos #define MAXPORT 0xffff 79 1.6 christos #define PORT 53 80 1.1 christos #define MAXTIMEOUT 0xffff 81 1.1 christos #define TCPTIMEOUT 10 82 1.1 christos #define UDPTIMEOUT 5 83 1.6 christos #define MAXTRIES 0xffffffff 84 1.1 christos 85 1.9 christos static isc_mem_t *mctx = NULL; 86 1.14 christos static isc_loopmgr_t *loopmgr = NULL; 87 1.9 christos static dns_requestmgr_t *requestmgr = NULL; 88 1.9 christos static const char *batchname = NULL; 89 1.9 christos static FILE *batchfp = NULL; 90 1.7 christos static bool burst = false; 91 1.3 christos static bool have_ipv4 = false; 92 1.3 christos static bool have_ipv6 = false; 93 1.3 christos static bool have_src = false; 94 1.3 christos static bool tcp_mode = false; 95 1.3 christos static bool besteffort = true; 96 1.3 christos static bool display_short_form = false; 97 1.3 christos static bool display_headers = true; 98 1.3 christos static bool display_comments = true; 99 1.4 christos static int display_rrcomments = 0; 100 1.3 christos static bool display_ttlunits = true; 101 1.3 christos static bool display_ttl = true; 102 1.3 christos static bool display_class = true; 103 1.3 christos static bool display_crypto = true; 104 1.3 christos static bool display_multiline = false; 105 1.3 christos static bool display_question = true; 106 1.6 christos static bool display_answer = true; 107 1.3 christos static bool display_authority = true; 108 1.3 christos static bool display_additional = true; 109 1.3 christos static bool display_unknown_format = false; 110 1.6 christos static bool yaml = false; 111 1.3 christos static bool continue_on_error = false; 112 1.3 christos static uint32_t display_splitwidth = 0xffffffff; 113 1.1 christos static isc_sockaddr_t srcaddr; 114 1.9 christos static char *server = NULL; 115 1.1 christos static isc_sockaddr_t dstaddr; 116 1.1 christos static in_port_t port = 53; 117 1.1 christos static unsigned char cookie_secret[33]; 118 1.1 christos static int onfly = 0; 119 1.1 christos static char hexcookie[81]; 120 1.1 christos 121 1.14 christos static isc_sockaddr_t bind_any; 122 1.14 christos static isc_nm_t *netmgr = NULL; 123 1.14 christos static dns_dispatchmgr_t *dispatchmgr = NULL; 124 1.14 christos static dns_dispatch_t *dispatchvx = NULL; 125 1.14 christos static dns_view_t *view = NULL; 126 1.14 christos 127 1.1 christos struct query { 128 1.6 christos char textname[MXNAME]; /*% Name we're going to be 129 1.6 christos * looking up */ 130 1.3 christos bool recurse; 131 1.3 christos bool have_aaonly; 132 1.3 christos bool have_adflag; 133 1.3 christos bool have_cdflag; 134 1.3 christos bool have_zflag; 135 1.3 christos bool dnssec; 136 1.3 christos bool expire; 137 1.3 christos bool send_cookie; 138 1.1 christos char *cookie; 139 1.3 christos bool nsid; 140 1.1 christos dns_rdatatype_t rdtype; 141 1.1 christos dns_rdataclass_t rdclass; 142 1.3 christos uint16_t udpsize; 143 1.3 christos int16_t edns; 144 1.1 christos dns_ednsopt_t *ednsopts; 145 1.1 christos unsigned int ednsoptscnt; 146 1.1 christos unsigned int ednsflags; 147 1.1 christos isc_sockaddr_t *ecs_addr; 148 1.1 christos unsigned int timeout; 149 1.1 christos unsigned int udptimeout; 150 1.1 christos unsigned int udpretries; 151 1.1 christos ISC_LINK(struct query) link; 152 1.1 christos }; 153 1.1 christos static struct query default_query; 154 1.1 christos static ISC_LIST(struct query) queries; 155 1.1 christos 156 1.1 christos #define EDNSOPTS 100U 157 1.1 christos /*% opcode text */ 158 1.6 christos static const char *const opcodetext[] = { 159 1.6 christos "QUERY", "IQUERY", "STATUS", "RESERVED3", 160 1.6 christos "NOTIFY", "UPDATE", "RESERVED6", "RESERVED7", 161 1.6 christos "RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11", 162 1.6 christos "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15" 163 1.1 christos }; 164 1.1 christos 165 1.1 christos /*% return code text */ 166 1.6 christos static const char *const rcodetext[] = { 167 1.6 christos "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", 168 1.6 christos "REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH", 169 1.6 christos "NOTZONE", "RESERVED11", "RESERVED12", "RESERVED13", "RESERVED14", 170 1.6 christos "RESERVED15", "BADVERS" 171 1.1 christos }; 172 1.1 christos 173 1.1 christos /*% safe rcodetext[] */ 174 1.1 christos static char * 175 1.6 christos rcode_totext(dns_rcode_t rcode) { 176 1.1 christos static char buf[sizeof("?65535")]; 177 1.1 christos union { 178 1.1 christos const char *consttext; 179 1.1 christos char *deconsttext; 180 1.1 christos } totext; 181 1.1 christos 182 1.6 christos if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) { 183 1.1 christos snprintf(buf, sizeof(buf), "?%u", rcode); 184 1.1 christos totext.deconsttext = buf; 185 1.6 christos } else { 186 1.1 christos totext.consttext = rcodetext[rcode]; 187 1.6 christos } 188 1.14 christos return totext.deconsttext; 189 1.1 christos } 190 1.1 christos 191 1.1 christos static void 192 1.14 christos recvresponse(void *arg) { 193 1.14 christos dns_request_t *request = (dns_request_t *)arg; 194 1.1 christos isc_result_t result; 195 1.1 christos dns_message_t *query = NULL, *response = NULL; 196 1.1 christos unsigned int parseflags = 0; 197 1.6 christos isc_buffer_t *msgbuf = NULL, *buf = NULL; 198 1.1 christos unsigned int len = OUTPUTBUF; 199 1.1 christos dns_master_style_t *style = NULL; 200 1.1 christos unsigned int styleflags = 0; 201 1.1 christos dns_messagetextflag_t flags; 202 1.1 christos 203 1.14 christos query = dns_request_getarg(request); 204 1.1 christos 205 1.14 christos result = dns_request_getresult(request); 206 1.14 christos if (result != ISC_R_SUCCESS) { 207 1.1 christos fprintf(stderr, "response failed with %s\n", 208 1.14 christos isc_result_totext(result)); 209 1.6 christos if (continue_on_error) { 210 1.1 christos goto cleanup; 211 1.6 christos } else { 212 1.13 christos exit(EXIT_FAILURE); 213 1.6 christos } 214 1.1 christos } 215 1.1 christos 216 1.14 christos dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, 217 1.14 christos &response); 218 1.1 christos 219 1.1 christos parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER; 220 1.1 christos if (besteffort) { 221 1.1 christos parseflags |= DNS_MESSAGEPARSE_BESTEFFORT; 222 1.1 christos parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION; 223 1.1 christos } 224 1.6 christos 225 1.14 christos msgbuf = dns_request_getanswer(request); 226 1.14 christos result = dns_request_getresponse(request, response, parseflags); 227 1.15 christos CHECKM("dns_request_getresponse", result); 228 1.1 christos 229 1.1 christos styleflags |= DNS_STYLEFLAG_REL_OWNER; 230 1.6 christos if (yaml) { 231 1.6 christos styleflags |= DNS_STYLEFLAG_YAML; 232 1.6 christos response->indent.string = " "; 233 1.6 christos response->indent.count = 3; 234 1.6 christos } else { 235 1.6 christos if (display_comments) { 236 1.6 christos styleflags |= DNS_STYLEFLAG_COMMENT; 237 1.6 christos } 238 1.6 christos if (display_unknown_format) { 239 1.6 christos styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; 240 1.6 christos } 241 1.6 christos if (display_rrcomments > 0) { 242 1.4 christos styleflags |= DNS_STYLEFLAG_RRCOMMENT; 243 1.4 christos } 244 1.6 christos if (display_ttlunits) { 245 1.6 christos styleflags |= DNS_STYLEFLAG_TTL_UNITS; 246 1.6 christos } 247 1.6 christos if (!display_ttl) { 248 1.6 christos styleflags |= DNS_STYLEFLAG_NO_TTL; 249 1.6 christos } 250 1.6 christos if (!display_class) { 251 1.6 christos styleflags |= DNS_STYLEFLAG_NO_CLASS; 252 1.6 christos } 253 1.6 christos if (!display_crypto) { 254 1.6 christos styleflags |= DNS_STYLEFLAG_NOCRYPTO; 255 1.6 christos } 256 1.6 christos if (display_multiline) { 257 1.6 christos styleflags |= DNS_STYLEFLAG_OMIT_OWNER; 258 1.6 christos styleflags |= DNS_STYLEFLAG_OMIT_CLASS; 259 1.6 christos styleflags |= DNS_STYLEFLAG_REL_DATA; 260 1.6 christos styleflags |= DNS_STYLEFLAG_OMIT_TTL; 261 1.6 christos styleflags |= DNS_STYLEFLAG_TTL; 262 1.6 christos styleflags |= DNS_STYLEFLAG_MULTILINE; 263 1.6 christos styleflags |= DNS_STYLEFLAG_COMMENT; 264 1.6 christos /* Turn on rrcomments unless explicitly disabled */ 265 1.6 christos if (display_rrcomments >= 0) { 266 1.6 christos styleflags |= DNS_STYLEFLAG_RRCOMMENT; 267 1.6 christos } 268 1.6 christos } 269 1.6 christos } 270 1.6 christos if (display_multiline || (!display_ttl && !display_class)) { 271 1.6 christos result = dns_master_stylecreate(&style, styleflags, 24, 24, 24, 272 1.6 christos 32, 80, 8, display_splitwidth, 273 1.6 christos mctx); 274 1.6 christos } else if (!display_ttl || !display_class) { 275 1.6 christos result = dns_master_stylecreate(&style, styleflags, 24, 24, 32, 276 1.6 christos 40, 80, 8, display_splitwidth, 277 1.6 christos mctx); 278 1.6 christos } else { 279 1.6 christos result = dns_master_stylecreate(&style, styleflags, 24, 32, 40, 280 1.6 christos 48, 80, 8, display_splitwidth, 281 1.6 christos mctx); 282 1.1 christos } 283 1.15 christos CHECKM("dns_master_stylecreate2", result); 284 1.1 christos 285 1.1 christos flags = 0; 286 1.1 christos if (!display_headers) { 287 1.1 christos flags |= DNS_MESSAGETEXTFLAG_NOHEADERS; 288 1.1 christos flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; 289 1.1 christos } 290 1.6 christos if (!display_comments) { 291 1.1 christos flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; 292 1.6 christos } 293 1.1 christos 294 1.6 christos isc_buffer_allocate(mctx, &buf, len); 295 1.1 christos 296 1.6 christos if (yaml) { 297 1.6 christos char sockstr[ISC_SOCKADDR_FORMATSIZE]; 298 1.6 christos uint16_t sport; 299 1.6 christos char *hash; 300 1.6 christos int pf; 301 1.6 christos 302 1.13 christos printf("- type: MESSAGE\n"); 303 1.6 christos printf(" message:\n"); 304 1.6 christos 305 1.6 christos if (((response->flags & DNS_MESSAGEFLAG_RD) != 0) && 306 1.6 christos ((response->flags & DNS_MESSAGEFLAG_RA) != 0)) 307 1.6 christos { 308 1.6 christos printf(" type: RECURSIVE_RESPONSE\n"); 309 1.6 christos } else { 310 1.6 christos printf(" type: AUTH_RESPONSE\n"); 311 1.6 christos } 312 1.6 christos 313 1.6 christos printf(" message_size: %ub\n", 314 1.6 christos isc_buffer_usedlength(msgbuf)); 315 1.6 christos 316 1.6 christos pf = isc_sockaddr_pf(&dstaddr); 317 1.6 christos if (pf == PF_INET || pf == PF_INET6) { 318 1.6 christos printf(" socket_family: %s\n", 319 1.6 christos pf == PF_INET ? "INET" : "INET6"); 320 1.6 christos 321 1.6 christos printf(" socket_protocol: %s\n", 322 1.6 christos tcp_mode ? "TCP" : "UDP"); 323 1.6 christos 324 1.6 christos sport = isc_sockaddr_getport(&dstaddr); 325 1.6 christos isc_sockaddr_format(&dstaddr, sockstr, sizeof(sockstr)); 326 1.6 christos hash = strchr(sockstr, '#'); 327 1.6 christos if (hash != NULL) { 328 1.6 christos *hash = '\0'; 329 1.6 christos } 330 1.10 christos printf(" response_address: \"%s\"\n", sockstr); 331 1.6 christos printf(" response_port: %u\n", sport); 332 1.6 christos } 333 1.6 christos 334 1.6 christos if (have_src) { 335 1.6 christos sport = isc_sockaddr_getport(&srcaddr); 336 1.6 christos isc_sockaddr_format(&srcaddr, sockstr, sizeof(sockstr)); 337 1.6 christos hash = strchr(sockstr, '#'); 338 1.6 christos if (hash != NULL) { 339 1.6 christos *hash = '\0'; 340 1.6 christos } 341 1.10 christos printf(" query_address: \"%s\"\n", sockstr); 342 1.6 christos printf(" query_port: %u\n", sport); 343 1.6 christos } 344 1.6 christos 345 1.6 christos printf(" %s:\n", "response_message_data"); 346 1.6 christos result = dns_message_headertotext(response, style, flags, buf); 347 1.15 christos CHECKM("dns_message_headertotext", result); 348 1.6 christos } else if (display_comments && !display_short_form) { 349 1.1 christos printf(";; Got answer:\n"); 350 1.1 christos 351 1.1 christos if (display_headers) { 352 1.1 christos printf(";; ->>HEADER<<- opcode: %s, status: %s, " 353 1.1 christos "id: %u\n", 354 1.1 christos opcodetext[response->opcode], 355 1.6 christos rcode_totext(response->rcode), response->id); 356 1.1 christos printf(";; flags:"); 357 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_QR) != 0) { 358 1.1 christos printf(" qr"); 359 1.6 christos } 360 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_AA) != 0) { 361 1.1 christos printf(" aa"); 362 1.6 christos } 363 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_TC) != 0) { 364 1.1 christos printf(" tc"); 365 1.6 christos } 366 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_RD) != 0) { 367 1.1 christos printf(" rd"); 368 1.6 christos } 369 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_RA) != 0) { 370 1.1 christos printf(" ra"); 371 1.6 christos } 372 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_AD) != 0) { 373 1.1 christos printf(" ad"); 374 1.6 christos } 375 1.6 christos if ((response->flags & DNS_MESSAGEFLAG_CD) != 0) { 376 1.1 christos printf(" cd"); 377 1.6 christos } 378 1.6 christos if ((response->flags & 0x0040U) != 0) { 379 1.1 christos printf("; MBZ: 0x4"); 380 1.6 christos } 381 1.1 christos 382 1.1 christos printf("; QUERY: %u, ANSWER: %u, " 383 1.1 christos "AUTHORITY: %u, ADDITIONAL: %u\n", 384 1.1 christos response->counts[DNS_SECTION_QUESTION], 385 1.1 christos response->counts[DNS_SECTION_ANSWER], 386 1.1 christos response->counts[DNS_SECTION_AUTHORITY], 387 1.1 christos response->counts[DNS_SECTION_ADDITIONAL]); 388 1.1 christos 389 1.1 christos if ((response->flags & DNS_MESSAGEFLAG_RD) != 0 && 390 1.1 christos (response->flags & DNS_MESSAGEFLAG_RA) == 0) 391 1.6 christos { 392 1.1 christos printf(";; WARNING: recursion requested " 393 1.1 christos "but not available\n"); 394 1.6 christos } 395 1.1 christos } 396 1.1 christos } 397 1.1 christos 398 1.1 christos repopulate_buffer: 399 1.1 christos 400 1.1 christos if (display_comments && display_headers && !display_short_form) { 401 1.6 christos result = dns_message_pseudosectiontotext( 402 1.6 christos response, DNS_PSEUDOSECTION_OPT, style, flags, buf); 403 1.1 christos if (result == ISC_R_NOSPACE) { 404 1.6 christos buftoosmall: 405 1.1 christos len += OUTPUTBUF; 406 1.1 christos isc_buffer_free(&buf); 407 1.6 christos isc_buffer_allocate(mctx, &buf, len); 408 1.6 christos goto repopulate_buffer; 409 1.1 christos } 410 1.15 christos CHECKM("dns_message_pseudosectiontotext", result); 411 1.1 christos } 412 1.1 christos 413 1.1 christos if (display_question && display_headers && !display_short_form) { 414 1.6 christos result = dns_message_sectiontotext( 415 1.6 christos response, DNS_SECTION_QUESTION, style, flags, buf); 416 1.6 christos if (result == ISC_R_NOSPACE) { 417 1.1 christos goto buftoosmall; 418 1.6 christos } 419 1.15 christos CHECKM("dns_message_sectiontotext", result); 420 1.1 christos } 421 1.1 christos 422 1.1 christos if (display_answer && !display_short_form) { 423 1.6 christos result = dns_message_sectiontotext(response, DNS_SECTION_ANSWER, 424 1.1 christos style, flags, buf); 425 1.6 christos if (result == ISC_R_NOSPACE) { 426 1.1 christos goto buftoosmall; 427 1.6 christos } 428 1.15 christos CHECKM("dns_message_sectiontotext", result); 429 1.1 christos } else if (display_answer) { 430 1.1 christos dns_name_t *name; 431 1.1 christos dns_rdataset_t *rdataset; 432 1.1 christos isc_result_t loopresult; 433 1.1 christos dns_name_t empty_name; 434 1.1 christos dns_rdata_t rdata = DNS_RDATA_INIT; 435 1.1 christos unsigned int answerstyleflags = 0; 436 1.1 christos 437 1.6 christos if (!display_crypto) { 438 1.1 christos answerstyleflags |= DNS_STYLEFLAG_NOCRYPTO; 439 1.6 christos } 440 1.6 christos if (display_unknown_format) { 441 1.1 christos answerstyleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; 442 1.6 christos } 443 1.1 christos 444 1.1 christos dns_name_init(&empty_name, NULL); 445 1.1 christos result = dns_message_firstname(response, DNS_SECTION_ANSWER); 446 1.6 christos if (result != ISC_R_NOMORE) { 447 1.15 christos CHECKM("dns_message_firstname", result); 448 1.6 christos } 449 1.1 christos 450 1.1 christos for (;;) { 451 1.6 christos if (result == ISC_R_NOMORE) { 452 1.1 christos break; 453 1.6 christos } 454 1.15 christos CHECKM("dns_message_nextname", result); 455 1.1 christos name = NULL; 456 1.6 christos dns_message_currentname(response, DNS_SECTION_ANSWER, 457 1.1 christos &name); 458 1.1 christos 459 1.1 christos for (rdataset = ISC_LIST_HEAD(name->list); 460 1.1 christos rdataset != NULL; 461 1.6 christos rdataset = ISC_LIST_NEXT(rdataset, link)) 462 1.6 christos { 463 1.1 christos loopresult = dns_rdataset_first(rdataset); 464 1.1 christos while (loopresult == ISC_R_SUCCESS) { 465 1.1 christos dns_rdataset_current(rdataset, &rdata); 466 1.1 christos result = dns_rdata_tofmttext( 467 1.6 christos &rdata, NULL, answerstyleflags, 468 1.6 christos 0, 60, " ", buf); 469 1.6 christos if (result == ISC_R_NOSPACE) { 470 1.1 christos goto buftoosmall; 471 1.6 christos } 472 1.15 christos CHECKM("dns_rdata_tofmttext", result); 473 1.1 christos loopresult = 474 1.6 christos dns_rdataset_next(rdataset); 475 1.1 christos dns_rdata_reset(&rdata); 476 1.1 christos if (strlen("\n") >= 477 1.11 christos isc_buffer_availablelength(buf)) 478 1.11 christos { 479 1.1 christos goto buftoosmall; 480 1.6 christos } 481 1.1 christos isc_buffer_putstr(buf, "\n"); 482 1.1 christos } 483 1.1 christos } 484 1.1 christos result = dns_message_nextname(response, 485 1.1 christos DNS_SECTION_ANSWER); 486 1.1 christos } 487 1.1 christos } 488 1.1 christos 489 1.1 christos if (display_authority && !display_short_form) { 490 1.6 christos result = dns_message_sectiontotext( 491 1.6 christos response, DNS_SECTION_AUTHORITY, style, flags, buf); 492 1.6 christos if (result == ISC_R_NOSPACE) { 493 1.1 christos goto buftoosmall; 494 1.6 christos } 495 1.15 christos CHECKM("dns_message_sectiontotext", result); 496 1.1 christos } 497 1.1 christos 498 1.1 christos if (display_additional && !display_short_form) { 499 1.6 christos result = dns_message_sectiontotext( 500 1.6 christos response, DNS_SECTION_ADDITIONAL, style, flags, buf); 501 1.6 christos if (result == ISC_R_NOSPACE) { 502 1.1 christos goto buftoosmall; 503 1.6 christos } 504 1.15 christos CHECKM("dns_message_sectiontotext", result); 505 1.1 christos } 506 1.1 christos 507 1.1 christos if (display_additional && !display_short_form && display_headers) { 508 1.1 christos /* 509 1.1 christos * Only print the signature on the first record. 510 1.1 christos */ 511 1.6 christos result = dns_message_pseudosectiontotext( 512 1.6 christos response, DNS_PSEUDOSECTION_TSIG, style, flags, buf); 513 1.6 christos if (result == ISC_R_NOSPACE) { 514 1.1 christos goto buftoosmall; 515 1.6 christos } 516 1.15 christos CHECKM("dns_message_pseudosectiontotext", result); 517 1.6 christos result = dns_message_pseudosectiontotext( 518 1.6 christos response, DNS_PSEUDOSECTION_SIG0, style, flags, buf); 519 1.6 christos if (result == ISC_R_NOSPACE) { 520 1.1 christos goto buftoosmall; 521 1.6 christos } 522 1.15 christos CHECKM("dns_message_pseudosectiontotext", result); 523 1.1 christos } 524 1.1 christos 525 1.6 christos if (display_headers && display_comments && !display_short_form && !yaml) 526 1.6 christos { 527 1.1 christos printf("\n"); 528 1.6 christos } 529 1.1 christos 530 1.1 christos printf("%.*s", (int)isc_buffer_usedlength(buf), 531 1.1 christos (char *)isc_buffer_base(buf)); 532 1.1 christos isc_buffer_free(&buf); 533 1.1 christos 534 1.1 christos cleanup: 535 1.1 christos fflush(stdout); 536 1.6 christos if (style != NULL) { 537 1.1 christos dns_master_styledestroy(&style, mctx); 538 1.6 christos } 539 1.6 christos if (query != NULL) { 540 1.7 christos dns_message_detach(&query); 541 1.6 christos } 542 1.6 christos if (response != NULL) { 543 1.7 christos dns_message_detach(&response); 544 1.6 christos } 545 1.14 christos dns_request_destroy(&request); 546 1.1 christos 547 1.6 christos if (--onfly == 0) { 548 1.14 christos isc_loopmgr_shutdown(loopmgr); 549 1.6 christos } 550 1.1 christos return; 551 1.1 christos } 552 1.1 christos 553 1.1 christos /*% 554 1.1 christos * Add EDNS0 option record to a message. Currently, the only supported 555 1.1 christos * options are UDP buffer size, the DO bit, and EDNS options 556 1.1 christos * (e.g., NSID, COOKIE, client-subnet) 557 1.1 christos */ 558 1.1 christos static void 559 1.6 christos add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags, 560 1.6 christos dns_ednsopt_t *opts, size_t count) { 561 1.1 christos dns_rdataset_t *rdataset = NULL; 562 1.1 christos isc_result_t result; 563 1.1 christos 564 1.1 christos result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, 565 1.1 christos opts, count); 566 1.15 christos CHECKM("dns_message_buildopt", result); 567 1.1 christos result = dns_message_setopt(msg, rdataset); 568 1.15 christos CHECKM("dns_message_setopt", result); 569 1.1 christos } 570 1.1 christos 571 1.1 christos static void 572 1.1 christos compute_cookie(unsigned char *cookie, size_t len) { 573 1.1 christos /* XXXMPA need to fix, should be per server. */ 574 1.1 christos INSIST(len >= 8U); 575 1.1 christos memmove(cookie, cookie_secret, 8); 576 1.1 christos } 577 1.1 christos 578 1.1 christos static isc_result_t 579 1.14 christos sendquery(struct query *query) { 580 1.9 christos dns_request_t *request = NULL; 581 1.9 christos dns_message_t *message = NULL; 582 1.9 christos dns_name_t *qname = NULL; 583 1.9 christos dns_rdataset_t *qrdataset = NULL; 584 1.1 christos isc_result_t result; 585 1.1 christos dns_fixedname_t queryname; 586 1.1 christos isc_buffer_t buf; 587 1.14 christos unsigned int options = 0; 588 1.1 christos 589 1.1 christos onfly++; 590 1.1 christos 591 1.1 christos dns_fixedname_init(&queryname); 592 1.1 christos isc_buffer_init(&buf, query->textname, strlen(query->textname)); 593 1.1 christos isc_buffer_add(&buf, strlen(query->textname)); 594 1.1 christos result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf, 595 1.1 christos dns_rootname, 0, NULL); 596 1.15 christos CHECKM("dns_name_fromtext", result); 597 1.1 christos 598 1.14 christos dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER, 599 1.14 christos &message); 600 1.1 christos 601 1.1 christos message->opcode = dns_opcode_query; 602 1.6 christos if (query->recurse) { 603 1.1 christos message->flags |= DNS_MESSAGEFLAG_RD; 604 1.6 christos } 605 1.6 christos if (query->have_aaonly) { 606 1.1 christos message->flags |= DNS_MESSAGEFLAG_AA; 607 1.6 christos } 608 1.6 christos if (query->have_adflag) { 609 1.1 christos message->flags |= DNS_MESSAGEFLAG_AD; 610 1.6 christos } 611 1.6 christos if (query->have_cdflag) { 612 1.1 christos message->flags |= DNS_MESSAGEFLAG_CD; 613 1.6 christos } 614 1.6 christos if (query->have_zflag) { 615 1.1 christos message->flags |= 0x0040U; 616 1.6 christos } 617 1.1 christos message->rdclass = query->rdclass; 618 1.1 christos message->id = (unsigned short)(random() & 0xFFFF); 619 1.1 christos 620 1.14 christos dns_message_gettempname(message, &qname); 621 1.1 christos 622 1.14 christos dns_message_gettemprdataset(message, &qrdataset); 623 1.1 christos 624 1.1 christos dns_name_clone(dns_fixedname_name(&queryname), qname); 625 1.6 christos dns_rdataset_makequestion(qrdataset, query->rdclass, query->rdtype); 626 1.1 christos ISC_LIST_APPEND(qname->list, qrdataset, link); 627 1.1 christos dns_message_addname(message, qname, DNS_SECTION_QUESTION); 628 1.1 christos 629 1.6 christos if (query->udpsize > 0 || query->dnssec || query->edns > -1 || 630 1.6 christos query->ecs_addr != NULL) 631 1.1 christos { 632 1.1 christos dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS]; 633 1.1 christos unsigned int flags; 634 1.1 christos int i = 0; 635 1.1 christos char ecsbuf[20]; 636 1.1 christos unsigned char cookie[40]; 637 1.1 christos 638 1.6 christos if (query->udpsize == 0) { 639 1.12 christos query->udpsize = 1232; 640 1.6 christos } 641 1.6 christos if (query->edns < 0) { 642 1.1 christos query->edns = 0; 643 1.6 christos } 644 1.1 christos 645 1.1 christos if (query->nsid) { 646 1.1 christos INSIST(i < DNS_EDNSOPTIONS); 647 1.1 christos opts[i].code = DNS_OPT_NSID; 648 1.1 christos opts[i].length = 0; 649 1.1 christos opts[i].value = NULL; 650 1.1 christos i++; 651 1.1 christos } 652 1.1 christos 653 1.1 christos if (query->ecs_addr != NULL) { 654 1.3 christos uint8_t addr[16], family; 655 1.3 christos uint32_t plen; 656 1.1 christos struct sockaddr *sa; 657 1.1 christos struct sockaddr_in *sin; 658 1.1 christos struct sockaddr_in6 *sin6; 659 1.1 christos size_t addrl; 660 1.1 christos isc_buffer_t b; 661 1.1 christos 662 1.1 christos sa = &query->ecs_addr->type.sa; 663 1.1 christos plen = query->ecs_addr->length; 664 1.1 christos 665 1.1 christos /* Round up prefix len to a multiple of 8 */ 666 1.1 christos addrl = (plen + 7) / 8; 667 1.1 christos 668 1.1 christos INSIST(i < DNS_EDNSOPTIONS); 669 1.1 christos opts[i].code = DNS_OPT_CLIENT_SUBNET; 670 1.6 christos opts[i].length = (uint16_t)addrl + 4; 671 1.15 christos CHECKM("isc_buffer_allocate", result); 672 1.1 christos isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); 673 1.1 christos if (sa->sa_family == AF_INET) { 674 1.1 christos family = 1; 675 1.6 christos sin = (struct sockaddr_in *)sa; 676 1.1 christos memmove(addr, &sin->sin_addr, 4); 677 1.6 christos if ((plen % 8) != 0) { 678 1.6 christos addr[addrl - 1] &= ~0U 679 1.6 christos << (8 - (plen % 8)); 680 1.6 christos } 681 1.1 christos } else { 682 1.1 christos family = 2; 683 1.6 christos sin6 = (struct sockaddr_in6 *)sa; 684 1.1 christos memmove(addr, &sin6->sin6_addr, 16); 685 1.1 christos } 686 1.1 christos 687 1.1 christos /* Mask off last address byte */ 688 1.6 christos if (addrl > 0 && (plen % 8) != 0) { 689 1.1 christos addr[addrl - 1] &= ~0U << (8 - (plen % 8)); 690 1.6 christos } 691 1.1 christos 692 1.1 christos /* family */ 693 1.1 christos isc_buffer_putuint16(&b, family); 694 1.1 christos /* source prefix-length */ 695 1.1 christos isc_buffer_putuint8(&b, plen); 696 1.1 christos /* scope prefix-length */ 697 1.1 christos isc_buffer_putuint8(&b, 0); 698 1.1 christos /* address */ 699 1.6 christos if (addrl > 0) { 700 1.14 christos isc_buffer_putmem(&b, addr, 701 1.14 christos (unsigned int)addrl); 702 1.6 christos } 703 1.1 christos 704 1.6 christos opts[i].value = (uint8_t *)ecsbuf; 705 1.1 christos i++; 706 1.1 christos } 707 1.1 christos 708 1.1 christos if (query->send_cookie) { 709 1.1 christos INSIST(i < DNS_EDNSOPTIONS); 710 1.1 christos opts[i].code = DNS_OPT_COOKIE; 711 1.1 christos if (query->cookie != NULL) { 712 1.1 christos isc_buffer_t b; 713 1.1 christos 714 1.1 christos isc_buffer_init(&b, cookie, sizeof(cookie)); 715 1.1 christos result = isc_hex_decodestring(query->cookie, 716 1.1 christos &b); 717 1.15 christos CHECKM("isc_hex_decodestring", result); 718 1.1 christos opts[i].value = isc_buffer_base(&b); 719 1.1 christos opts[i].length = isc_buffer_usedlength(&b); 720 1.1 christos } else { 721 1.1 christos compute_cookie(cookie, 8); 722 1.1 christos opts[i].length = 8; 723 1.1 christos opts[i].value = cookie; 724 1.1 christos } 725 1.1 christos i++; 726 1.1 christos } 727 1.1 christos 728 1.1 christos if (query->expire) { 729 1.1 christos INSIST(i < DNS_EDNSOPTIONS); 730 1.1 christos opts[i].code = DNS_OPT_EXPIRE; 731 1.1 christos opts[i].length = 0; 732 1.1 christos opts[i].value = NULL; 733 1.1 christos i++; 734 1.1 christos } 735 1.1 christos 736 1.1 christos if (query->ednsoptscnt != 0) { 737 1.1 christos memmove(&opts[i], query->ednsopts, 738 1.1 christos sizeof(dns_ednsopt_t) * query->ednsoptscnt); 739 1.1 christos i += query->ednsoptscnt; 740 1.1 christos } 741 1.1 christos 742 1.1 christos flags = query->ednsflags; 743 1.1 christos flags &= ~DNS_MESSAGEEXTFLAG_DO; 744 1.6 christos if (query->dnssec) { 745 1.1 christos flags |= DNS_MESSAGEEXTFLAG_DO; 746 1.6 christos } 747 1.1 christos add_opt(message, query->udpsize, query->edns, flags, opts, i); 748 1.1 christos } 749 1.1 christos 750 1.6 christos if (tcp_mode) { 751 1.12 christos options |= DNS_REQUESTOPT_TCP; 752 1.6 christos } 753 1.12 christos 754 1.12 christos result = dns_request_create( 755 1.14 christos requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, NULL, 756 1.14 christos NULL, options, NULL, query->timeout, query->udptimeout, 757 1.14 christos query->udpretries, isc_loop_main(loopmgr), recvresponse, 758 1.14 christos message, &request); 759 1.15 christos CHECKM("dns_request_create", result); 760 1.1 christos 761 1.14 christos return ISC_R_SUCCESS; 762 1.1 christos } 763 1.1 christos 764 1.1 christos static void 765 1.14 christos sendqueries(void *arg) { 766 1.14 christos struct query *query = (struct query *)arg; 767 1.1 christos 768 1.1 christos while (query != NULL) { 769 1.1 christos struct query *next = ISC_LIST_NEXT(query, link); 770 1.1 christos 771 1.14 christos sendquery(query); 772 1.1 christos query = next; 773 1.1 christos } 774 1.1 christos 775 1.6 christos if (onfly == 0) { 776 1.14 christos isc_loopmgr_shutdown(loopmgr); 777 1.6 christos } 778 1.1 christos } 779 1.1 christos 780 1.12 christos noreturn static void 781 1.12 christos usage(void); 782 1.1 christos 783 1.1 christos static void 784 1.1 christos usage(void) { 785 1.12 christos fprintf(stderr, "Usage: mdig @server {global-opt} host\n" 786 1.12 christos " {local-opt} [ host {local-opt} [...]]\n" 787 1.12 christos "\nUse \"mdig -h\" (or \"mdig -h | more\") " 788 1.12 christos "for complete list of options\n"); 789 1.13 christos exit(EXIT_FAILURE); 790 1.1 christos } 791 1.1 christos 792 1.1 christos /*% help */ 793 1.1 christos static void 794 1.1 christos help(void) { 795 1.12 christos printf("Usage: mdig @server {global-opt} host\n" 796 1.12 christos " {local-opt} [ host {local-opt} [...]]\n" 797 1.12 christos "Where:\n" 798 1.12 christos " anywhere opt is one of:\n" 799 1.12 christos " -f filename (batch mode)\n" 800 1.12 christos " -h (print help and exit)\n" 801 1.12 christos " -v (print version and exit)\n" 802 1.12 christos " global opt is one of:\n" 803 1.12 christos " -4 (use IPv4 query transport " 804 1.12 christos "only)\n" 805 1.12 christos " -6 (use IPv6 query transport " 806 1.12 christos "only)\n" 807 1.12 christos " -b address[#port] (bind to source " 808 1.12 christos "address/port)\n" 809 1.12 christos " -p port (specify port number)\n" 810 1.12 christos " -m (enable memory usage " 811 1.12 christos "debugging)\n" 812 1.12 christos " +[no]vc (TCP mode)\n" 813 1.12 christos " +[no]tcp (TCP mode, alternate " 814 1.12 christos "syntax)\n" 815 1.12 christos " +[no]besteffort (Try to parse even " 816 1.12 christos "illegal " 817 1.12 christos "messages)\n" 818 1.12 christos " +[no]cl (Control display of class " 819 1.12 christos "in records)\n" 820 1.12 christos " +[no]comments (Control display of " 821 1.12 christos "comment lines)\n" 822 1.12 christos " +[no]rrcomments (Control display of " 823 1.12 christos "per-record " 824 1.12 christos "comments)\n" 825 1.12 christos " +[no]crypto (Control display of " 826 1.12 christos "cryptographic " 827 1.12 christos "fields in records)\n" 828 1.12 christos " +[no]question (Control display of " 829 1.12 christos "question)\n" 830 1.12 christos " +[no]answer (Control display of " 831 1.12 christos "answer)\n" 832 1.12 christos " +[no]authority (Control display of " 833 1.12 christos "authority)\n" 834 1.12 christos " +[no]additional (Control display of " 835 1.12 christos "additional)\n" 836 1.12 christos " +[no]short (Disable everything " 837 1.12 christos "except " 838 1.12 christos "short\n" 839 1.12 christos " form of answer)\n" 840 1.12 christos " +[no]ttlid (Control display of ttls " 841 1.12 christos "in records)\n" 842 1.12 christos " +[no]ttlunits (Display TTLs in " 843 1.12 christos "human-readable units)\n" 844 1.12 christos " +[no]unknownformat (Print RDATA in RFC 3597 " 845 1.12 christos "\"unknown\" format)\n" 846 1.12 christos " +[no]all (Set or clear all display " 847 1.12 christos "flags)\n" 848 1.12 christos " +[no]multiline (Print records in an " 849 1.12 christos "expanded format)\n" 850 1.12 christos " +[no]split=## (Split hex/base64 fields " 851 1.12 christos "into chunks)\n" 852 1.12 christos " local opt is one of:\n" 853 1.12 christos " -c class (specify query class)\n" 854 1.12 christos " -t type (specify query type)\n" 855 1.12 christos " -x dot-notation (shortcut for reverse " 856 1.12 christos "lookups)\n" 857 1.12 christos " +timeout=### (Set query timeout) " 858 1.12 christos "[UDP=5,TCP=10]\n" 859 1.12 christos " +udptimeout=### (Set timeout before UDP " 860 1.12 christos "retry)\n" 861 1.12 christos " +tries=### (Set number of UDP " 862 1.12 christos "attempts) [3]\n" 863 1.12 christos " +retry=### (Set number of UDP " 864 1.12 christos "retries) [2]\n" 865 1.12 christos " +bufsize=### (Set EDNS0 Max UDP packet " 866 1.12 christos "size)\n" 867 1.12 christos " +subnet=addr (Set edns-client-subnet " 868 1.12 christos "option)\n" 869 1.12 christos " +[no]edns[=###] (Set EDNS version) [0]\n" 870 1.12 christos " +ednsflags=### (Set EDNS flag bits)\n" 871 1.12 christos " +ednsopt=###[:value] (Send specified EDNS " 872 1.12 christos "option)\n" 873 1.12 christos " +noednsopt (Clear list of +ednsopt " 874 1.12 christos "options)\n" 875 1.12 christos " +[no]recurse (Recursive mode)\n" 876 1.12 christos " +[no]aaonly (Set AA flag in query " 877 1.12 christos "(+[no]aaflag))\n" 878 1.12 christos " +[no]adflag (Set AD flag in query)\n" 879 1.12 christos " +[no]cdflag (Set CD flag in query)\n" 880 1.12 christos " +[no]zflag (Set Z flag in query)\n" 881 1.12 christos " +[no]dnssec (Request DNSSEC records)\n" 882 1.12 christos " +[no]expire (Request time to expire)\n" 883 1.12 christos " +[no]cookie[=###] (Send a COOKIE option)\n" 884 1.12 christos " +[no]nsid (Request Name " 885 1.12 christos "Server ID)\n"); 886 1.12 christos } 887 1.12 christos 888 1.12 christos noreturn static void 889 1.12 christos fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); 890 1.1 christos 891 1.1 christos static void 892 1.1 christos fatal(const char *format, ...) { 893 1.1 christos va_list args; 894 1.1 christos 895 1.1 christos fflush(stdout); 896 1.1 christos fprintf(stderr, "mdig: "); 897 1.1 christos va_start(args, format); 898 1.1 christos vfprintf(stderr, format, args); 899 1.1 christos va_end(args); 900 1.1 christos fprintf(stderr, "\n"); 901 1.13 christos _exit(EXIT_FAILURE); 902 1.1 christos } 903 1.1 christos 904 1.1 christos static isc_result_t 905 1.3 christos parse_uint_helper(uint32_t *uip, const char *value, uint32_t max, 906 1.1 christos const char *desc, int base) { 907 1.3 christos uint32_t n; 908 1.1 christos isc_result_t result = isc_parse_uint32(&n, value, base); 909 1.6 christos if (result == ISC_R_SUCCESS && n > max) { 910 1.1 christos result = ISC_R_RANGE; 911 1.6 christos } 912 1.1 christos if (result != ISC_R_SUCCESS) { 913 1.6 christos printf("invalid %s '%s': %s\n", desc, value, 914 1.6 christos isc_result_totext(result)); 915 1.14 christos return result; 916 1.1 christos } 917 1.1 christos *uip = n; 918 1.14 christos return ISC_R_SUCCESS; 919 1.1 christos } 920 1.1 christos 921 1.1 christos static isc_result_t 922 1.6 christos parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { 923 1.14 christos return parse_uint_helper(uip, value, max, desc, 10); 924 1.1 christos } 925 1.1 christos 926 1.1 christos static isc_result_t 927 1.6 christos parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { 928 1.14 christos return parse_uint_helper(uip, value, max, desc, 0); 929 1.1 christos } 930 1.1 christos 931 1.3 christos static void 932 1.3 christos newopts(struct query *query) { 933 1.3 christos size_t len = sizeof(query->ednsopts[0]) * EDNSOPTS; 934 1.3 christos size_t i; 935 1.3 christos 936 1.3 christos query->ednsopts = isc_mem_allocate(mctx, len); 937 1.3 christos 938 1.3 christos for (i = 0; i < EDNSOPTS; i++) { 939 1.3 christos query->ednsopts[i].code = 0; 940 1.3 christos query->ednsopts[i].length = 0; 941 1.3 christos query->ednsopts[i].value = NULL; 942 1.3 christos } 943 1.3 christos } 944 1.1 christos 945 1.1 christos static void 946 1.1 christos save_opt(struct query *query, char *code, char *value) { 947 1.3 christos uint32_t num; 948 1.1 christos isc_buffer_t b; 949 1.1 christos isc_result_t result; 950 1.1 christos 951 1.3 christos if (query->ednsopts == NULL) { 952 1.3 christos newopts(query); 953 1.3 christos } 954 1.3 christos 955 1.3 christos if (query->ednsoptscnt == EDNSOPTS) { 956 1.1 christos fatal("too many ednsopts"); 957 1.3 christos } 958 1.1 christos 959 1.1 christos result = parse_uint(&num, code, 65535, "ednsopt"); 960 1.3 christos if (result != ISC_R_SUCCESS) { 961 1.1 christos fatal("bad edns code point: %s", code); 962 1.3 christos } 963 1.1 christos 964 1.3 christos query->ednsopts[query->ednsoptscnt].code = num; 965 1.3 christos query->ednsopts[query->ednsoptscnt].length = 0; 966 1.3 christos query->ednsopts[query->ednsoptscnt].value = NULL; 967 1.1 christos 968 1.1 christos if (value != NULL) { 969 1.1 christos char *buf; 970 1.6 christos buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1); 971 1.6 christos isc_buffer_init(&b, buf, strlen(value) / 2 + 1); 972 1.1 christos result = isc_hex_decodestring(value, &b); 973 1.15 christos CHECKM("isc_hex_decodestring", result); 974 1.6 christos query->ednsopts[query->ednsoptscnt].value = isc_buffer_base(&b); 975 1.3 christos query->ednsopts[query->ednsoptscnt].length = 976 1.6 christos isc_buffer_usedlength(&b); 977 1.1 christos } 978 1.1 christos 979 1.1 christos query->ednsoptscnt++; 980 1.1 christos } 981 1.1 christos 982 1.1 christos static isc_result_t 983 1.1 christos parse_netprefix(isc_sockaddr_t **sap, const char *value) { 984 1.1 christos isc_sockaddr_t *sa = NULL; 985 1.1 christos struct in_addr in4; 986 1.1 christos struct in6_addr in6; 987 1.3 christos uint32_t netmask = 0xffffffff; 988 1.1 christos char *slash = NULL; 989 1.3 christos bool parsed = false; 990 1.1 christos char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")]; 991 1.1 christos 992 1.6 christos if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) { 993 1.1 christos fatal("invalid prefix '%s'\n", value); 994 1.6 christos } 995 1.1 christos 996 1.1 christos slash = strchr(buf, '/'); 997 1.1 christos if (slash != NULL) { 998 1.5 christos isc_result_t result; 999 1.1 christos *slash = '\0'; 1000 1.1 christos result = isc_parse_uint32(&netmask, slash + 1, 10); 1001 1.1 christos if (result != ISC_R_SUCCESS) { 1002 1.6 christos fatal("invalid prefix length in '%s': %s\n", value, 1003 1.6 christos isc_result_totext(result)); 1004 1.1 christos } 1005 1.1 christos } else if (strcmp(value, "0") == 0) { 1006 1.1 christos netmask = 0; 1007 1.1 christos } 1008 1.1 christos 1009 1.1 christos sa = isc_mem_allocate(mctx, sizeof(*sa)); 1010 1.1 christos if (inet_pton(AF_INET6, buf, &in6) == 1) { 1011 1.3 christos parsed = true; 1012 1.1 christos isc_sockaddr_fromin6(sa, &in6, 0); 1013 1.6 christos if (netmask > 128) { 1014 1.1 christos netmask = 128; 1015 1.6 christos } 1016 1.1 christos } else if (inet_pton(AF_INET, buf, &in4) == 1) { 1017 1.3 christos parsed = true; 1018 1.1 christos isc_sockaddr_fromin(sa, &in4, 0); 1019 1.6 christos if (netmask > 32) { 1020 1.1 christos netmask = 32; 1021 1.6 christos } 1022 1.1 christos } else if (netmask != 0xffffffff) { 1023 1.1 christos int i; 1024 1.1 christos 1025 1.1 christos for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) { 1026 1.1 christos strlcat(buf, ".0", sizeof(buf)); 1027 1.1 christos if (inet_pton(AF_INET, buf, &in4) == 1) { 1028 1.3 christos parsed = true; 1029 1.1 christos isc_sockaddr_fromin(sa, &in4, 0); 1030 1.1 christos break; 1031 1.1 christos } 1032 1.1 christos } 1033 1.1 christos 1034 1.6 christos if (netmask > 32) { 1035 1.1 christos netmask = 32; 1036 1.6 christos } 1037 1.1 christos } 1038 1.1 christos 1039 1.6 christos if (!parsed) { 1040 1.1 christos fatal("invalid address '%s'", value); 1041 1.6 christos } 1042 1.1 christos 1043 1.1 christos sa->length = netmask; 1044 1.1 christos *sap = sa; 1045 1.1 christos 1046 1.14 christos return ISC_R_SUCCESS; 1047 1.1 christos } 1048 1.1 christos 1049 1.1 christos /*% 1050 1.1 christos * Append 'len' bytes of 'text' at '*p', failing with 1051 1.1 christos * ISC_R_NOSPACE if that would advance p past 'end'. 1052 1.1 christos */ 1053 1.1 christos static isc_result_t 1054 1.1 christos append(const char *text, int len, char **p, char *end) { 1055 1.6 christos if (len > end - *p) { 1056 1.14 christos return ISC_R_NOSPACE; 1057 1.6 christos } 1058 1.1 christos memmove(*p, text, len); 1059 1.1 christos *p += len; 1060 1.14 christos return ISC_R_SUCCESS; 1061 1.1 christos } 1062 1.1 christos 1063 1.1 christos static isc_result_t 1064 1.1 christos reverse_octets(const char *in, char **p, char *end) { 1065 1.1 christos const char *dot = strchr(in, '.'); 1066 1.1 christos int len; 1067 1.1 christos if (dot != NULL) { 1068 1.1 christos isc_result_t result; 1069 1.1 christos result = reverse_octets(dot + 1, p, end); 1070 1.15 christos CHECKM("reverse_octets", result); 1071 1.1 christos result = append(".", 1, p, end); 1072 1.15 christos CHECKM("append", result); 1073 1.1 christos len = (int)(dot - in); 1074 1.1 christos } else { 1075 1.1 christos len = strlen(in); 1076 1.1 christos } 1077 1.14 christos return append(in, len, p, end); 1078 1.1 christos } 1079 1.1 christos 1080 1.1 christos static void 1081 1.6 christos get_reverse(char *reverse, size_t len, const char *value) { 1082 1.1 christos int r; 1083 1.1 christos isc_result_t result; 1084 1.1 christos isc_netaddr_t addr; 1085 1.1 christos 1086 1.1 christos addr.family = AF_INET6; 1087 1.1 christos r = inet_pton(AF_INET6, value, &addr.type.in6); 1088 1.1 christos if (r > 0) { 1089 1.1 christos /* This is a valid IPv6 address. */ 1090 1.1 christos dns_fixedname_t fname; 1091 1.1 christos dns_name_t *name; 1092 1.1 christos 1093 1.1 christos name = dns_fixedname_initname(&fname); 1094 1.14 christos result = dns_byaddr_createptrname(&addr, name); 1095 1.15 christos CHECKM("dns_byaddr_createptrname", result); 1096 1.1 christos dns_name_format(name, reverse, (unsigned int)len); 1097 1.1 christos return; 1098 1.1 christos } else { 1099 1.1 christos /* 1100 1.1 christos * Not a valid IPv6 address. Assume IPv4. 1101 1.1 christos * Construct the in-addr.arpa name by blindly 1102 1.1 christos * reversing octets whether or not they look like 1103 1.1 christos * integers, so that this can be used for RFC2317 1104 1.1 christos * names and such. 1105 1.1 christos */ 1106 1.1 christos char *p = reverse; 1107 1.1 christos char *end = reverse + len; 1108 1.1 christos result = reverse_octets(value, &p, end); 1109 1.15 christos CHECKM("reverse_octets", result); 1110 1.1 christos /* Append .in-addr.arpa. and a terminating NUL. */ 1111 1.1 christos result = append(".in-addr.arpa.", 15, &p, end); 1112 1.15 christos CHECKM("append", result); 1113 1.1 christos return; 1114 1.1 christos } 1115 1.1 christos } 1116 1.1 christos 1117 1.1 christos /*% 1118 1.1 christos * We're not using isc_commandline_parse() here since the command line 1119 1.1 christos * syntax of mdig is quite a bit different from that which can be described 1120 1.1 christos * by that routine. 1121 1.1 christos * XXX doc options 1122 1.1 christos */ 1123 1.1 christos 1124 1.1 christos static void 1125 1.6 christos plus_option(char *option, struct query *query, bool global) { 1126 1.1 christos isc_result_t result; 1127 1.3 christos char *cmd, *value, *last = NULL, *code; 1128 1.3 christos uint32_t num; 1129 1.3 christos bool state = true; 1130 1.1 christos size_t n; 1131 1.1 christos 1132 1.3 christos INSIST(option != NULL); 1133 1.3 christos 1134 1.3 christos if ((cmd = strtok_r(option, "=", &last)) == NULL) { 1135 1.3 christos printf(";; Invalid option %s\n", option); 1136 1.1 christos return; 1137 1.1 christos } 1138 1.1 christos if (strncasecmp(cmd, "no", 2) == 0) { 1139 1.1 christos cmd += 2; 1140 1.3 christos state = false; 1141 1.1 christos } 1142 1.3 christos /* parse the rest of the string */ 1143 1.3 christos value = strtok_r(NULL, "", &last); 1144 1.1 christos 1145 1.6 christos #define FULLCHECK(A) \ 1146 1.6 christos do { \ 1147 1.6 christos size_t _l = strlen(cmd); \ 1148 1.1 christos if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \ 1149 1.6 christos goto invalid_option; \ 1150 1.1 christos } while (0) 1151 1.6 christos #define FULLCHECK2(A, B) \ 1152 1.6 christos do { \ 1153 1.6 christos size_t _l = strlen(cmd); \ 1154 1.1 christos if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ 1155 1.6 christos (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \ 1156 1.6 christos goto invalid_option; \ 1157 1.1 christos } while (0) 1158 1.6 christos #define GLOBAL() \ 1159 1.6 christos do { \ 1160 1.6 christos if (!global) \ 1161 1.1 christos goto global_option; \ 1162 1.1 christos } while (0) 1163 1.1 christos 1164 1.1 christos switch (cmd[0]) { 1165 1.1 christos case 'a': 1166 1.1 christos switch (cmd[1]) { 1167 1.1 christos case 'a': /* aaonly / aaflag */ 1168 1.1 christos FULLCHECK2("aaonly", "aaflag"); 1169 1.1 christos query->have_aaonly = state; 1170 1.1 christos break; 1171 1.1 christos case 'd': 1172 1.1 christos switch (cmd[2]) { 1173 1.1 christos case 'd': /* additional */ 1174 1.1 christos FULLCHECK("additional"); 1175 1.1 christos display_additional = state; 1176 1.1 christos break; 1177 1.6 christos case 'f': /* adflag */ 1178 1.1 christos case '\0': /* +ad is a synonym for +adflag */ 1179 1.1 christos FULLCHECK("adflag"); 1180 1.1 christos query->have_adflag = state; 1181 1.1 christos break; 1182 1.1 christos default: 1183 1.1 christos goto invalid_option; 1184 1.1 christos } 1185 1.1 christos break; 1186 1.1 christos case 'l': /* all */ 1187 1.1 christos FULLCHECK("all"); 1188 1.1 christos GLOBAL(); 1189 1.1 christos display_question = state; 1190 1.1 christos display_answer = state; 1191 1.1 christos display_authority = state; 1192 1.1 christos display_additional = state; 1193 1.1 christos display_comments = state; 1194 1.4 christos display_rrcomments = state ? 1 : -1; 1195 1.1 christos break; 1196 1.1 christos case 'n': /* answer */ 1197 1.1 christos FULLCHECK("answer"); 1198 1.1 christos GLOBAL(); 1199 1.1 christos display_answer = state; 1200 1.1 christos break; 1201 1.1 christos case 'u': /* authority */ 1202 1.1 christos FULLCHECK("authority"); 1203 1.1 christos GLOBAL(); 1204 1.1 christos display_authority = state; 1205 1.1 christos break; 1206 1.1 christos default: 1207 1.1 christos goto invalid_option; 1208 1.1 christos } 1209 1.1 christos break; 1210 1.1 christos case 'b': 1211 1.1 christos switch (cmd[1]) { 1212 1.6 christos case 'e': /* besteffort */ 1213 1.1 christos FULLCHECK("besteffort"); 1214 1.1 christos GLOBAL(); 1215 1.1 christos besteffort = state; 1216 1.1 christos break; 1217 1.7 christos case 'u': 1218 1.7 christos switch (cmd[2]) { 1219 1.7 christos case 'f': /* bufsize */ 1220 1.7 christos FULLCHECK("bufsize"); 1221 1.7 christos if (value == NULL) { 1222 1.7 christos goto need_value; 1223 1.7 christos } 1224 1.7 christos if (!state) { 1225 1.7 christos goto invalid_option; 1226 1.7 christos } 1227 1.7 christos result = parse_uint(&num, value, COMMSIZE, 1228 1.7 christos "buffer size"); 1229 1.15 christos CHECKM("parse_uint(buffer size)", result); 1230 1.7 christos query->udpsize = num; 1231 1.7 christos break; 1232 1.7 christos case 'r': /* burst */ 1233 1.7 christos FULLCHECK("burst"); 1234 1.7 christos GLOBAL(); 1235 1.7 christos burst = state; 1236 1.7 christos break; 1237 1.7 christos default: 1238 1.1 christos goto invalid_option; 1239 1.6 christos } 1240 1.1 christos break; 1241 1.1 christos default: 1242 1.1 christos goto invalid_option; 1243 1.1 christos } 1244 1.1 christos break; 1245 1.1 christos case 'c': 1246 1.1 christos switch (cmd[1]) { 1247 1.6 christos case 'd': /* cdflag */ 1248 1.1 christos switch (cmd[2]) { 1249 1.6 christos case 'f': /* cdflag */ 1250 1.1 christos case '\0': /* +cd is a synonym for +cdflag */ 1251 1.1 christos FULLCHECK("cdflag"); 1252 1.1 christos query->have_cdflag = state; 1253 1.1 christos break; 1254 1.1 christos default: 1255 1.1 christos goto invalid_option; 1256 1.1 christos } 1257 1.1 christos break; 1258 1.1 christos case 'l': /* cl */ 1259 1.1 christos FULLCHECK("cl"); 1260 1.1 christos GLOBAL(); 1261 1.1 christos display_class = state; 1262 1.1 christos break; 1263 1.1 christos case 'o': /* comments */ 1264 1.1 christos switch (cmd[2]) { 1265 1.1 christos case 'm': 1266 1.1 christos FULLCHECK("comments"); 1267 1.1 christos GLOBAL(); 1268 1.1 christos display_comments = state; 1269 1.1 christos break; 1270 1.1 christos case 'n': 1271 1.1 christos FULLCHECK("continue"); 1272 1.1 christos GLOBAL(); 1273 1.1 christos continue_on_error = state; 1274 1.1 christos break; 1275 1.1 christos case 'o': 1276 1.1 christos FULLCHECK("cookie"); 1277 1.6 christos if (state && query->edns == -1) { 1278 1.1 christos query->edns = 0; 1279 1.6 christos } 1280 1.1 christos query->send_cookie = state; 1281 1.1 christos if (value != NULL) { 1282 1.1 christos n = strlcpy(hexcookie, value, 1283 1.1 christos sizeof(hexcookie)); 1284 1.6 christos if (n >= sizeof(hexcookie)) { 1285 1.1 christos fatal("COOKIE data too large"); 1286 1.6 christos } 1287 1.1 christos query->cookie = hexcookie; 1288 1.6 christos } else { 1289 1.1 christos query->cookie = NULL; 1290 1.6 christos } 1291 1.1 christos break; 1292 1.1 christos default: 1293 1.1 christos goto invalid_option; 1294 1.1 christos } 1295 1.1 christos break; 1296 1.1 christos case 'r': 1297 1.1 christos FULLCHECK("crypto"); 1298 1.1 christos GLOBAL(); 1299 1.1 christos display_crypto = state; 1300 1.1 christos break; 1301 1.1 christos default: 1302 1.1 christos goto invalid_option; 1303 1.1 christos } 1304 1.1 christos break; 1305 1.1 christos case 'd': 1306 1.1 christos switch (cmd[1]) { 1307 1.1 christos case 'n': /* dnssec */ 1308 1.1 christos FULLCHECK("dnssec"); 1309 1.6 christos if (state && query->edns == -1) { 1310 1.1 christos query->edns = 0; 1311 1.6 christos } 1312 1.1 christos query->dnssec = state; 1313 1.1 christos break; 1314 1.1 christos default: 1315 1.1 christos goto invalid_option; 1316 1.1 christos } 1317 1.1 christos break; 1318 1.1 christos case 'e': 1319 1.1 christos switch (cmd[1]) { 1320 1.1 christos case 'd': 1321 1.6 christos switch (cmd[2]) { 1322 1.1 christos case 'n': 1323 1.1 christos switch (cmd[3]) { 1324 1.1 christos case 's': 1325 1.1 christos switch (cmd[4]) { 1326 1.1 christos case 0: 1327 1.1 christos FULLCHECK("edns"); 1328 1.1 christos if (!state) { 1329 1.1 christos query->edns = -1; 1330 1.1 christos break; 1331 1.1 christos } 1332 1.1 christos if (value == NULL) { 1333 1.1 christos query->edns = 0; 1334 1.1 christos break; 1335 1.1 christos } 1336 1.6 christos result = parse_uint(&num, value, 1337 1.1 christos 255, 1338 1.1 christos "edns"); 1339 1.15 christos CHECKM("parse_uint(edns)", 1340 1.15 christos result); 1341 1.1 christos query->edns = num; 1342 1.1 christos break; 1343 1.1 christos case 'f': 1344 1.1 christos FULLCHECK("ednsflags"); 1345 1.1 christos if (!state) { 1346 1.1 christos query->ednsflags = 0; 1347 1.1 christos break; 1348 1.1 christos } 1349 1.1 christos if (value == NULL) { 1350 1.1 christos query->ednsflags = 0; 1351 1.1 christos break; 1352 1.1 christos } 1353 1.6 christos result = parse_xint( 1354 1.6 christos &num, value, 0xffff, 1355 1.6 christos "ednsflags"); 1356 1.15 christos CHECKM("parse_xint(ednsflags)", 1357 1.15 christos result); 1358 1.13 christos if (query->edns == -1) { 1359 1.13 christos query->edns = 1; 1360 1.13 christos } 1361 1.1 christos query->ednsflags = num; 1362 1.1 christos break; 1363 1.1 christos case 'o': 1364 1.1 christos FULLCHECK("ednsopt"); 1365 1.1 christos if (!state) { 1366 1.1 christos query->ednsoptscnt = 0; 1367 1.1 christos break; 1368 1.1 christos } 1369 1.3 christos code = NULL; 1370 1.3 christos if (value != NULL) { 1371 1.3 christos code = strtok_r(value, 1372 1.3 christos ":", 1373 1.3 christos &last); 1374 1.3 christos } 1375 1.3 christos if (code == NULL) { 1376 1.1 christos fatal("ednsopt no " 1377 1.1 christos "code point " 1378 1.1 christos "specified"); 1379 1.3 christos } 1380 1.3 christos value = strtok_r(NULL, "\0", 1381 1.3 christos &last); 1382 1.1 christos save_opt(query, code, value); 1383 1.1 christos break; 1384 1.1 christos default: 1385 1.1 christos goto invalid_option; 1386 1.1 christos } 1387 1.1 christos break; 1388 1.1 christos default: 1389 1.1 christos goto invalid_option; 1390 1.1 christos } 1391 1.1 christos break; 1392 1.1 christos default: 1393 1.1 christos goto invalid_option; 1394 1.1 christos } 1395 1.1 christos break; 1396 1.1 christos case 'x': 1397 1.1 christos FULLCHECK("expire"); 1398 1.1 christos query->expire = state; 1399 1.1 christos break; 1400 1.1 christos default: 1401 1.1 christos goto invalid_option; 1402 1.1 christos } 1403 1.1 christos break; 1404 1.1 christos case 'm': /* multiline */ 1405 1.1 christos FULLCHECK("multiline"); 1406 1.1 christos GLOBAL(); 1407 1.1 christos display_multiline = state; 1408 1.1 christos break; 1409 1.1 christos case 'n': 1410 1.1 christos FULLCHECK("nsid"); 1411 1.6 christos if (state && query->edns == -1) { 1412 1.1 christos query->edns = 0; 1413 1.6 christos } 1414 1.1 christos query->nsid = state; 1415 1.1 christos break; 1416 1.1 christos case 'q': 1417 1.1 christos FULLCHECK("question"); 1418 1.1 christos GLOBAL(); 1419 1.1 christos display_question = state; 1420 1.1 christos break; 1421 1.1 christos case 'r': 1422 1.1 christos switch (cmd[1]) { 1423 1.1 christos case 'e': 1424 1.1 christos switch (cmd[2]) { 1425 1.1 christos case 'c': /* recurse */ 1426 1.1 christos FULLCHECK("recurse"); 1427 1.1 christos query->recurse = state; 1428 1.1 christos break; 1429 1.1 christos case 't': /* retry / retries */ 1430 1.1 christos FULLCHECK2("retry", "retries"); 1431 1.6 christos if (value == NULL) { 1432 1.1 christos goto need_value; 1433 1.6 christos } 1434 1.6 christos if (!state) { 1435 1.1 christos goto invalid_option; 1436 1.6 christos } 1437 1.6 christos result = parse_uint(&query->udpretries, value, 1438 1.6 christos MAXTRIES - 1, "udpretries"); 1439 1.15 christos CHECKM("parse_uint(udpretries)", result); 1440 1.1 christos break; 1441 1.1 christos default: 1442 1.1 christos goto invalid_option; 1443 1.1 christos } 1444 1.1 christos break; 1445 1.1 christos case 'r': 1446 1.1 christos FULLCHECK("rrcomments"); 1447 1.1 christos GLOBAL(); 1448 1.4 christos display_rrcomments = state ? 1 : -1; 1449 1.1 christos break; 1450 1.1 christos default: 1451 1.1 christos goto invalid_option; 1452 1.1 christos } 1453 1.1 christos break; 1454 1.1 christos case 's': 1455 1.1 christos switch (cmd[1]) { 1456 1.1 christos case 'h': 1457 1.1 christos FULLCHECK("short"); 1458 1.1 christos GLOBAL(); 1459 1.1 christos display_short_form = state; 1460 1.1 christos if (state) { 1461 1.3 christos display_question = false; 1462 1.3 christos display_answer = true; 1463 1.3 christos display_authority = false; 1464 1.3 christos display_additional = false; 1465 1.3 christos display_comments = false; 1466 1.4 christos display_rrcomments = -1; 1467 1.1 christos } 1468 1.1 christos break; 1469 1.1 christos case 'p': /* split */ 1470 1.1 christos FULLCHECK("split"); 1471 1.1 christos GLOBAL(); 1472 1.6 christos if (value != NULL && !state) { 1473 1.1 christos goto invalid_option; 1474 1.6 christos } 1475 1.1 christos if (!state) { 1476 1.1 christos display_splitwidth = 0; 1477 1.1 christos break; 1478 1.6 christos } else if (value == NULL) { 1479 1.1 christos break; 1480 1.6 christos } 1481 1.1 christos 1482 1.6 christos result = parse_uint(&display_splitwidth, value, 1023, 1483 1.6 christos "split"); 1484 1.1 christos if ((display_splitwidth % 4) != 0) { 1485 1.1 christos display_splitwidth = 1486 1.6 christos ((display_splitwidth + 3) / 4) * 4; 1487 1.6 christos fprintf(stderr, 1488 1.6 christos ";; Warning, split must be " 1489 1.6 christos "a multiple of 4; adjusting " 1490 1.6 christos "to %u\n", 1491 1.1 christos display_splitwidth); 1492 1.1 christos } 1493 1.1 christos /* 1494 1.1 christos * There is an adjustment done in the 1495 1.1 christos * totext_<rrtype>() functions which causes 1496 1.1 christos * splitwidth to shrink. This is okay when we're 1497 1.1 christos * using the default width but incorrect in this 1498 1.1 christos * case, so we correct for it 1499 1.1 christos */ 1500 1.6 christos if (display_splitwidth) { 1501 1.1 christos display_splitwidth += 3; 1502 1.6 christos } 1503 1.15 christos CHECKM("parse_uint(split)", result); 1504 1.1 christos break; 1505 1.1 christos case 'u': /* subnet */ 1506 1.1 christos FULLCHECK("subnet"); 1507 1.6 christos if (state && value == NULL) { 1508 1.1 christos goto need_value; 1509 1.6 christos } 1510 1.1 christos if (!state) { 1511 1.1 christos if (query->ecs_addr != NULL) { 1512 1.1 christos isc_mem_free(mctx, query->ecs_addr); 1513 1.1 christos query->ecs_addr = NULL; 1514 1.1 christos } 1515 1.1 christos break; 1516 1.1 christos } 1517 1.6 christos if (query->edns == -1) { 1518 1.1 christos query->edns = 0; 1519 1.6 christos } 1520 1.1 christos result = parse_netprefix(&query->ecs_addr, value); 1521 1.15 christos CHECKM("parse_netprefix", result); 1522 1.1 christos break; 1523 1.1 christos default: 1524 1.1 christos goto invalid_option; 1525 1.1 christos } 1526 1.1 christos break; 1527 1.1 christos case 't': 1528 1.1 christos switch (cmd[1]) { 1529 1.1 christos case 'c': /* tcp */ 1530 1.1 christos FULLCHECK("tcp"); 1531 1.1 christos GLOBAL(); 1532 1.1 christos tcp_mode = state; 1533 1.1 christos break; 1534 1.1 christos case 'i': /* timeout */ 1535 1.1 christos FULLCHECK("timeout"); 1536 1.6 christos if (value == NULL) { 1537 1.1 christos goto need_value; 1538 1.6 christos } 1539 1.6 christos if (!state) { 1540 1.1 christos goto invalid_option; 1541 1.6 christos } 1542 1.6 christos result = parse_uint(&query->timeout, value, MAXTIMEOUT, 1543 1.6 christos "timeout"); 1544 1.15 christos CHECKM("parse_uint(timeout)", result); 1545 1.6 christos if (query->timeout == 0) { 1546 1.1 christos query->timeout = 1; 1547 1.6 christos } 1548 1.1 christos break; 1549 1.1 christos case 'r': 1550 1.1 christos FULLCHECK("tries"); 1551 1.6 christos if (value == NULL) { 1552 1.1 christos goto need_value; 1553 1.6 christos } 1554 1.6 christos if (!state) { 1555 1.1 christos goto invalid_option; 1556 1.6 christos } 1557 1.6 christos result = parse_uint(&query->udpretries, value, MAXTRIES, 1558 1.6 christos "udpretries"); 1559 1.15 christos CHECKM("parse_uint(udpretries)", result); 1560 1.12 christos if (query->udpretries > 0) { 1561 1.12 christos query->udpretries -= 1; 1562 1.6 christos } 1563 1.1 christos break; 1564 1.1 christos case 't': 1565 1.1 christos switch (cmd[2]) { 1566 1.1 christos case 'l': 1567 1.1 christos switch (cmd[3]) { 1568 1.1 christos case 0: 1569 1.1 christos case 'i': /* ttlid */ 1570 1.1 christos FULLCHECK2("ttl", "ttlid"); 1571 1.1 christos GLOBAL(); 1572 1.1 christos display_ttl = state; 1573 1.1 christos break; 1574 1.1 christos case 'u': /* ttlunits */ 1575 1.1 christos FULLCHECK("ttlunits"); 1576 1.1 christos GLOBAL(); 1577 1.3 christos display_ttl = true; 1578 1.1 christos display_ttlunits = state; 1579 1.1 christos break; 1580 1.1 christos default: 1581 1.1 christos goto invalid_option; 1582 1.1 christos } 1583 1.1 christos break; 1584 1.1 christos default: 1585 1.1 christos goto invalid_option; 1586 1.1 christos } 1587 1.1 christos break; 1588 1.1 christos default: 1589 1.1 christos goto invalid_option; 1590 1.1 christos } 1591 1.1 christos break; 1592 1.1 christos case 'u': 1593 1.1 christos switch (cmd[1]) { 1594 1.1 christos case 'd': 1595 1.1 christos FULLCHECK("udptimeout"); 1596 1.6 christos if (value == NULL) { 1597 1.1 christos goto need_value; 1598 1.6 christos } 1599 1.6 christos if (!state) { 1600 1.1 christos goto invalid_option; 1601 1.6 christos } 1602 1.1 christos result = parse_uint(&query->udptimeout, value, 1603 1.1 christos MAXTIMEOUT, "udptimeout"); 1604 1.15 christos CHECKM("parse_uint(udptimeout)", result); 1605 1.1 christos break; 1606 1.1 christos case 'n': 1607 1.1 christos FULLCHECK("unknownformat"); 1608 1.1 christos display_unknown_format = state; 1609 1.1 christos break; 1610 1.1 christos default: 1611 1.1 christos goto invalid_option; 1612 1.1 christos } 1613 1.1 christos break; 1614 1.1 christos case 'v': 1615 1.1 christos FULLCHECK("vc"); 1616 1.1 christos GLOBAL(); 1617 1.1 christos tcp_mode = state; 1618 1.1 christos break; 1619 1.6 christos case 'y': /* yaml */ 1620 1.6 christos FULLCHECK("yaml"); 1621 1.6 christos yaml = state; 1622 1.6 christos if (state) { 1623 1.15 christos display_rrcomments = 1; 1624 1.6 christos } 1625 1.6 christos break; 1626 1.1 christos case 'z': /* zflag */ 1627 1.1 christos FULLCHECK("zflag"); 1628 1.1 christos query->have_zflag = state; 1629 1.1 christos break; 1630 1.1 christos global_option: 1631 1.1 christos fprintf(stderr, "Ignored late global option: +%s\n", option); 1632 1.1 christos break; 1633 1.1 christos default: 1634 1.1 christos invalid_option: 1635 1.1 christos need_value: 1636 1.1 christos fprintf(stderr, "Invalid option: +%s\n", option); 1637 1.1 christos usage(); 1638 1.1 christos } 1639 1.1 christos return; 1640 1.1 christos } 1641 1.1 christos 1642 1.1 christos /*% 1643 1.3 christos * #true returned if value was used 1644 1.1 christos */ 1645 1.1 christos static const char *single_dash_opts = "46himv"; 1646 1.3 christos static const char *dash_opts = "46bcfhiptvx"; 1647 1.3 christos static bool 1648 1.6 christos dash_option(const char *option, char *next, struct query *query, bool global, 1649 1.6 christos bool *setname) { 1650 1.1 christos char opt; 1651 1.1 christos const char *value; 1652 1.1 christos isc_result_t result; 1653 1.3 christos bool value_from_next; 1654 1.1 christos isc_consttextregion_t tr; 1655 1.1 christos dns_rdatatype_t rdtype; 1656 1.1 christos dns_rdataclass_t rdclass; 1657 1.1 christos char textname[MXNAME]; 1658 1.1 christos struct in_addr in4; 1659 1.1 christos struct in6_addr in6; 1660 1.1 christos in_port_t srcport; 1661 1.1 christos char *hash; 1662 1.3 christos uint32_t num; 1663 1.1 christos 1664 1.1 christos while (strpbrk(option, single_dash_opts) == &option[0]) { 1665 1.1 christos /* 1666 1.1 christos * Since the -[46hiv] options do not take an argument, 1667 1.1 christos * account for them (in any number and/or combination) 1668 1.1 christos * if they appear as the first character(s) of an opt. 1669 1.1 christos */ 1670 1.1 christos opt = option[0]; 1671 1.1 christos switch (opt) { 1672 1.1 christos case '4': 1673 1.1 christos GLOBAL(); 1674 1.1 christos if (have_ipv4) { 1675 1.1 christos isc_net_disableipv6(); 1676 1.3 christos have_ipv6 = false; 1677 1.1 christos } else { 1678 1.1 christos fatal("can't find IPv4 networking"); 1679 1.10 christos UNREACHABLE(); 1680 1.14 christos return false; 1681 1.1 christos } 1682 1.1 christos break; 1683 1.1 christos case '6': 1684 1.1 christos GLOBAL(); 1685 1.1 christos if (have_ipv6) { 1686 1.1 christos isc_net_disableipv4(); 1687 1.3 christos have_ipv4 = false; 1688 1.1 christos } else { 1689 1.1 christos fatal("can't find IPv6 networking"); 1690 1.10 christos UNREACHABLE(); 1691 1.14 christos return false; 1692 1.1 christos } 1693 1.1 christos break; 1694 1.1 christos case 'h': 1695 1.1 christos help(); 1696 1.13 christos exit(EXIT_SUCCESS); 1697 1.1 christos break; 1698 1.1 christos case 'i': 1699 1.14 christos fatal("-%c removed", opt); 1700 1.1 christos case 'm': 1701 1.1 christos /* 1702 1.1 christos * handled by preparse_args() 1703 1.1 christos */ 1704 1.1 christos break; 1705 1.1 christos case 'v': 1706 1.14 christos printf("mDiG %s\n", PACKAGE_VERSION); 1707 1.13 christos exit(EXIT_SUCCESS); 1708 1.1 christos break; 1709 1.1 christos } 1710 1.6 christos if (strlen(option) > 1U) { 1711 1.1 christos option = &option[1]; 1712 1.6 christos } else { 1713 1.14 christos return false; 1714 1.6 christos } 1715 1.1 christos } 1716 1.1 christos opt = option[0]; 1717 1.1 christos if (strlen(option) > 1U) { 1718 1.3 christos value_from_next = false; 1719 1.1 christos value = &option[1]; 1720 1.1 christos } else { 1721 1.3 christos value_from_next = true; 1722 1.1 christos value = next; 1723 1.1 christos } 1724 1.6 christos if (value == NULL) { 1725 1.1 christos goto invalid_option; 1726 1.6 christos } 1727 1.1 christos switch (opt) { 1728 1.1 christos case 'b': 1729 1.1 christos GLOBAL(); 1730 1.1 christos hash = strchr(value, '#'); 1731 1.1 christos if (hash != NULL) { 1732 1.1 christos result = parse_uint(&num, hash + 1, MAXPORT, 1733 1.1 christos "port number"); 1734 1.15 christos CHECKM("parse_uint(srcport)", result); 1735 1.1 christos srcport = num; 1736 1.1 christos *hash = '\0'; 1737 1.6 christos } else { 1738 1.1 christos srcport = 0; 1739 1.6 christos } 1740 1.1 christos if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) { 1741 1.1 christos isc_sockaddr_fromin6(&srcaddr, &in6, srcport); 1742 1.1 christos isc_net_disableipv4(); 1743 1.1 christos } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) { 1744 1.1 christos isc_sockaddr_fromin(&srcaddr, &in4, srcport); 1745 1.1 christos isc_net_disableipv6(); 1746 1.1 christos } else { 1747 1.6 christos if (hash != NULL) { 1748 1.1 christos *hash = '#'; 1749 1.6 christos } 1750 1.1 christos fatal("invalid address %s", value); 1751 1.1 christos } 1752 1.6 christos if (hash != NULL) { 1753 1.1 christos *hash = '#'; 1754 1.6 christos } 1755 1.3 christos have_src = true; 1756 1.14 christos return value_from_next; 1757 1.1 christos case 'c': 1758 1.1 christos tr.base = value; 1759 1.1 christos tr.length = strlen(value); 1760 1.1 christos result = dns_rdataclass_fromtext(&rdclass, 1761 1.1 christos (isc_textregion_t *)&tr); 1762 1.15 christos CHECKM("dns_rdataclass_fromtext", result); 1763 1.1 christos query->rdclass = rdclass; 1764 1.14 christos return value_from_next; 1765 1.1 christos case 'f': 1766 1.1 christos batchname = value; 1767 1.14 christos return value_from_next; 1768 1.1 christos case 'p': 1769 1.1 christos GLOBAL(); 1770 1.1 christos result = parse_uint(&num, value, MAXPORT, "port number"); 1771 1.15 christos CHECKM("parse_uint(port)", result); 1772 1.1 christos port = num; 1773 1.14 christos return value_from_next; 1774 1.1 christos case 't': 1775 1.1 christos tr.base = value; 1776 1.1 christos tr.length = strlen(value); 1777 1.1 christos result = dns_rdatatype_fromtext(&rdtype, 1778 1.1 christos (isc_textregion_t *)&tr); 1779 1.15 christos CHECKM("dns_rdatatype_fromtext", result); 1780 1.1 christos query->rdtype = rdtype; 1781 1.14 christos return value_from_next; 1782 1.1 christos case 'x': 1783 1.3 christos get_reverse(textname, sizeof(textname), value); 1784 1.1 christos strlcpy(query->textname, textname, sizeof(query->textname)); 1785 1.1 christos query->rdtype = dns_rdatatype_ptr; 1786 1.1 christos query->rdclass = dns_rdataclass_in; 1787 1.3 christos *setname = true; 1788 1.14 christos return value_from_next; 1789 1.1 christos global_option: 1790 1.1 christos fprintf(stderr, "Ignored late global option: -%s\n", option); 1791 1.1 christos usage(); 1792 1.1 christos default: 1793 1.1 christos invalid_option: 1794 1.1 christos fprintf(stderr, "Invalid option: -%s\n", option); 1795 1.1 christos usage(); 1796 1.1 christos } 1797 1.10 christos UNREACHABLE(); 1798 1.14 christos return false; 1799 1.1 christos } 1800 1.1 christos 1801 1.1 christos static struct query * 1802 1.12 christos clone_default_query(void) { 1803 1.1 christos struct query *query; 1804 1.1 christos 1805 1.1 christos query = isc_mem_allocate(mctx, sizeof(struct query)); 1806 1.1 christos memmove(query, &default_query, sizeof(struct query)); 1807 1.1 christos if (default_query.ecs_addr != NULL) { 1808 1.1 christos size_t len = sizeof(isc_sockaddr_t); 1809 1.1 christos 1810 1.1 christos query->ecs_addr = isc_mem_allocate(mctx, len); 1811 1.1 christos memmove(query->ecs_addr, default_query.ecs_addr, len); 1812 1.1 christos } 1813 1.1 christos 1814 1.6 christos if (query->timeout == 0) { 1815 1.1 christos query->timeout = tcp_mode ? TCPTIMEOUT : UDPTIMEOUT; 1816 1.6 christos } 1817 1.1 christos 1818 1.14 christos return query; 1819 1.1 christos } 1820 1.1 christos 1821 1.1 christos /*% 1822 1.1 christos * Because we may be trying to do memory allocation recording, we're going 1823 1.1 christos * to need to parse the arguments for the -m *before* we start the main 1824 1.1 christos * argument parsing routine. 1825 1.1 christos * 1826 1.1 christos * I'd prefer not to have to do this, but I am not quite sure how else to 1827 1.1 christos * fix the problem. Argument parsing in mdig involves memory allocation 1828 1.1 christos * by its nature, so it can't be done in the main argument parser. 1829 1.1 christos */ 1830 1.1 christos static void 1831 1.1 christos preparse_args(int argc, char **argv) { 1832 1.1 christos int rc; 1833 1.1 christos char **rv; 1834 1.1 christos char *option; 1835 1.3 christos bool ipv4only = false, ipv6only = false; 1836 1.1 christos 1837 1.1 christos rc = argc; 1838 1.1 christos rv = argv; 1839 1.1 christos for (rc--, rv++; rc > 0; rc--, rv++) { 1840 1.6 christos if (rv[0][0] != '-') { 1841 1.1 christos continue; 1842 1.6 christos } 1843 1.1 christos option = &rv[0][1]; 1844 1.1 christos while (strpbrk(option, single_dash_opts) == &option[0]) { 1845 1.1 christos switch (option[0]) { 1846 1.1 christos case 'm': 1847 1.1 christos isc_mem_debugging = ISC_MEM_DEBUGTRACE | 1848 1.6 christos ISC_MEM_DEBUGRECORD; 1849 1.1 christos break; 1850 1.1 christos case '4': 1851 1.1 christos if (ipv6only) { 1852 1.1 christos fatal("only one of -4 and -6 allowed"); 1853 1.1 christos } 1854 1.3 christos ipv4only = true; 1855 1.1 christos break; 1856 1.1 christos case '6': 1857 1.1 christos if (ipv4only) { 1858 1.1 christos fatal("only one of -4 and -6 allowed"); 1859 1.1 christos } 1860 1.3 christos ipv6only = true; 1861 1.1 christos break; 1862 1.1 christos } 1863 1.1 christos option = &option[1]; 1864 1.1 christos } 1865 1.3 christos if (strlen(option) == 0U) { 1866 1.3 christos continue; 1867 1.3 christos } 1868 1.3 christos /* Look for dash value option. */ 1869 1.3 christos if (strpbrk(option, dash_opts) != &option[0] || 1870 1.11 christos strlen(option) > 1U) 1871 1.11 christos { 1872 1.3 christos /* Error or value in option. */ 1873 1.3 christos continue; 1874 1.3 christos } 1875 1.3 christos /* Dash value is next argument so we need to skip it. */ 1876 1.3 christos rc--, rv++; 1877 1.3 christos /* Handle missing argument */ 1878 1.6 christos if (rc == 0) { 1879 1.3 christos break; 1880 1.6 christos } 1881 1.1 christos } 1882 1.1 christos } 1883 1.1 christos 1884 1.1 christos static void 1885 1.6 christos parse_args(bool is_batchfile, int argc, char **argv) { 1886 1.1 christos struct query *query = NULL; 1887 1.1 christos char batchline[MXNAME]; 1888 1.1 christos int bargc; 1889 1.1 christos char *bargv[64]; 1890 1.1 christos int rc; 1891 1.1 christos char **rv; 1892 1.3 christos bool global = true; 1893 1.3 christos char *last; 1894 1.1 christos 1895 1.1 christos /* 1896 1.1 christos * The semantics for parsing the args is a bit complex; if 1897 1.1 christos * we don't have a host yet, make the arg apply globally, 1898 1.1 christos * otherwise make it apply to the latest host. This is 1899 1.1 christos * a bit different than the previous versions, but should 1900 1.1 christos * form a consistent user interface. 1901 1.1 christos * 1902 1.1 christos * First, create a "default query" which won't actually be used 1903 1.1 christos * anywhere, except for cloning into new queries 1904 1.1 christos */ 1905 1.1 christos 1906 1.1 christos if (!is_batchfile) { 1907 1.1 christos default_query.textname[0] = 0; 1908 1.3 christos default_query.recurse = true; 1909 1.3 christos default_query.have_aaonly = false; 1910 1.3 christos default_query.have_adflag = true; /*XXX*/ 1911 1.3 christos default_query.have_cdflag = false; 1912 1.3 christos default_query.have_zflag = false; 1913 1.3 christos default_query.dnssec = false; 1914 1.3 christos default_query.expire = false; 1915 1.3 christos default_query.send_cookie = false; 1916 1.1 christos default_query.cookie = NULL; 1917 1.3 christos default_query.nsid = false; 1918 1.1 christos default_query.rdtype = dns_rdatatype_a; 1919 1.1 christos default_query.rdclass = dns_rdataclass_in; 1920 1.1 christos default_query.udpsize = 0; 1921 1.1 christos default_query.edns = 0; /*XXX*/ 1922 1.1 christos default_query.ednsopts = NULL; 1923 1.1 christos default_query.ednsoptscnt = 0; 1924 1.1 christos default_query.ednsflags = 0; 1925 1.1 christos default_query.ecs_addr = NULL; 1926 1.1 christos default_query.timeout = 0; 1927 1.1 christos default_query.udptimeout = 0; 1928 1.12 christos default_query.udpretries = 2; 1929 1.1 christos ISC_LINK_INIT(&default_query, link); 1930 1.1 christos } 1931 1.1 christos 1932 1.1 christos if (is_batchfile) { 1933 1.1 christos /* Processing '-f batchfile'. */ 1934 1.1 christos query = clone_default_query(); 1935 1.3 christos global = false; 1936 1.6 christos } else { 1937 1.1 christos query = &default_query; 1938 1.6 christos } 1939 1.1 christos 1940 1.1 christos rc = argc; 1941 1.1 christos rv = argv; 1942 1.1 christos for (rc--, rv++; rc > 0; rc--, rv++) { 1943 1.6 christos if (strncmp(rv[0], "%", 1) == 0) { 1944 1.1 christos break; 1945 1.6 christos } 1946 1.1 christos if (rv[0][0] == '@') { 1947 1.6 christos if (server != NULL) { 1948 1.1 christos fatal("server already set to @%s", server); 1949 1.6 christos } 1950 1.1 christos server = &rv[0][1]; 1951 1.1 christos } else if (rv[0][0] == '+') { 1952 1.1 christos plus_option(&rv[0][1], query, global); 1953 1.1 christos } else if (rv[0][0] == '-') { 1954 1.3 christos bool setname = false; 1955 1.1 christos 1956 1.1 christos if (rc <= 1) { 1957 1.6 christos if (dash_option(&rv[0][1], NULL, query, global, 1958 1.11 christos &setname)) 1959 1.11 christos { 1960 1.1 christos rc--; 1961 1.1 christos rv++; 1962 1.1 christos } 1963 1.1 christos } else { 1964 1.6 christos if (dash_option(&rv[0][1], rv[1], query, global, 1965 1.11 christos &setname)) 1966 1.11 christos { 1967 1.1 christos rc--; 1968 1.1 christos rv++; 1969 1.1 christos } 1970 1.1 christos } 1971 1.1 christos if (setname) { 1972 1.6 christos if (query == &default_query) { 1973 1.1 christos query = clone_default_query(); 1974 1.6 christos } 1975 1.1 christos ISC_LIST_APPEND(queries, query, link); 1976 1.1 christos 1977 1.1 christos default_query.textname[0] = 0; 1978 1.1 christos query = clone_default_query(); 1979 1.3 christos global = false; 1980 1.1 christos } 1981 1.1 christos } else { 1982 1.1 christos /* 1983 1.1 christos * Anything which isn't an option 1984 1.1 christos */ 1985 1.6 christos if (query == &default_query) { 1986 1.1 christos query = clone_default_query(); 1987 1.6 christos } 1988 1.1 christos strlcpy(query->textname, rv[0], 1989 1.1 christos sizeof(query->textname)); 1990 1.1 christos ISC_LIST_APPEND(queries, query, link); 1991 1.1 christos 1992 1.1 christos query = clone_default_query(); 1993 1.3 christos global = false; 1994 1.1 christos /* XXX Error message */ 1995 1.1 christos } 1996 1.1 christos } 1997 1.1 christos 1998 1.1 christos /* 1999 1.1 christos * If we have a batchfile, read the query list from it. 2000 1.1 christos */ 2001 1.1 christos if ((batchname != NULL) && !is_batchfile) { 2002 1.6 christos if (strcmp(batchname, "-") == 0) { 2003 1.1 christos batchfp = stdin; 2004 1.6 christos } else { 2005 1.1 christos batchfp = fopen(batchname, "r"); 2006 1.6 christos } 2007 1.1 christos if (batchfp == NULL) { 2008 1.1 christos perror(batchname); 2009 1.1 christos fatal("couldn't open batch file '%s'", batchname); 2010 1.1 christos } 2011 1.1 christos while (fgets(batchline, sizeof(batchline), batchfp) != 0) { 2012 1.6 christos if (batchline[0] == '\r' || batchline[0] == '\n' || 2013 1.6 christos batchline[0] == '#' || batchline[0] == ';') 2014 1.6 christos { 2015 1.1 christos continue; 2016 1.6 christos } 2017 1.6 christos for (bargc = 1, bargv[bargc] = strtok_r( 2018 1.6 christos batchline, " \t\r\n", &last); 2019 1.6 christos (bargc < 14) && bargv[bargc]; bargc++, 2020 1.6 christos bargv[bargc] = strtok_r(NULL, " \t\r\n", &last)) 2021 1.3 christos { 2022 1.3 christos /* empty body */ 2023 1.1 christos } 2024 1.1 christos 2025 1.1 christos bargv[0] = argv[0]; 2026 1.3 christos parse_args(true, bargc, (char **)bargv); 2027 1.1 christos } 2028 1.6 christos if (batchfp != stdin) { 2029 1.1 christos fclose(batchfp); 2030 1.6 christos } 2031 1.1 christos } 2032 1.1 christos if (query != &default_query) { 2033 1.6 christos if (query->ecs_addr != NULL) { 2034 1.1 christos isc_mem_free(mctx, query->ecs_addr); 2035 1.6 christos } 2036 1.1 christos isc_mem_free(mctx, query); 2037 1.1 christos } 2038 1.1 christos } 2039 1.1 christos 2040 1.10 christos /* 2041 1.10 christos * Try honoring the operating system's preferred ephemeral port range. 2042 1.10 christos */ 2043 1.10 christos static void 2044 1.10 christos set_source_ports(dns_dispatchmgr_t *manager) { 2045 1.10 christos isc_portset_t *v4portset = NULL, *v6portset = NULL; 2046 1.10 christos in_port_t udpport_low, udpport_high; 2047 1.10 christos isc_result_t result; 2048 1.10 christos 2049 1.10 christos result = isc_portset_create(mctx, &v4portset); 2050 1.10 christos if (result != ISC_R_SUCCESS) { 2051 1.10 christos fatal("isc_portset_create (v4) failed"); 2052 1.10 christos } 2053 1.10 christos 2054 1.10 christos result = isc_net_getudpportrange(AF_INET, &udpport_low, &udpport_high); 2055 1.10 christos if (result != ISC_R_SUCCESS) { 2056 1.10 christos fatal("isc_net_getudpportrange (v4) failed"); 2057 1.10 christos } 2058 1.10 christos 2059 1.10 christos isc_portset_addrange(v4portset, udpport_low, udpport_high); 2060 1.10 christos 2061 1.10 christos result = isc_portset_create(mctx, &v6portset); 2062 1.10 christos if (result != ISC_R_SUCCESS) { 2063 1.10 christos fatal("isc_portset_create (v6) failed"); 2064 1.10 christos } 2065 1.10 christos result = isc_net_getudpportrange(AF_INET6, &udpport_low, &udpport_high); 2066 1.10 christos if (result != ISC_R_SUCCESS) { 2067 1.10 christos fatal("isc_net_getudpportrange (v6) failed"); 2068 1.10 christos } 2069 1.10 christos 2070 1.10 christos isc_portset_addrange(v6portset, udpport_low, udpport_high); 2071 1.10 christos 2072 1.10 christos result = dns_dispatchmgr_setavailports(manager, v4portset, v6portset); 2073 1.10 christos if (result != ISC_R_SUCCESS) { 2074 1.10 christos fatal("dns_dispatchmgr_setavailports failed"); 2075 1.10 christos } 2076 1.10 christos 2077 1.10 christos isc_portset_destroy(mctx, &v4portset); 2078 1.10 christos isc_portset_destroy(mctx, &v6portset); 2079 1.10 christos } 2080 1.10 christos 2081 1.14 christos static void 2082 1.14 christos teardown(void *arg ISC_ATTR_UNUSED) { 2083 1.14 christos dns_view_detach(&view); 2084 1.14 christos dns_requestmgr_shutdown(requestmgr); 2085 1.14 christos dns_requestmgr_detach(&requestmgr); 2086 1.14 christos dns_dispatch_detach(&dispatchvx); 2087 1.14 christos dns_dispatchmgr_detach(&dispatchmgr); 2088 1.14 christos } 2089 1.14 christos 2090 1.14 christos static void 2091 1.14 christos setup(void *arg ISC_ATTR_UNUSED) { 2092 1.14 christos RUNCHECK(dns_dispatchmgr_create(mctx, loopmgr, netmgr, &dispatchmgr)); 2093 1.14 christos 2094 1.14 christos set_source_ports(dispatchmgr); 2095 1.14 christos 2096 1.14 christos if (have_ipv4) { 2097 1.14 christos isc_sockaddr_any(&bind_any); 2098 1.14 christos } else { 2099 1.14 christos isc_sockaddr_any6(&bind_any); 2100 1.14 christos } 2101 1.14 christos RUNCHECK(dns_dispatch_createudp( 2102 1.14 christos dispatchmgr, have_src ? &srcaddr : &bind_any, &dispatchvx)); 2103 1.14 christos 2104 1.14 christos RUNCHECK(dns_requestmgr_create( 2105 1.14 christos mctx, loopmgr, dispatchmgr, have_ipv4 ? dispatchvx : NULL, 2106 1.14 christos have_ipv6 ? dispatchvx : NULL, &requestmgr)); 2107 1.14 christos 2108 1.14 christos RUNCHECK(dns_view_create(mctx, loopmgr, NULL, 0, "_mdig", &view)); 2109 1.14 christos } 2110 1.14 christos 2111 1.1 christos /*% Main processing routine for mdig */ 2112 1.1 christos int 2113 1.1 christos main(int argc, char *argv[]) { 2114 1.9 christos struct query *query = NULL; 2115 1.1 christos isc_result_t result; 2116 1.9 christos isc_log_t *lctx = NULL; 2117 1.9 christos isc_logconfig_t *lcfg = NULL; 2118 1.12 christos unsigned int i; 2119 1.1 christos int ns; 2120 1.1 christos 2121 1.6 christos if (isc_net_probeipv4() == ISC_R_SUCCESS) { 2122 1.3 christos have_ipv4 = true; 2123 1.6 christos } 2124 1.6 christos if (isc_net_probeipv6() == ISC_R_SUCCESS) { 2125 1.3 christos have_ipv6 = true; 2126 1.6 christos } 2127 1.6 christos if (!have_ipv4 && !have_ipv6) { 2128 1.1 christos fatal("could not find either IPv4 or IPv6"); 2129 1.6 christos } 2130 1.1 christos 2131 1.1 christos preparse_args(argc, argv); 2132 1.1 christos 2133 1.14 christos isc_managers_create(&mctx, 1, &loopmgr, &netmgr); 2134 1.6 christos isc_log_create(mctx, &lctx, &lcfg); 2135 1.1 christos 2136 1.3 christos RUNCHECK(dst_lib_init(mctx, NULL)); 2137 1.3 christos isc_nonce_buf(cookie_secret, sizeof(cookie_secret)); 2138 1.1 christos 2139 1.1 christos ISC_LIST_INIT(queries); 2140 1.3 christos parse_args(false, argc, argv); 2141 1.6 christos if (server == NULL) { 2142 1.1 christos fatal("a server '@xxx' is required"); 2143 1.6 christos } 2144 1.1 christos 2145 1.1 christos ns = 0; 2146 1.14 christos result = isc_getaddresses(server, port, &dstaddr, 1, &ns); 2147 1.1 christos if (result != ISC_R_SUCCESS) { 2148 1.6 christos fatal("couldn't get address for '%s': %s", server, 2149 1.6 christos isc_result_totext(result)); 2150 1.1 christos } 2151 1.1 christos 2152 1.1 christos if (isc_sockaddr_pf(&dstaddr) == PF_INET && have_ipv6) { 2153 1.1 christos isc_net_disableipv6(); 2154 1.3 christos have_ipv6 = false; 2155 1.1 christos } else if (isc_sockaddr_pf(&dstaddr) == PF_INET6 && have_ipv4) { 2156 1.1 christos isc_net_disableipv4(); 2157 1.3 christos have_ipv4 = false; 2158 1.1 christos } 2159 1.6 christos if (have_ipv4 && have_ipv6) { 2160 1.1 christos fatal("can't choose between IPv4 and IPv6"); 2161 1.6 christos } 2162 1.1 christos 2163 1.1 christos query = ISC_LIST_HEAD(queries); 2164 1.14 christos isc_loopmgr_setup(loopmgr, setup, NULL); 2165 1.14 christos isc_loopmgr_setup(loopmgr, sendqueries, query); 2166 1.14 christos isc_loopmgr_teardown(loopmgr, teardown, NULL); 2167 1.1 christos 2168 1.7 christos /* 2169 1.7 christos * Stall to the start of a new second. 2170 1.7 christos */ 2171 1.7 christos if (burst) { 2172 1.7 christos isc_time_t start, now; 2173 1.14 christos start = isc_time_now(); 2174 1.7 christos /* 2175 1.7 christos * Sleep to 1ms of the end of the second then run a busy loop 2176 1.7 christos * until the second changes. 2177 1.7 christos */ 2178 1.7 christos do { 2179 1.14 christos now = isc_time_now(); 2180 1.7 christos if (isc_time_seconds(&start) == isc_time_seconds(&now)) 2181 1.7 christos { 2182 1.13 christos unsigned int us = US_PER_SEC - 2183 1.13 christos (isc_time_nanoseconds(&now) / 2184 1.13 christos NS_PER_US); 2185 1.7 christos if (us > US_PER_MS) { 2186 1.7 christos usleep(us - US_PER_MS); 2187 1.7 christos } 2188 1.7 christos } else { 2189 1.7 christos break; 2190 1.7 christos } 2191 1.7 christos } while (1); 2192 1.7 christos } 2193 1.7 christos 2194 1.14 christos isc_loopmgr_run(loopmgr); 2195 1.8 christos 2196 1.8 christos dst_lib_destroy(); 2197 1.8 christos 2198 1.8 christos isc_log_destroy(&lctx); 2199 1.8 christos 2200 1.1 christos query = ISC_LIST_HEAD(queries); 2201 1.1 christos while (query != NULL) { 2202 1.1 christos struct query *next = ISC_LIST_NEXT(query, link); 2203 1.1 christos 2204 1.3 christos if (query->ednsopts != NULL) { 2205 1.3 christos for (i = 0; i < EDNSOPTS; i++) { 2206 1.3 christos if (query->ednsopts[i].value != NULL) { 2207 1.3 christos isc_mem_free(mctx, 2208 1.3 christos query->ednsopts[i].value); 2209 1.3 christos } 2210 1.3 christos } 2211 1.3 christos isc_mem_free(mctx, query->ednsopts); 2212 1.3 christos } 2213 1.1 christos if (query->ecs_addr != NULL) { 2214 1.1 christos isc_mem_free(mctx, query->ecs_addr); 2215 1.1 christos query->ecs_addr = NULL; 2216 1.1 christos } 2217 1.1 christos isc_mem_free(mctx, query); 2218 1.1 christos query = next; 2219 1.1 christos } 2220 1.1 christos 2221 1.6 christos if (default_query.ecs_addr != NULL) { 2222 1.1 christos isc_mem_free(mctx, default_query.ecs_addr); 2223 1.6 christos } 2224 1.1 christos 2225 1.14 christos isc_managers_destroy(&mctx, &loopmgr, &netmgr); 2226 1.14 christos return 0; 2227 1.1 christos } 2228