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