1 /* 2 * checkconf/unbound-host.c - replacement for host that supports validation. 3 * 4 * Copyright (c) 2007, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /** 37 * \file 38 * 39 * This file performs functionality like 'host', and also supports validation. 40 * It uses the libunbound library. 41 */ 42 43 #include "config.h" 44 #ifdef HAVE_GETOPT_H 45 #include <getopt.h> 46 #endif 47 /* remove alloc checks, not in this part of the code */ 48 #ifdef UNBOUND_ALLOC_STATS 49 #undef malloc 50 #undef calloc 51 #undef free 52 #undef realloc 53 #endif 54 #ifdef UNBOUND_ALLOC_LITE 55 #undef malloc 56 #undef calloc 57 #undef free 58 #undef realloc 59 #undef strdup 60 #define unbound_lite_wrapstr(s) s 61 #endif 62 #include "libunbound/unbound.h" 63 #include "sldns/rrdef.h" 64 #include "sldns/wire2str.h" 65 #ifdef HAVE_NSS 66 /* nss3 */ 67 #include "nss.h" 68 #endif 69 #ifdef HAVE_SSL 70 #ifdef HAVE_OPENSSL_SSL_H 71 #include <openssl/ssl.h> 72 #endif 73 #ifdef HAVE_OPENSSL_ERR_H 74 #include <openssl/err.h> 75 #endif 76 #endif /* HAVE_SSL */ 77 78 /** verbosity for unbound-host app */ 79 static int verb = 0; 80 81 /** Give unbound-host usage, and exit (1). */ 82 static void 83 usage(void) 84 { 85 printf("Usage: unbound-host [-vdhr46] [-c class] [-t type] hostname\n"); 86 printf(" [-y key] [-f keyfile] [-F namedkeyfile]\n"); 87 printf(" [-C configfile]\n"); 88 printf(" Queries the DNS for information.\n"); 89 printf(" The hostname is looked up for IP4, IP6 and mail.\n"); 90 printf(" If an ip-address is given a reverse lookup is done.\n"); 91 printf(" Use the -v option to see DNSSEC security information.\n"); 92 printf(" -t type what type to look for.\n"); 93 printf(" -c class what class to look for, if not class IN.\n"); 94 printf(" -y 'keystring' specify trust anchor, DS or DNSKEY, like\n"); 95 printf(" -y 'example.com DS 31560 5 1 1CFED8478...'\n"); 96 printf(" -D DNSSEC enable with default root anchor\n"); 97 printf(" from %s\n", ROOT_ANCHOR_FILE); 98 printf(" -f keyfile read trust anchors from file, with lines as -y.\n"); 99 printf(" -F keyfile read named.conf-style trust anchors.\n"); 100 printf(" -C config use the specified unbound.conf (none read by default)\n"); 101 printf(" -r read forwarder information from /etc/resolv.conf\n"); 102 printf(" breaks validation if the forwarder does not do DNSSEC.\n"); 103 printf(" -v be more verbose, shows nodata and security.\n"); 104 printf(" -d debug, traces the action, -d -d shows more.\n"); 105 printf(" -4 use ipv4 network, avoid ipv6.\n"); 106 printf(" -6 use ipv6 network, avoid ipv4.\n"); 107 printf(" -h show this usage help.\n"); 108 printf("Version %s\n", PACKAGE_VERSION); 109 printf("BSD licensed, see LICENSE in source package for details.\n"); 110 printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 111 exit(1); 112 } 113 114 /** determine if str is ip4 and put into reverse lookup format */ 115 static int 116 isip4(const char* nm, char** res) 117 { 118 struct in_addr addr; 119 /* ddd.ddd.ddd.ddd.in-addr.arpa. is less than 32 */ 120 char buf[32]; 121 if(inet_pton(AF_INET, nm, &addr) <= 0) { 122 return 0; 123 } 124 snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa", 125 (unsigned)((uint8_t*)&addr)[3], (unsigned)((uint8_t*)&addr)[2], 126 (unsigned)((uint8_t*)&addr)[1], (unsigned)((uint8_t*)&addr)[0]); 127 *res = strdup(buf); 128 return 1; 129 } 130 131 /** determine if str is ip6 and put into reverse lookup format */ 132 static int 133 isip6(const char* nm, char** res) 134 { 135 struct in6_addr addr; 136 /* [nibble.]{32}.ip6.arpa. is less than 128 */ 137 const char* hex = "0123456789abcdef"; 138 char buf[128]; 139 char *p; 140 int i; 141 if(inet_pton(AF_INET6, nm, &addr) <= 0) { 142 return 0; 143 } 144 p = buf; 145 for(i=15; i>=0; i--) { 146 uint8_t b = ((uint8_t*)&addr)[i]; 147 *p++ = hex[ (b&0x0f) ]; 148 *p++ = '.'; 149 *p++ = hex[ (b&0xf0) >> 4 ]; 150 *p++ = '.'; 151 } 152 snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa"); 153 *res = strdup(buf); 154 if(!*res) { 155 fprintf(stderr, "error: out of memory\n"); 156 exit(1); 157 } 158 return 1; 159 } 160 161 /** massage input name */ 162 static char* 163 massage_qname(const char* nm, int* reverse) 164 { 165 /* recognise IP4 and IP6, create reverse addresses if needed */ 166 char* res; 167 if(isip4(nm, &res)) { 168 *reverse = 1; 169 } else if(isip6(nm, &res)) { 170 *reverse = 1; 171 } else { 172 res = strdup(nm); 173 } 174 if(!res) { 175 fprintf(stderr, "error: out of memory\n"); 176 exit(1); 177 } 178 return res; 179 } 180 181 /** massage input type */ 182 static int 183 massage_type(const char* t, int reverse, int* multi) 184 { 185 if(t) { 186 int r = sldns_get_rr_type_by_name(t); 187 if(r == 0 && strcasecmp(t, "TYPE0") != 0 && 188 strcmp(t, "") != 0) { 189 fprintf(stderr, "error unknown type %s\n", t); 190 exit(1); 191 } 192 return r; 193 } 194 if(!t && reverse) 195 return LDNS_RR_TYPE_PTR; 196 *multi = 1; 197 return LDNS_RR_TYPE_A; 198 } 199 200 /** massage input class */ 201 static int 202 massage_class(const char* c) 203 { 204 if(c) { 205 int r = sldns_get_rr_class_by_name(c); 206 if(r == 0 && strcasecmp(c, "CLASS0") != 0 && 207 strcmp(c, "") != 0) { 208 fprintf(stderr, "error unknown class %s\n", c); 209 exit(1); 210 } 211 return r; 212 } 213 return LDNS_RR_CLASS_IN; 214 } 215 216 /** nice security status string */ 217 static const char* 218 secure_str(struct ub_result* result) 219 { 220 if(result->rcode != 0 && result->rcode != 3) return "(error)"; 221 if(result->secure) return "(secure)"; 222 if(result->bogus) return "(BOGUS (security failure))"; 223 return "(insecure)"; 224 } 225 226 /** nice string for type */ 227 static void 228 pretty_type(char* s, size_t len, int t) 229 { 230 char d[16]; 231 sldns_wire2str_type_buf((uint16_t)t, d, sizeof(d)); 232 snprintf(s, len, "%s", d); 233 } 234 235 /** nice string for class */ 236 static void 237 pretty_class(char* s, size_t len, int c) 238 { 239 char d[16]; 240 sldns_wire2str_class_buf((uint16_t)c, d, sizeof(d)); 241 snprintf(s, len, "%s", d); 242 } 243 244 /** nice string for rcode */ 245 static void 246 pretty_rcode(char* s, size_t len, int r) 247 { 248 char d[16]; 249 sldns_wire2str_rcode_buf(r, d, sizeof(d)); 250 snprintf(s, len, "%s", d); 251 } 252 253 /** convert and print rdata */ 254 static void 255 print_rd(int t, char* data, size_t len) 256 { 257 char s[65535]; 258 sldns_wire2str_rdata_buf((uint8_t*)data, len, s, sizeof(s), (uint16_t)t); 259 printf(" %s", s); 260 } 261 262 /** pretty line of RR data for results */ 263 static void 264 pretty_rdata(char* q, char* cstr, char* tstr, int t, const char* sec, 265 char* data, size_t len) 266 { 267 printf("%s", q); 268 if(strcmp(cstr, "IN") != 0) 269 printf(" in class %s", cstr); 270 if(t == LDNS_RR_TYPE_A) 271 printf(" has address"); 272 else if(t == LDNS_RR_TYPE_AAAA) 273 printf(" has IPv6 address"); 274 else if(t == LDNS_RR_TYPE_MX) 275 printf(" mail is handled by"); 276 else if(t == LDNS_RR_TYPE_PTR) 277 printf(" domain name pointer"); 278 else printf(" has %s record", tstr); 279 print_rd(t, data, len); 280 if(verb > 0) 281 printf(" %s", sec); 282 printf("\n"); 283 } 284 285 /** pretty line of output for results */ 286 static void 287 pretty_output(char* q, int t, int c, struct ub_result* result, int docname) 288 { 289 int i; 290 const char *secstatus = secure_str(result); 291 char tstr[16]; 292 char cstr[16]; 293 char rcodestr[16]; 294 pretty_type(tstr, 16, t); 295 pretty_class(cstr, 16, c); 296 pretty_rcode(rcodestr, 16, result->rcode); 297 298 if(!result->havedata && result->rcode) { 299 printf("Host %s not found: %d(%s).", 300 q, result->rcode, rcodestr); 301 if(verb > 0) 302 printf(" %s", secstatus); 303 printf("\n"); 304 if(result->bogus && result->why_bogus) 305 printf("%s\n", result->why_bogus); 306 return; 307 } 308 if(docname && result->canonname && 309 result->canonname != result->qname) { 310 printf("%s is an alias for %s", result->qname, 311 result->canonname); 312 if(verb > 0) 313 printf(" %s", secstatus); 314 printf("\n"); 315 } 316 /* remove trailing . from long canonnames for nicer output */ 317 if(result->canonname && strlen(result->canonname) > 1 && 318 result->canonname[strlen(result->canonname)-1] == '.') 319 result->canonname[strlen(result->canonname)-1] = 0; 320 if(!result->havedata) { 321 if(verb > 0) { 322 printf("%s", result->canonname?result->canonname:q); 323 if(strcmp(cstr, "IN") != 0) 324 printf(" in class %s", cstr); 325 if(t == LDNS_RR_TYPE_A) 326 printf(" has no address"); 327 else if(t == LDNS_RR_TYPE_AAAA) 328 printf(" has no IPv6 address"); 329 else if(t == LDNS_RR_TYPE_PTR) 330 printf(" has no domain name ptr"); 331 else if(t == LDNS_RR_TYPE_MX) 332 printf(" has no mail handler record"); 333 else if(t == LDNS_RR_TYPE_ANY) { 334 char* s = sldns_wire2str_pkt( 335 result->answer_packet, 336 (size_t)result->answer_len); 337 if(!s) { 338 fprintf(stderr, "alloc failure\n"); 339 exit(1); 340 } 341 printf("%s\n", s); 342 } else printf(" has no %s record", tstr); 343 printf(" %s\n", secstatus); 344 } 345 /* else: emptiness to indicate no data */ 346 if(result->bogus && result->why_bogus) 347 printf("%s\n", result->why_bogus); 348 return; 349 } 350 i=0; 351 while(result->data[i]) 352 { 353 pretty_rdata( 354 result->canonname?result->canonname:q, 355 cstr, tstr, t, secstatus, result->data[i], 356 (size_t)result->len[i]); 357 i++; 358 } 359 if(result->bogus && result->why_bogus) 360 printf("%s\n", result->why_bogus); 361 } 362 363 /** perform a lookup and printout return if domain existed */ 364 static int 365 dnslook(struct ub_ctx* ctx, char* q, int t, int c, int docname) 366 { 367 int ret; 368 struct ub_result* result; 369 370 ret = ub_resolve(ctx, q, t, c, &result); 371 if(ret != 0) { 372 fprintf(stderr, "resolve error: %s\n", ub_strerror(ret)); 373 exit(1); 374 } 375 pretty_output(q, t, c, result, docname); 376 ret = result->nxdomain; 377 ub_resolve_free(result); 378 return ret; 379 } 380 381 /** perform host lookup */ 382 static void 383 lookup(struct ub_ctx* ctx, const char* nm, const char* qt, const char* qc) 384 { 385 /* massage input into a query name, type and class */ 386 int multi = 0; /* no type, so do A, AAAA, MX */ 387 int reverse = 0; /* we are doing a reverse lookup */ 388 char* realq = massage_qname(nm, &reverse); 389 int t = massage_type(qt, reverse, &multi); 390 int c = massage_class(qc); 391 392 /* perform the query */ 393 if(multi) { 394 if(!dnslook(ctx, realq, LDNS_RR_TYPE_A, c, 1)) { 395 /* domain exists, lookup more */ 396 (void)dnslook(ctx, realq, LDNS_RR_TYPE_AAAA, c, 0); 397 (void)dnslook(ctx, realq, LDNS_RR_TYPE_MX, c, 0); 398 } 399 } else { 400 (void)dnslook(ctx, realq, t, c, 1); 401 } 402 ub_ctx_delete(ctx); 403 free(realq); 404 } 405 406 /** print error if any */ 407 static void 408 check_ub_res(int r) 409 { 410 if(r != 0) { 411 fprintf(stderr, "error: %s\n", ub_strerror(r)); 412 exit(1); 413 } 414 } 415 416 /** getopt global, in case header files fail to declare it. */ 417 extern int optind; 418 /** getopt global, in case header files fail to declare it. */ 419 extern char* optarg; 420 421 /** Main routine for unbound-host */ 422 int main(int argc, char* argv[]) 423 { 424 int c; 425 char* qclass = NULL; 426 char* qtype = NULL; 427 struct ub_ctx* ctx = NULL; 428 int debuglevel = 0; 429 430 ctx = ub_ctx_create(); 431 if(!ctx) { 432 fprintf(stderr, "error: out of memory\n"); 433 exit(1); 434 } 435 /* no need to fetch additional targets, we only do few lookups */ 436 check_ub_res(ub_ctx_set_option(ctx, "target-fetch-policy:", "0 0 0 0 0")); 437 438 /* parse the options */ 439 while( (c=getopt(argc, argv, "46DF:c:df:hrt:vy:C:")) != -1) { 440 switch(c) { 441 case '4': 442 check_ub_res(ub_ctx_set_option(ctx, "do-ip6:", "no")); 443 break; 444 case '6': 445 check_ub_res(ub_ctx_set_option(ctx, "do-ip4:", "no")); 446 break; 447 case 'c': 448 qclass = optarg; 449 break; 450 case 'C': 451 check_ub_res(ub_ctx_config(ctx, optarg)); 452 break; 453 case 'D': 454 check_ub_res(ub_ctx_add_ta_file(ctx, ROOT_ANCHOR_FILE)); 455 break; 456 case 'd': 457 debuglevel++; 458 if(debuglevel < 2) 459 debuglevel = 2; /* at least VERB_DETAIL */ 460 break; 461 case 'r': 462 check_ub_res(ub_ctx_resolvconf(ctx, "/etc/resolv.conf")); 463 break; 464 case 't': 465 qtype = optarg; 466 break; 467 case 'v': 468 verb++; 469 break; 470 case 'y': 471 check_ub_res(ub_ctx_add_ta(ctx, optarg)); 472 break; 473 case 'f': 474 check_ub_res(ub_ctx_add_ta_file(ctx, optarg)); 475 break; 476 case 'F': 477 check_ub_res(ub_ctx_trustedkeys(ctx, optarg)); 478 break; 479 case '?': 480 case 'h': 481 default: 482 usage(); 483 } 484 } 485 if(debuglevel != 0) /* set after possible -C options */ 486 check_ub_res(ub_ctx_debuglevel(ctx, debuglevel)); 487 if(ub_ctx_get_option(ctx, "use-syslog", &optarg) == 0) { 488 if(strcmp(optarg, "yes") == 0) /* disable use-syslog */ 489 check_ub_res(ub_ctx_set_option(ctx, 490 "use-syslog:", "no")); 491 free(optarg); 492 } 493 argc -= optind; 494 argv += optind; 495 if(argc != 1) 496 usage(); 497 498 #ifdef HAVE_SSL 499 #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS 500 ERR_load_crypto_strings(); 501 #endif 502 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 503 ERR_load_SSL_strings(); 504 #endif 505 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) 506 OpenSSL_add_all_algorithms(); 507 #else 508 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS 509 | OPENSSL_INIT_ADD_ALL_DIGESTS 510 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 511 #endif 512 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 513 (void)SSL_library_init(); 514 #else 515 (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); 516 #endif 517 #endif /* HAVE_SSL */ 518 #ifdef HAVE_NSS 519 if(NSS_NoDB_Init(".") != SECSuccess) { 520 fprintf(stderr, "could not init NSS\n"); 521 return 1; 522 } 523 #endif 524 lookup(ctx, argv[0], qtype, qclass); 525 return 0; 526 } 527