/* * drill.c * the main file of drill * (c) 2005-2008 NLnet Labs * * See the file LICENSE for the license * */ #include "drill.h" #include #ifdef HAVE_SSL #include #endif /* query debug, 2 hex dumps */ int verbosity; static int is_ixfr_with_serial(const char* name, uint32_t *serial) { char* end; if (strlen(name) > 5 && strncasecmp(name, "IXFR", 4) == 0 && name[4] == '=') { *serial = (uint32_t) strtol((name+5), &end, 10); return 1; } return 0; } static void usage(FILE *stream, const char *progname) { fprintf(stream, " Usage: %s name [@server] [type] [class]\n", progname); fprintf(stream, "\t can be a domain name or an IP address (-x lookups)\n"); fprintf(stream, "\t defaults to A\n"); fprintf(stream, "\t defaults to IN\n"); fprintf(stream, "\n\targuments may be placed in random order\n"); fprintf(stream, "\n Options:\n"); fprintf(stream, "\t-D\t\tenable DNSSEC (DO bit)\n"); #ifdef HAVE_SSL fprintf(stream, "\t-T\t\ttrace from the root down to \n"); fprintf(stream, "\t-S\t\tchase signature(s) from to a known key [*]\n"); #endif /*HAVE_SSL*/ fprintf(stream, "\t-I
\tsource address to query from\n"); fprintf(stream, "\t-V \tverbosity (0-5)\n"); fprintf(stream, "\t-Q\t\tquiet mode (overrules -V)\n"); fprintf(stream, "\n"); fprintf(stream, "\t-f file\t\tread packet from file and send it\n"); fprintf(stream, "\t-i file\t\tread packet from file and print it\n"); fprintf(stream, "\t-w file\t\twrite answer packet to file\n"); fprintf(stream, "\t-q file\t\twrite query packet to file\n"); fprintf(stream, "\t-h\t\tshow this help\n"); fprintf(stream, "\t-v\t\tshow version\n"); fprintf(stream, "\n Query options:\n"); fprintf(stream, "\t-4\t\tstay on ip4\n"); fprintf(stream, "\t-6\t\tstay on ip6\n"); fprintf(stream, "\t-a\t\tfallback to EDNS0 and TCP if the answer is truncated\n"); fprintf(stream, "\t-b \tuse as the buffer size (defaults to 512 b)\n"); fprintf(stream, "\t-c \tuse file for recursive nameserver configuration" "\n\t\t\t(/etc/resolv.conf)\n"); fprintf(stream, "\t-k \tspecify a file that contains a trusted DNSSEC key [**]\n"); fprintf(stream, "\t\t\tUsed to verify any signatures in the current answer.\n"); fprintf(stream, "\t\t\tWhen DNSSEC enabled tracing (-TD) or signature\n" "\t\t\tchasing (-S) and no key files are given, keys are read\n" "\t\t\tfrom: %s\n", LDNS_TRUST_ANCHOR_FILE); fprintf(stream, "\t-o \tset flags to:" "\n\t\t\t[QR|qr][AA|aa][TC|tc][RD|rd][CD|cd][RA|ra][AD|ad]\n"); fprintf(stream, "\t\t\tlowercase: unset bit, uppercase: set bit\n"); fprintf(stream, "\t-p \tuse as remote port number\n"); fprintf(stream, "\t-s\t\tshow the DS RR for each key in a packet\n"); fprintf(stream, "\t-u\t\tsend the query with udp (the default)\n"); fprintf(stream, "\t-x\t\tdo a reverse lookup\n"); fprintf(stream, "\twhen doing a secure trace:\n"); fprintf(stream, "\t-r \tuse file as root servers hint file\n"); fprintf(stream, "\t-t\t\tsend the query with tcp (connected)\n"); fprintf(stream, "\t-d \tuse domain as the start point for the trace\n"); fprintf(stream, "\t-y \tspecify named base64 tsig key, and optional an\n\t\t\talgorithm (defaults to hmac-md5.sig-alg.reg.int)\n"); fprintf(stream, "\t-z\t\tdon't randomize the nameservers before use\n"); fprintf(stream, "\n [*] = enables/implies DNSSEC\n"); fprintf(stream, " [**] = can be given more than once\n"); fprintf(stream, "\n ldns-team@nlnetlabs.nl | http://www.nlnetlabs.nl/ldns/\n"); } /** * Prints the drill version to stderr */ static void version(FILE *stream, const char *progname) { fprintf(stream, "%s version %s (ldns version %s)\n", progname, DRILL_VERSION, ldns_version()); fprintf(stream, "Written by NLnet Labs.\n"); fprintf(stream, "\nCopyright (c) 2004-2008 NLnet Labs.\n"); fprintf(stream, "Licensed under the revised BSD license.\n"); fprintf(stream, "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"); fprintf(stream, "FOR A PARTICULAR PURPOSE.\n"); } /** * Main function of drill * parse the arguments and prepare a query */ int main(int argc, char *argv[]) { ldns_resolver *res = NULL; ldns_resolver *cmdline_res = NULL; /* only used to resolv @name names */ ldns_rr_list *cmdline_rr_list = NULL; ldns_rdf *cmdline_dname = NULL; ldns_rdf *qname; ldns_pkt *pkt; ldns_pkt *qpkt; char *serv; char *src = NULL; const char *name; char *progname; char *query_file = NULL; char *answer_file = NULL; ldns_buffer *query_buffer = NULL; ldns_rdf *serv_rdf; ldns_rdf *src_rdf = NULL; ldns_rr_type type; ldns_rr_class clas; #if 0 ldns_pkt_opcode opcode = LDNS_PACKET_QUERY; #endif int i, c; int int_type; int int_clas; int PURPOSE; char *tsig_name = NULL; char *tsig_data = NULL; char *tsig_algorithm = NULL; size_t tsig_separator; size_t tsig_separator2; ldns_rr *axfr_rr; ldns_status status; char *type_str; uint32_t serial = 0; /* list of keys used in dnssec operations */ ldns_rr_list *key_list = ldns_rr_list_new(); /* what key verify the current answer */ ldns_rr_list *key_verified; /* resolver options */ uint16_t qflags; uint16_t qbuf; uint16_t qport; uint8_t qfamily; bool qdnssec; bool qfallback; bool qds; bool qusevc; bool qrandom; bool drill_reverse = false; char *resolv_conf_file = NULL; ldns_rdf *trace_start_name = NULL; int result = 0; uint8_t s6addr[16]; char ip6_arpa_str[74]; uint8_t s4addr[4]; char in_addr_arpa_str[40]; #ifdef USE_WINSOCK int r; WSADATA wsa_data; #endif ldns_output_format_storage fmt_storage; ldns_output_format* fmt = ldns_output_format_init(&fmt_storage); int_type = -1; serv = NULL; type = 0; int_clas = -1; name = NULL; clas = 0; qname = NULL; src = NULL; progname = strdup(argv[0]); #ifdef USE_WINSOCK r = WSAStartup(MAKEWORD(2,2), &wsa_data); if(r != 0) { printf("Failed WSAStartup: %d\n", r); result = EXIT_FAILURE; goto exit; } #endif /* USE_WINSOCK */ PURPOSE = DRILL_QUERY; qflags = LDNS_RD; qport = LDNS_PORT; verbosity = 2; qdnssec = false; qfamily = LDNS_RESOLV_INETANY; qfallback = false; qds = false; qbuf = 0; qusevc = false; qrandom = true; key_verified = NULL; ldns_edns_option_list* edns_list = NULL; ldns_init_random(NULL, 0); /* string from orig drill: "i:w:I46Sk:TNp:b:DsvhVcuaq:f:xr" */ /* global first, query opt next, option with parm's last * and sorted */ /* "46DITSVQf:i:w:q:achuvxzy:so:p:b:k:" */ while ((c = getopt(argc, argv, "46ab:c:d:Df:hi:I:k:o:p:q:Qr:sStTuvV:w:xy:z")) != -1) { switch(c) { /* global options */ case '4': qfamily = LDNS_RESOLV_INET; break; case '6': qfamily = LDNS_RESOLV_INET6; break; case 'D': qdnssec = true; break; case 'I': src = optarg; break; case 'T': if (PURPOSE == DRILL_CHASE) { fprintf(stderr, "-T and -S cannot be used at the same time.\n"); exit(EXIT_FAILURE); } PURPOSE = DRILL_TRACE; break; #ifdef HAVE_SSL case 'S': if (PURPOSE == DRILL_TRACE) { fprintf(stderr, "-T and -S cannot be used at the same time.\n"); exit(EXIT_FAILURE); } PURPOSE = DRILL_CHASE; break; #endif /* HAVE_SSL */ case 'V': if (strtok(optarg, "0123456789") != NULL) { fprintf(stderr, "-V expects an number as an argument.\n"); exit(EXIT_FAILURE); } verbosity = atoi(optarg); break; case 'Q': fmt->flags |= LDNS_FMT_SHORT; verbosity = -1; break; case 'f': query_file = optarg; break; case 'i': answer_file = optarg; PURPOSE = DRILL_AFROMFILE; break; case 'w': answer_file = optarg; break; case 'q': query_file = optarg; PURPOSE = DRILL_QTOFILE; break; case 'r': if (global_dns_root) { fprintf(stderr, "There was already a series of root servers set\n"); exit(EXIT_FAILURE); } global_dns_root = read_root_hints(optarg); if (!global_dns_root) { fprintf(stderr, "Unable to read root hints file %s, aborting\n", optarg); exit(EXIT_FAILURE); } break; /* query options */ case 'a': qfallback = true; break; case 'b': qbuf = (uint16_t)atoi(optarg); if (qbuf == 0) { error("%s", " could not be converted"); } break; case 'c': resolv_conf_file = optarg; break; case 't': qusevc = true; break; case 'k': status = read_key_file(optarg, key_list, false); if (status != LDNS_STATUS_OK) { error("Could not parse the key file %s: %s", optarg, ldns_get_errorstr_by_id(status)); } qdnssec = true; /* enable that too */ break; case 'o': /* only looks at the first hit: capital=ON, lowercase=OFF*/ if (strstr(optarg, "QR")) { DRILL_ON(qflags, LDNS_QR); } if (strstr(optarg, "qr")) { DRILL_OFF(qflags, LDNS_QR); } if (strstr(optarg, "AA")) { DRILL_ON(qflags, LDNS_AA); } if (strstr(optarg, "aa")) { DRILL_OFF(qflags, LDNS_AA); } if (strstr(optarg, "TC")) { DRILL_ON(qflags, LDNS_TC); } if (strstr(optarg, "tc")) { DRILL_OFF(qflags, LDNS_TC); } if (strstr(optarg, "RD")) { DRILL_ON(qflags, LDNS_RD); } if (strstr(optarg, "rd")) { DRILL_OFF(qflags, LDNS_RD); } if (strstr(optarg, "CD")) { DRILL_ON(qflags, LDNS_CD); } if (strstr(optarg, "cd")) { DRILL_OFF(qflags, LDNS_CD); } if (strstr(optarg, "RA")) { DRILL_ON(qflags, LDNS_RA); } if (strstr(optarg, "ra")) { DRILL_OFF(qflags, LDNS_RA); } if (strstr(optarg, "AD")) { DRILL_ON(qflags, LDNS_AD); } if (strstr(optarg, "ad")) { DRILL_OFF(qflags, LDNS_AD); } break; case 'p': qport = (uint16_t)atoi(optarg); if (qport == 0) { error("%s", " could not be converted"); } break; case 's': qds = true; break; case 'u': qusevc = false; break; case 'v': version(stdout, progname); result = EXIT_SUCCESS; goto exit; case 'x': drill_reverse = true; break; case 'y': #ifdef HAVE_SSL if (strchr(optarg, ':')) { tsig_separator = (size_t) (strchr(optarg, ':') - optarg); if (tsig_algorithm) { free(tsig_algorithm); tsig_algorithm = NULL; } if (strchr(optarg + tsig_separator + 1, ':')) { tsig_separator2 = (size_t) (strchr(optarg + tsig_separator + 1, ':') - optarg); tsig_algorithm = xmalloc(strlen(optarg) - tsig_separator2); strncpy(tsig_algorithm, optarg + tsig_separator2 + 1, strlen(optarg) - tsig_separator2); tsig_algorithm[strlen(optarg) - tsig_separator2 - 1] = '\0'; } else { tsig_separator2 = strlen(optarg); tsig_algorithm = strdup("hmac-md5.sig-alg.reg.int"); } tsig_name = xmalloc(tsig_separator + 1); tsig_data = xmalloc(tsig_separator2 - tsig_separator); strncpy(tsig_name, optarg, tsig_separator); strncpy(tsig_data, optarg + tsig_separator + 1, tsig_separator2 - tsig_separator - 1); /* strncpy does not append \0 if source is longer than n */ tsig_name[tsig_separator] = '\0'; tsig_data[ tsig_separator2 - tsig_separator - 1] = '\0'; } #else fprintf(stderr, "TSIG requested, but SSL is not supported\n"); result = EXIT_FAILURE; goto exit; #endif /* HAVE_SSL */ break; case 'z': qrandom = false; break; case 'd': trace_start_name = ldns_dname_new_frm_str(optarg); if (!trace_start_name) { fprintf(stderr, "Unable to parse argument for -%c\n", c); result = EXIT_FAILURE; goto exit; } break; case 'h': version(stdout, progname); usage(stdout, progname); result = EXIT_SUCCESS; goto exit; break; default: fprintf(stderr, "Unknown argument: -%c, use -h to see usage\n", c); result = EXIT_FAILURE; goto exit; } } argc -= optind; argv += optind; if ((PURPOSE == DRILL_CHASE || (PURPOSE == DRILL_TRACE && qdnssec)) && ldns_rr_list_rr_count(key_list) == 0) { (void) read_key_file(LDNS_TRUST_ANCHOR_FILE, key_list, true); } if (ldns_rr_list_rr_count(key_list) > 0) { printf(";; Number of trusted keys: %d\n", (int) ldns_rr_list_rr_count(key_list)); } /* do a secure trace when requested */ if (PURPOSE == DRILL_TRACE && qdnssec) { #ifdef HAVE_SSL if (ldns_rr_list_rr_count(key_list) == 0) { warning("%s", "No trusted keys were given. Will not be able to verify authenticity!"); } PURPOSE = DRILL_SECTRACE; #else fprintf(stderr, "ldns has not been compiled with OpenSSL support. Secure trace not available\n"); exit(1); #endif /* HAVE_SSL */ } /* parse the arguments, with multiple arguments, the last argument * found is used */ for(i = 0; i < argc; i++) { /* if ^@ then it's a server */ if (argv[i][0] == '@') { if (strlen(argv[i]) == 1) { warning("%s", "No nameserver given"); exit(EXIT_FAILURE); } serv = argv[i] + 1; continue; } /* if ^+ then it's an EDNS option */ if (argv[i][0] == '+') { if (!strcmp(argv[i]+1, "nsid")) { ldns_edns_option *edns; edns_list = ldns_edns_option_list_new(); /* create NSID EDNS*/ edns = ldns_edns_new_from_data(LDNS_EDNS_NSID, 0, NULL); if (edns_list == NULL || edns == NULL) { error("EDNS option could not be allocated"); break; } if (!(ldns_edns_option_list_push(edns_list, edns))) { error("EDNS option NSID could not be attached"); break; } continue; } else { error("Unsupported argument after '+'"); break; } } /* if has a dot, it's a name */ if (strchr(argv[i], '.')) { name = argv[i]; continue; } /* if it matches a type, it's a type */ if (int_type == -1) { type = ldns_get_rr_type_by_name(argv[i]); if (type != 0) { int_type = 0; continue; } else if (is_ixfr_with_serial(argv[i], &serial)) { type = LDNS_RR_TYPE_IXFR; int_type = 0; continue; } } /* if it matches a class, it's a class */ if (int_clas == -1) { clas = ldns_get_rr_class_by_name(argv[i]); if (clas != 0) { int_clas = 0; continue; } } /* it all fails assume it's a name */ name = argv[i]; } /* act like dig and use for . NS */ if (!name) { name = "."; int_type = 0; type = LDNS_RR_TYPE_NS; } /* defaults if not given */ if (int_clas == -1) { clas = LDNS_RR_CLASS_IN; } if (int_type == -1) { if (!drill_reverse) { type = LDNS_RR_TYPE_A; } else { type = LDNS_RR_TYPE_PTR; } } if (!drill_reverse) ; /* pass */ else if (strchr(name, ':')) { /* ipv4 or ipv6 addr? */ if (!inet_pton(AF_INET6, name, &s6addr)) { error("Syntax error: cannot parse IPv6 address\n"); } (void) snprintf(ip6_arpa_str, sizeof(ip6_arpa_str), "%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa.", (unsigned int)(s6addr[15] & 0x0F), (unsigned int)(s6addr[15] >> 4), (unsigned int)(s6addr[14] & 0x0F), (unsigned int)(s6addr[14] >> 4), (unsigned int)(s6addr[13] & 0x0F), (unsigned int)(s6addr[13] >> 4), (unsigned int)(s6addr[12] & 0x0F), (unsigned int)(s6addr[12] >> 4), (unsigned int)(s6addr[11] & 0x0F), (unsigned int)(s6addr[11] >> 4), (unsigned int)(s6addr[10] & 0x0F), (unsigned int)(s6addr[10] >> 4), (unsigned int)(s6addr[9] & 0x0F), (unsigned int)(s6addr[9] >> 4), (unsigned int)(s6addr[8] & 0x0F), (unsigned int)(s6addr[8] >> 4), (unsigned int)(s6addr[7] & 0x0F), (unsigned int)(s6addr[7] >> 4), (unsigned int)(s6addr[6] & 0x0F), (unsigned int)(s6addr[6] >> 4), (unsigned int)(s6addr[5] & 0x0F), (unsigned int)(s6addr[5] >> 4), (unsigned int)(s6addr[4] & 0x0F), (unsigned int)(s6addr[4] >> 4), (unsigned int)(s6addr[3] & 0x0F), (unsigned int)(s6addr[3] >> 4), (unsigned int)(s6addr[2] & 0x0F), (unsigned int)(s6addr[2] >> 4), (unsigned int)(s6addr[1] & 0x0F), (unsigned int)(s6addr[1] >> 4), (unsigned int)(s6addr[0] & 0x0F), (unsigned int)(s6addr[0] >> 4)); name = ip6_arpa_str; } else if (!inet_pton(AF_INET, name, &s4addr)) { error("Syntax error: cannot parse IPv4 address\n"); } else { (void) snprintf(in_addr_arpa_str, sizeof(in_addr_arpa_str), "%d.%d.%d.%d.in-addr.arpa.", (int)s4addr[3], (int)s4addr[2], (int)s4addr[1], (int)s4addr[0]); name = in_addr_arpa_str; } if (src) { src_rdf = ldns_rdf_new_addr_frm_str(src); if(!src_rdf) { fprintf(stderr, "-I must be a valid IP[v6] address.\n"); exit(EXIT_FAILURE); } if (ldns_rdf_size(src_rdf) == 4) { qfamily = LDNS_RESOLV_INET; } else if (ldns_rdf_size(src_rdf) == 16) { qfamily = LDNS_RESOLV_INET6; } } /* set the nameserver to use */ if (!serv) { /* no server given -- make a resolver from /etc/resolv.conf */ status = ldns_resolver_new_frm_file(&res, resolv_conf_file); if (status != LDNS_STATUS_OK) { warning("Could not create a resolver structure: %s (%s)\n" "Try drill @localhost if you have a resolver running on your machine.", ldns_get_errorstr_by_id(status), resolv_conf_file); result = EXIT_FAILURE; goto exit; } } else { res = ldns_resolver_new(); if (!res || strlen(serv) <= 0) { warning("Could not create a resolver structure"); result = EXIT_FAILURE; goto exit; } /* add the nameserver */ serv_rdf = ldns_rdf_new_addr_frm_str(serv); if (!serv_rdf) { /* try to resolv the name if possible */ status = ldns_resolver_new_frm_file(&cmdline_res, resolv_conf_file); if (status != LDNS_STATUS_OK) { error("%s", "@server ip could not be converted"); } ldns_resolver_set_dnssec(cmdline_res, qdnssec); ldns_resolver_set_ip6(cmdline_res, qfamily); ldns_resolver_set_fallback(cmdline_res, qfallback); ldns_resolver_set_usevc(cmdline_res, qusevc); ldns_resolver_set_source(cmdline_res, src_rdf); cmdline_dname = ldns_dname_new_frm_str(serv); cmdline_rr_list = ldns_get_rr_list_addr_by_name( cmdline_res, cmdline_dname, LDNS_RR_CLASS_IN, qflags); ldns_rdf_deep_free(cmdline_dname); if (!cmdline_rr_list) { /* This error msg is not always accurate */ error("%s `%s\'", "could not find any address for the name:", serv); } else { if (ldns_resolver_push_nameserver_rr_list( res, cmdline_rr_list ) != LDNS_STATUS_OK) { error("%s", "pushing nameserver"); } } } else { if (ldns_resolver_push_nameserver(res, serv_rdf) != LDNS_STATUS_OK) { error("%s", "pushing nameserver"); } else { ldns_rdf_deep_free(serv_rdf); } } } /* set the resolver options */ ldns_resolver_set_ixfr_serial(res, serial); ldns_resolver_set_port(res, qport); ldns_resolver_set_source(res, src_rdf); if (verbosity >= 5) { ldns_resolver_set_debug(res, true); } else { ldns_resolver_set_debug(res, false); } ldns_resolver_set_dnssec(res, qdnssec); /* ldns_resolver_set_dnssec_cd(res, qdnssec);*/ ldns_resolver_set_ip6(res, qfamily); ldns_resolver_set_fallback(res, qfallback); ldns_resolver_set_usevc(res, qusevc); ldns_resolver_set_random(res, qrandom); if (qbuf != 0) { ldns_resolver_set_edns_udp_size(res, qbuf); } if (!name && PURPOSE != DRILL_AFROMFILE && !query_file ) { usage(stdout, progname); result = EXIT_FAILURE; goto exit; } if (tsig_name && tsig_data) { /* With dig TSIG keys are also specified with -y, * but format with drill is: -y * and with dig: -y [hmac:]name:key * * When we detect an unknown tsig algorithm in algo, * but a known algorithm in name, we cane assume dig * order was used. * * Following if statement is to anticipate and correct dig order */ if ( strcasecmp(tsig_algorithm, "hmac-md5.sig-alg.reg.int") && strcasecmp(tsig_algorithm, "hmac-md5") && strcasecmp(tsig_algorithm, "hmac-sha1") && strcasecmp(tsig_algorithm, "hmac-sha256") && ( strcasecmp(tsig_name, "hmac-md5.sig-alg.reg.int") == 0 || strcasecmp(tsig_name, "hmac-md5") == 0 || strcasecmp(tsig_name, "hmac-sha1") == 0 || strcasecmp(tsig_name, "hmac-sha256") == 0 )) { /* Roll options */ char *tmp_tsig_algorithm = tsig_name; tsig_name = tsig_data; tsig_data = tsig_algorithm; tsig_algorithm = tmp_tsig_algorithm; } if (strcasecmp(tsig_algorithm, "hmac-md5") == 0) { free(tsig_algorithm); tsig_algorithm = strdup("hmac-md5.sig-alg.reg.int"); } ldns_resolver_set_tsig_keyname(res, tsig_name); ldns_resolver_set_tsig_keydata(res, tsig_data); ldns_resolver_set_tsig_algorithm(res, tsig_algorithm); } /* main switching part of drill */ switch(PURPOSE) { case DRILL_TRACE: /* do a trace from the root down */ if (!global_dns_root) { init_root(); } qname = ldns_dname_new_frm_str(name); if (!qname) { error("%s", "parsing query name"); } /* don't care about return packet */ do_trace(res, qname, type, clas); clear_root(); break; case DRILL_SECTRACE: /* do a secure trace from the root down */ if (!global_dns_root) { init_root(); } qname = ldns_dname_new_frm_str(name); if (!qname) { error("%s", "making qname"); } /* don't care about return packet */ #ifdef HAVE_SSL result = do_secure_trace(res, qname, type, clas, key_list, trace_start_name); #endif /* HAVE_SSL */ clear_root(); break; case DRILL_CHASE: qname = ldns_dname_new_frm_str(name); if (!qname) { error("%s", "making qname"); } ldns_resolver_set_dnssec(res, true); ldns_resolver_set_dnssec_cd(res, true); /* set dnssec implies udp_size of 4096 */ ldns_resolver_set_edns_udp_size(res, 4096); pkt = NULL; status = ldns_resolver_query_status( &pkt, res, qname, type, clas, qflags); if (status != LDNS_STATUS_OK) { error("error sending query: %s", ldns_get_errorstr_by_id(status)); } if (!pkt) { if (status == LDNS_STATUS_OK) { error("%s", "error pkt sending"); } result = EXIT_FAILURE; } else { if (verbosity >= 3) { ldns_pkt_print(stdout, pkt); } if (!ldns_pkt_answer(pkt)) { mesg("No answer in packet"); } else { #ifdef HAVE_SSL ldns_resolver_set_dnssec_anchors(res, ldns_rr_list_clone(key_list)); result = do_chase(res, qname, type, clas, key_list, pkt, qflags, NULL); if (result == LDNS_STATUS_OK) { if (verbosity != -1) { mesg("Chase successful"); } result = 0; } else { if (verbosity != -1) { mesg("Chase failed."); } } #endif /* HAVE_SSL */ } ldns_pkt_free(pkt); } break; case DRILL_AFROMFILE: pkt = read_hex_pkt(answer_file); if (pkt) { if (verbosity != -1) { ldns_pkt_print(stdout, pkt); } ldns_pkt_free(pkt); } break; case DRILL_QTOFILE: qname = ldns_dname_new_frm_str(name); if (!qname) { error("%s", "making qname"); } status = ldns_resolver_prepare_query_pkt(&qpkt, res, qname, type, clas, qflags); if(status != LDNS_STATUS_OK) { error("%s", "making query: %s", ldns_get_errorstr_by_id(status)); } dump_hex(qpkt, query_file); ldns_pkt_free(qpkt); break; case DRILL_NSEC: break; case DRILL_QUERY: default: if (query_file) { /* this old way, the query packet needed to be parseable, but we want to be able to send mangled packets, so we need to do it directly */ #if 0 qpkt = read_hex_pkt(query_file); if (qpkt) { status = ldns_resolver_send_pkt(&pkt, res, qpkt); if (status != LDNS_STATUS_OK) { printf("Error: %s\n", ldns_get_errorstr_by_id(status)); exit(1); } } else { /* qpkt was bogus, reset pkt */ pkt = NULL; } #endif query_buffer = read_hex_buffer(query_file); if (query_buffer) { status = ldns_send_buffer(&pkt, res, query_buffer, NULL); ldns_buffer_free(query_buffer); if (status != LDNS_STATUS_OK) { printf("Error: %s\n", ldns_get_errorstr_by_id(status)); exit(1); } } else { printf("NO BUFFER\n"); pkt = NULL; } } else { qname = ldns_dname_new_frm_str(name); if (!qname) { error("%s", "error in making qname"); } if (type == LDNS_RR_TYPE_AXFR) { status = ldns_axfr_start(res, qname, clas); if(status != LDNS_STATUS_OK) { error("Error starting axfr: %s", ldns_get_errorstr_by_id(status)); } axfr_rr = ldns_axfr_next(res); if(!axfr_rr) { fprintf(stderr, "AXFR failed.\n"); ldns_pkt_print(stdout, ldns_axfr_last_pkt(res)); goto exit; } while (axfr_rr) { if (verbosity != -1) { ldns_rr_print(stdout, axfr_rr); } ldns_rr_free(axfr_rr); axfr_rr = ldns_axfr_next(res); } goto exit; } else { /* create a packet and set the RD flag on it */ pkt = NULL; status = ldns_resolver_prepare_query_pkt(&qpkt, res, qname, type, clas, qflags); if(status != LDNS_STATUS_OK) { error("%s", "making query: %s", ldns_get_errorstr_by_id(status)); } if (edns_list) { /* attach the structed EDNS options */ ldns_pkt_set_edns_option_list(qpkt, edns_list); } status = ldns_resolver_send_pkt(&pkt, res, qpkt); ldns_pkt_free(qpkt); if (status != LDNS_STATUS_OK) { error("error sending query: %s" , ldns_get_errorstr_by_id( status)); } } } /* now handling the response message/packet */ if (!pkt) { mesg("No packet received"); result = EXIT_FAILURE; } else { ldns_pkt_print_fmt(stdout, fmt, pkt); if (verbosity != -1) { if (ldns_pkt_tc(pkt)) { fprintf(stdout, "\n;; WARNING: The answer packet was truncated; you might want to\n"); fprintf(stdout, ";; query again with TCP (-t argument), or EDNS0 (-b for buffer size)\n"); } } if (qds) { if (verbosity != -1) { print_ds_of_keys(pkt); printf("\n"); } } if (ldns_rr_list_rr_count(key_list) > 0) { /* -k's were given on the cmd line */ ldns_rr_list *rrset_verified; uint16_t key_count; rrset_verified = ldns_pkt_rr_list_by_name_and_type( pkt, qname, type, LDNS_SECTION_ANY_NOQUESTION); if (type == LDNS_RR_TYPE_ANY) { /* don't verify this */ break; } if (verbosity != -1) { printf("; "); ldns_rr_list_print(stdout, rrset_verified); } /* verify */ #ifdef HAVE_SSL key_verified = ldns_rr_list_new(); result = ldns_pkt_verify(pkt, type, qname, key_list, NULL, key_verified); if (result == LDNS_STATUS_ERR) { /* is the existence denied then? */ result = ldns_verify_denial(pkt, qname, type, NULL, NULL); if (result == LDNS_STATUS_OK) { if (verbosity != -1) { printf("Existence denied for "); ldns_rdf_print(stdout, qname); type_str = ldns_rr_type2str(type); printf("\t%s\n", type_str); LDNS_FREE(type_str); } } else { if (verbosity != -1) { printf("Bad data; RR for name and " "type not found or failed to " "verify, and denial of " "existence failed.\n"); } } } else if (result == LDNS_STATUS_OK) { for(key_count = 0; key_count < ldns_rr_list_rr_count(key_verified); key_count++) { if (verbosity != -1) { printf("; VALIDATED by id = %u, owner = ", (unsigned int)ldns_calc_keytag( ldns_rr_list_rr(key_verified, key_count))); ldns_rdf_print(stdout, ldns_rr_owner( ldns_rr_list_rr(key_list, key_count))); printf("\n"); } } } else { for(key_count = 0; key_count < ldns_rr_list_rr_count(key_list); key_count++) { if (verbosity != -1) { printf("; %s for id = %u, owner = ", ldns_get_errorstr_by_id(result), (unsigned int)ldns_calc_keytag( ldns_rr_list_rr(key_list, key_count))); ldns_rdf_print(stdout, ldns_rr_owner( ldns_rr_list_rr(key_list, key_count))); printf("\n"); } } } ldns_rr_list_free(key_verified); #else (void) key_count; #endif /* HAVE_SSL */ } if (answer_file) { dump_hex(pkt, answer_file); } ldns_pkt_free(pkt); } break; } exit: ldns_rdf_deep_free(qname); ldns_rdf_deep_free(src_rdf); ldns_resolver_deep_free(res); ldns_resolver_deep_free(cmdline_res); ldns_rr_list_deep_free(key_list); ldns_rr_list_deep_free(cmdline_rr_list); ldns_rdf_deep_free(trace_start_name); xfree(progname); xfree(tsig_name); xfree(tsig_data); xfree(tsig_algorithm); #ifdef HAVE_SSL #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(HAVE_LIBRESSL) #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA CRYPTO_cleanup_all_ex_data (); #endif #ifdef HAVE_ERR_FREE_STRINGS ERR_free_strings (); #endif #ifdef HAVE_EVP_CLEANUP EVP_cleanup (); #endif #endif #endif #ifdef USE_WINSOCK WSACleanup(); #endif return result; }