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