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