1238106Sdes/* 2238106Sdes * checkconf/unbound-host.c - replacement for host that supports validation. 3238106Sdes * 4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved. 5238106Sdes * 6238106Sdes * This software is open source. 7238106Sdes * 8238106Sdes * Redistribution and use in source and binary forms, with or without 9238106Sdes * modification, are permitted provided that the following conditions 10238106Sdes * are met: 11238106Sdes * 12238106Sdes * Redistributions of source code must retain the above copyright notice, 13238106Sdes * this list of conditions and the following disclaimer. 14238106Sdes * 15238106Sdes * Redistributions in binary form must reproduce the above copyright notice, 16238106Sdes * this list of conditions and the following disclaimer in the documentation 17238106Sdes * and/or other materials provided with the distribution. 18238106Sdes * 19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may 20238106Sdes * be used to endorse or promote products derived from this software without 21238106Sdes * specific prior written permission. 22238106Sdes * 23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34238106Sdes */ 35238106Sdes 36238106Sdes/** 37238106Sdes * \file 38238106Sdes * 39238106Sdes * This file performs functionality like 'host', and also supports validation. 40238106Sdes * It uses the libunbound library. 41238106Sdes */ 42238106Sdes 43238106Sdes#include "config.h" 44238106Sdes#ifdef HAVE_GETOPT_H 45238106Sdes#include <getopt.h> 46238106Sdes#endif 47238106Sdes/* remove alloc checks, not in this part of the code */ 48238106Sdes#ifdef UNBOUND_ALLOC_STATS 49238106Sdes#undef malloc 50238106Sdes#undef calloc 51238106Sdes#undef free 52238106Sdes#undef realloc 53238106Sdes#endif 54238106Sdes#ifdef UNBOUND_ALLOC_LITE 55238106Sdes#undef malloc 56238106Sdes#undef calloc 57238106Sdes#undef free 58238106Sdes#undef realloc 59238106Sdes#undef strdup 60238106Sdes#define unbound_lite_wrapstr(s) s 61238106Sdes#endif 62238106Sdes#include "libunbound/unbound.h" 63291767Sdes#include "sldns/rrdef.h" 64291767Sdes#include "sldns/wire2str.h" 65249141Sdes#ifdef HAVE_NSS 66249141Sdes/* nss3 */ 67249141Sdes#include "nss.h" 68249141Sdes#endif 69238106Sdes 70238106Sdes/** verbosity for unbound-host app */ 71238106Sdesstatic int verb = 0; 72238106Sdes 73238106Sdes/** Give unbound-host usage, and exit (1). */ 74238106Sdesstatic void 75238106Sdesusage() 76238106Sdes{ 77238106Sdes printf("Usage: unbound-host [-vdhr46] [-c class] [-t type] hostname\n"); 78238106Sdes printf(" [-y key] [-f keyfile] [-F namedkeyfile]\n"); 79238106Sdes printf(" [-C configfile]\n"); 80238106Sdes printf(" Queries the DNS for information.\n"); 81238106Sdes printf(" The hostname is looked up for IP4, IP6 and mail.\n"); 82238106Sdes printf(" If an ip-address is given a reverse lookup is done.\n"); 83238106Sdes printf(" Use the -v option to see DNSSEC security information.\n"); 84238106Sdes printf(" -t type what type to look for.\n"); 85238106Sdes printf(" -c class what class to look for, if not class IN.\n"); 86238106Sdes printf(" -y 'keystring' specify trust anchor, DS or DNSKEY, like\n"); 87238106Sdes printf(" -y 'example.com DS 31560 5 1 1CFED8478...'\n"); 88285206Sdes printf(" -D DNSSEC enable with default root anchor\n"); 89285206Sdes printf(" from %s\n", ROOT_ANCHOR_FILE); 90238106Sdes printf(" -f keyfile read trust anchors from file, with lines as -y.\n"); 91238106Sdes printf(" -F keyfile read named.conf-style trust anchors.\n"); 92238106Sdes printf(" -C config use the specified unbound.conf (none read by default)\n"); 93238106Sdes printf(" -r read forwarder information from /etc/resolv.conf\n"); 94238106Sdes printf(" breaks validation if the fwder does not do DNSSEC.\n"); 95238106Sdes printf(" -v be more verbose, shows nodata and security.\n"); 96238106Sdes printf(" -d debug, traces the action, -d -d shows more.\n"); 97238106Sdes printf(" -4 use ipv4 network, avoid ipv6.\n"); 98238106Sdes printf(" -6 use ipv6 network, avoid ipv4.\n"); 99238106Sdes printf(" -h show this usage help.\n"); 100238106Sdes printf("Version %s\n", PACKAGE_VERSION); 101238106Sdes printf("BSD licensed, see LICENSE in source package for details.\n"); 102238106Sdes printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 103238106Sdes exit(1); 104238106Sdes} 105238106Sdes 106238106Sdes/** determine if str is ip4 and put into reverse lookup format */ 107238106Sdesstatic int 108238106Sdesisip4(const char* nm, char** res) 109238106Sdes{ 110238106Sdes struct in_addr addr; 111238106Sdes /* ddd.ddd.ddd.ddd.in-addr.arpa. is less than 32 */ 112238106Sdes char buf[32]; 113238106Sdes if(inet_pton(AF_INET, nm, &addr) <= 0) { 114238106Sdes return 0; 115238106Sdes } 116238106Sdes snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa", 117238106Sdes (unsigned)((uint8_t*)&addr)[3], (unsigned)((uint8_t*)&addr)[2], 118238106Sdes (unsigned)((uint8_t*)&addr)[1], (unsigned)((uint8_t*)&addr)[0]); 119238106Sdes *res = strdup(buf); 120238106Sdes return 1; 121238106Sdes} 122238106Sdes 123238106Sdes/** determine if str is ip6 and put into reverse lookup format */ 124238106Sdesstatic int 125238106Sdesisip6(const char* nm, char** res) 126238106Sdes{ 127238106Sdes struct in6_addr addr; 128238106Sdes /* [nibble.]{32}.ip6.arpa. is less than 128 */ 129238106Sdes const char* hex = "0123456789abcdef"; 130238106Sdes char buf[128]; 131238106Sdes char *p; 132238106Sdes int i; 133238106Sdes if(inet_pton(AF_INET6, nm, &addr) <= 0) { 134238106Sdes return 0; 135238106Sdes } 136238106Sdes p = buf; 137238106Sdes for(i=15; i>=0; i--) { 138238106Sdes uint8_t b = ((uint8_t*)&addr)[i]; 139238106Sdes *p++ = hex[ (b&0x0f) ]; 140238106Sdes *p++ = '.'; 141238106Sdes *p++ = hex[ (b&0xf0) >> 4 ]; 142238106Sdes *p++ = '.'; 143238106Sdes } 144238106Sdes snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa"); 145238106Sdes *res = strdup(buf); 146238106Sdes if(!*res) { 147238106Sdes fprintf(stderr, "error: out of memory\n"); 148238106Sdes exit(1); 149238106Sdes } 150238106Sdes return 1; 151238106Sdes} 152238106Sdes 153238106Sdes/** massage input name */ 154238106Sdesstatic char* 155238106Sdesmassage_qname(const char* nm, int* reverse) 156238106Sdes{ 157238106Sdes /* recognise IP4 and IP6, create reverse addresses if needed */ 158238106Sdes char* res; 159238106Sdes if(isip4(nm, &res)) { 160238106Sdes *reverse = 1; 161238106Sdes } else if(isip6(nm, &res)) { 162238106Sdes *reverse = 1; 163238106Sdes } else { 164238106Sdes res = strdup(nm); 165238106Sdes } 166238106Sdes if(!res) { 167238106Sdes fprintf(stderr, "error: out of memory\n"); 168238106Sdes exit(1); 169238106Sdes } 170238106Sdes return res; 171238106Sdes} 172238106Sdes 173238106Sdes/** massage input type */ 174238106Sdesstatic int 175238106Sdesmassage_type(const char* t, int reverse, int* multi) 176238106Sdes{ 177238106Sdes if(t) { 178269257Sdes int r = sldns_get_rr_type_by_name(t); 179238106Sdes if(r == 0 && strcasecmp(t, "TYPE0") != 0 && 180238106Sdes strcmp(t, "") != 0) { 181238106Sdes fprintf(stderr, "error unknown type %s\n", t); 182238106Sdes exit(1); 183238106Sdes } 184238106Sdes return r; 185238106Sdes } 186238106Sdes if(!t && reverse) 187238106Sdes return LDNS_RR_TYPE_PTR; 188238106Sdes *multi = 1; 189238106Sdes return LDNS_RR_TYPE_A; 190238106Sdes} 191238106Sdes 192238106Sdes/** massage input class */ 193238106Sdesstatic int 194238106Sdesmassage_class(const char* c) 195238106Sdes{ 196238106Sdes if(c) { 197269257Sdes int r = sldns_get_rr_class_by_name(c); 198238106Sdes if(r == 0 && strcasecmp(c, "CLASS0") != 0 && 199238106Sdes strcmp(c, "") != 0) { 200238106Sdes fprintf(stderr, "error unknown class %s\n", c); 201238106Sdes exit(1); 202238106Sdes } 203238106Sdes return r; 204238106Sdes } 205238106Sdes return LDNS_RR_CLASS_IN; 206238106Sdes} 207238106Sdes 208238106Sdes/** nice security status string */ 209238106Sdesstatic const char* 210238106Sdessecure_str(struct ub_result* result) 211238106Sdes{ 212238106Sdes if(result->secure) return "(secure)"; 213238106Sdes if(result->bogus) return "(BOGUS (security failure))"; 214238106Sdes return "(insecure)"; 215238106Sdes} 216238106Sdes 217238106Sdes/** nice string for type */ 218238106Sdesstatic void 219238106Sdespretty_type(char* s, size_t len, int t) 220238106Sdes{ 221269257Sdes char d[16]; 222269257Sdes sldns_wire2str_type_buf((uint16_t)t, d, sizeof(d)); 223238106Sdes snprintf(s, len, "%s", d); 224238106Sdes} 225238106Sdes 226238106Sdes/** nice string for class */ 227238106Sdesstatic void 228238106Sdespretty_class(char* s, size_t len, int c) 229238106Sdes{ 230269257Sdes char d[16]; 231269257Sdes sldns_wire2str_class_buf((uint16_t)c, d, sizeof(d)); 232238106Sdes snprintf(s, len, "%s", d); 233238106Sdes} 234238106Sdes 235238106Sdes/** nice string for rcode */ 236238106Sdesstatic void 237238106Sdespretty_rcode(char* s, size_t len, int r) 238238106Sdes{ 239269257Sdes char d[16]; 240269257Sdes sldns_wire2str_rcode_buf(r, d, sizeof(d)); 241269257Sdes snprintf(s, len, "%s", d); 242238106Sdes} 243238106Sdes 244238106Sdes/** convert and print rdata */ 245238106Sdesstatic void 246238106Sdesprint_rd(int t, char* data, size_t len) 247238106Sdes{ 248269257Sdes char s[65535]; 249269257Sdes sldns_wire2str_rdata_buf((uint8_t*)data, len, s, sizeof(s), (uint16_t)t); 250269257Sdes printf(" %s", s); 251238106Sdes} 252238106Sdes 253238106Sdes/** pretty line of RR data for results */ 254238106Sdesstatic void 255238106Sdespretty_rdata(char* q, char* cstr, char* tstr, int t, const char* sec, 256238106Sdes char* data, size_t len) 257238106Sdes{ 258238106Sdes printf("%s", q); 259238106Sdes if(strcmp(cstr, "IN") != 0) 260238106Sdes printf(" in class %s", cstr); 261238106Sdes if(t == LDNS_RR_TYPE_A) 262238106Sdes printf(" has address"); 263238106Sdes else if(t == LDNS_RR_TYPE_AAAA) 264238106Sdes printf(" has IPv6 address"); 265238106Sdes else if(t == LDNS_RR_TYPE_MX) 266238106Sdes printf(" mail is handled by"); 267238106Sdes else if(t == LDNS_RR_TYPE_PTR) 268238106Sdes printf(" domain name pointer"); 269238106Sdes else printf(" has %s record", tstr); 270238106Sdes print_rd(t, data, len); 271238106Sdes if(verb > 0) 272238106Sdes printf(" %s", sec); 273238106Sdes printf("\n"); 274238106Sdes} 275238106Sdes 276238106Sdes/** pretty line of output for results */ 277238106Sdesstatic void 278238106Sdespretty_output(char* q, int t, int c, struct ub_result* result, int docname) 279238106Sdes{ 280238106Sdes int i; 281238106Sdes const char *secstatus = secure_str(result); 282238106Sdes char tstr[16]; 283238106Sdes char cstr[16]; 284238106Sdes char rcodestr[16]; 285238106Sdes pretty_type(tstr, 16, t); 286238106Sdes pretty_class(cstr, 16, c); 287238106Sdes pretty_rcode(rcodestr, 16, result->rcode); 288238106Sdes 289238106Sdes if(!result->havedata && result->rcode) { 290238106Sdes printf("Host %s not found: %d(%s).", 291238106Sdes q, result->rcode, rcodestr); 292238106Sdes if(verb > 0) 293238106Sdes printf(" %s", secstatus); 294238106Sdes printf("\n"); 295238106Sdes if(result->bogus && result->why_bogus) 296238106Sdes printf("%s\n", result->why_bogus); 297238106Sdes return; 298238106Sdes } 299238106Sdes if(docname && result->canonname && 300238106Sdes result->canonname != result->qname) { 301238106Sdes printf("%s is an alias for %s", result->qname, 302238106Sdes result->canonname); 303238106Sdes if(verb > 0) 304238106Sdes printf(" %s", secstatus); 305238106Sdes printf("\n"); 306238106Sdes } 307238106Sdes /* remove trailing . from long canonnames for nicer output */ 308238106Sdes if(result->canonname && strlen(result->canonname) > 1 && 309238106Sdes result->canonname[strlen(result->canonname)-1] == '.') 310238106Sdes result->canonname[strlen(result->canonname)-1] = 0; 311238106Sdes if(!result->havedata) { 312238106Sdes if(verb > 0) { 313238106Sdes printf("%s", result->canonname?result->canonname:q); 314238106Sdes if(strcmp(cstr, "IN") != 0) 315238106Sdes printf(" in class %s", cstr); 316238106Sdes if(t == LDNS_RR_TYPE_A) 317238106Sdes printf(" has no address"); 318238106Sdes else if(t == LDNS_RR_TYPE_AAAA) 319238106Sdes printf(" has no IPv6 address"); 320238106Sdes else if(t == LDNS_RR_TYPE_PTR) 321238106Sdes printf(" has no domain name ptr"); 322238106Sdes else if(t == LDNS_RR_TYPE_MX) 323238106Sdes printf(" has no mail handler record"); 324238106Sdes else if(t == LDNS_RR_TYPE_ANY) { 325269257Sdes char* s = sldns_wire2str_pkt( 326269257Sdes result->answer_packet, 327269257Sdes (size_t)result->answer_len); 328269257Sdes if(!s) { 329269257Sdes fprintf(stderr, "alloc failure\n"); 330238106Sdes exit(1); 331238106Sdes } 332269257Sdes printf("%s\n", s); 333238106Sdes } else printf(" has no %s record", tstr); 334238106Sdes printf(" %s\n", secstatus); 335238106Sdes } 336238106Sdes /* else: emptiness to indicate no data */ 337238106Sdes if(result->bogus && result->why_bogus) 338238106Sdes printf("%s\n", result->why_bogus); 339238106Sdes return; 340238106Sdes } 341238106Sdes i=0; 342238106Sdes while(result->data[i]) 343238106Sdes { 344238106Sdes pretty_rdata( 345238106Sdes result->canonname?result->canonname:q, 346238106Sdes cstr, tstr, t, secstatus, result->data[i], 347238106Sdes (size_t)result->len[i]); 348238106Sdes i++; 349238106Sdes } 350238106Sdes if(result->bogus && result->why_bogus) 351238106Sdes printf("%s\n", result->why_bogus); 352238106Sdes} 353238106Sdes 354238106Sdes/** perform a lookup and printout return if domain existed */ 355238106Sdesstatic int 356238106Sdesdnslook(struct ub_ctx* ctx, char* q, int t, int c, int docname) 357238106Sdes{ 358238106Sdes int ret; 359238106Sdes struct ub_result* result; 360238106Sdes 361238106Sdes ret = ub_resolve(ctx, q, t, c, &result); 362238106Sdes if(ret != 0) { 363238106Sdes fprintf(stderr, "resolve error: %s\n", ub_strerror(ret)); 364238106Sdes exit(1); 365238106Sdes } 366238106Sdes pretty_output(q, t, c, result, docname); 367238106Sdes ret = result->nxdomain; 368238106Sdes ub_resolve_free(result); 369238106Sdes return ret; 370238106Sdes} 371238106Sdes 372238106Sdes/** perform host lookup */ 373238106Sdesstatic void 374238106Sdeslookup(struct ub_ctx* ctx, const char* nm, const char* qt, const char* qc) 375238106Sdes{ 376238106Sdes /* massage input into a query name, type and class */ 377238106Sdes int multi = 0; /* no type, so do A, AAAA, MX */ 378238106Sdes int reverse = 0; /* we are doing a reverse lookup */ 379238106Sdes char* realq = massage_qname(nm, &reverse); 380238106Sdes int t = massage_type(qt, reverse, &multi); 381238106Sdes int c = massage_class(qc); 382238106Sdes 383238106Sdes /* perform the query */ 384238106Sdes if(multi) { 385238106Sdes if(!dnslook(ctx, realq, LDNS_RR_TYPE_A, c, 1)) { 386238106Sdes /* domain exists, lookup more */ 387238106Sdes (void)dnslook(ctx, realq, LDNS_RR_TYPE_AAAA, c, 0); 388238106Sdes (void)dnslook(ctx, realq, LDNS_RR_TYPE_MX, c, 0); 389238106Sdes } 390238106Sdes } else { 391238106Sdes (void)dnslook(ctx, realq, t, c, 1); 392238106Sdes } 393238106Sdes ub_ctx_delete(ctx); 394238106Sdes free(realq); 395238106Sdes} 396238106Sdes 397238106Sdes/** print error if any */ 398238106Sdesstatic void 399238106Sdescheck_ub_res(int r) 400238106Sdes{ 401238106Sdes if(r != 0) { 402238106Sdes fprintf(stderr, "error: %s\n", ub_strerror(r)); 403238106Sdes exit(1); 404238106Sdes } 405238106Sdes} 406238106Sdes 407238106Sdes/** getopt global, in case header files fail to declare it. */ 408238106Sdesextern int optind; 409238106Sdes/** getopt global, in case header files fail to declare it. */ 410238106Sdesextern char* optarg; 411238106Sdes 412285206Sdes/** Main routine for unbound-host */ 413238106Sdesint main(int argc, char* argv[]) 414238106Sdes{ 415238106Sdes int c; 416238106Sdes char* qclass = NULL; 417238106Sdes char* qtype = NULL; 418238106Sdes struct ub_ctx* ctx = NULL; 419238106Sdes int debuglevel = 0; 420238106Sdes 421238106Sdes ctx = ub_ctx_create(); 422238106Sdes if(!ctx) { 423238106Sdes fprintf(stderr, "error: out of memory\n"); 424238106Sdes exit(1); 425238106Sdes } 426285206Sdes /* no need to fetch additional targets, we only do few lookups */ 427285206Sdes check_ub_res(ub_ctx_set_option(ctx, "target-fetch-policy:", "0 0 0 0 0")); 428238106Sdes 429238106Sdes /* parse the options */ 430285206Sdes while( (c=getopt(argc, argv, "46DF:c:df:hrt:vy:C:")) != -1) { 431238106Sdes switch(c) { 432238106Sdes case '4': 433238106Sdes check_ub_res(ub_ctx_set_option(ctx, "do-ip6:", "no")); 434238106Sdes break; 435238106Sdes case '6': 436238106Sdes check_ub_res(ub_ctx_set_option(ctx, "do-ip4:", "no")); 437238106Sdes break; 438238106Sdes case 'c': 439238106Sdes qclass = optarg; 440238106Sdes break; 441238106Sdes case 'C': 442238106Sdes check_ub_res(ub_ctx_config(ctx, optarg)); 443238106Sdes break; 444285206Sdes case 'D': 445285206Sdes check_ub_res(ub_ctx_add_ta_file(ctx, ROOT_ANCHOR_FILE)); 446285206Sdes break; 447238106Sdes case 'd': 448238106Sdes debuglevel++; 449238106Sdes if(debuglevel < 2) 450238106Sdes debuglevel = 2; /* at least VERB_DETAIL */ 451238106Sdes break; 452238106Sdes case 'r': 453238106Sdes check_ub_res(ub_ctx_resolvconf(ctx, "/etc/resolv.conf")); 454238106Sdes break; 455238106Sdes case 't': 456238106Sdes qtype = optarg; 457238106Sdes break; 458238106Sdes case 'v': 459238106Sdes verb++; 460238106Sdes break; 461238106Sdes case 'y': 462238106Sdes check_ub_res(ub_ctx_add_ta(ctx, optarg)); 463238106Sdes break; 464238106Sdes case 'f': 465238106Sdes check_ub_res(ub_ctx_add_ta_file(ctx, optarg)); 466238106Sdes break; 467238106Sdes case 'F': 468238106Sdes check_ub_res(ub_ctx_trustedkeys(ctx, optarg)); 469238106Sdes break; 470238106Sdes case '?': 471238106Sdes case 'h': 472238106Sdes default: 473238106Sdes usage(); 474238106Sdes } 475238106Sdes } 476238106Sdes if(debuglevel != 0) /* set after possible -C options */ 477238106Sdes check_ub_res(ub_ctx_debuglevel(ctx, debuglevel)); 478238106Sdes if(ub_ctx_get_option(ctx, "use-syslog", &optarg) == 0) { 479238106Sdes if(strcmp(optarg, "yes") == 0) /* disable use-syslog */ 480238106Sdes check_ub_res(ub_ctx_set_option(ctx, 481238106Sdes "use-syslog:", "no")); 482238106Sdes free(optarg); 483238106Sdes } 484238106Sdes argc -= optind; 485238106Sdes argv += optind; 486238106Sdes if(argc != 1) 487238106Sdes usage(); 488238106Sdes 489249141Sdes#ifdef HAVE_NSS 490249141Sdes if(NSS_NoDB_Init(".") != SECSuccess) { 491249141Sdes fprintf(stderr, "could not init NSS\n"); 492249141Sdes return 1; 493249141Sdes } 494249141Sdes#endif 495238106Sdes lookup(ctx, argv[0], qtype, qclass); 496238106Sdes return 0; 497238106Sdes} 498