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->rcode != 0 && result->rcode != 3) return "(error)"; 213 if(result->secure) return "(secure)"; 214 if(result->bogus) return "(BOGUS (security failure))"; 215 return "(insecure)"; 216 } 217 218 /** nice string for type */ 219 static void 220 pretty_type(char* s, size_t len, int t) 221 { 222 char d[16]; 223 sldns_wire2str_type_buf((uint16_t)t, d, sizeof(d)); 224 snprintf(s, len, "%s", d); 225 } 226 227 /** nice string for class */ 228 static void 229 pretty_class(char* s, size_t len, int c) 230 { 231 char d[16]; 232 sldns_wire2str_class_buf((uint16_t)c, d, sizeof(d)); 233 snprintf(s, len, "%s", d); 234 } 235 236 /** nice string for rcode */ 237 static void 238 pretty_rcode(char* s, size_t len, int r) 239 { 240 char d[16]; 241 sldns_wire2str_rcode_buf(r, d, sizeof(d)); 242 snprintf(s, len, "%s", d); 243 } 244 245 /** convert and print rdata */ 246 static void 247 print_rd(int t, char* data, size_t len) 248 { 249 char s[65535]; 250 sldns_wire2str_rdata_buf((uint8_t*)data, len, s, sizeof(s), (uint16_t)t); 251 printf(" %s", s); 252 } 253 254 /** pretty line of RR data for results */ 255 static void 256 pretty_rdata(char* q, char* cstr, char* tstr, int t, const char* sec, 257 char* data, size_t len) 258 { 259 printf("%s", q); 260 if(strcmp(cstr, "IN") != 0) 261 printf(" in class %s", cstr); 262 if(t == LDNS_RR_TYPE_A) 263 printf(" has address"); 264 else if(t == LDNS_RR_TYPE_AAAA) 265 printf(" has IPv6 address"); 266 else if(t == LDNS_RR_TYPE_MX) 267 printf(" mail is handled by"); 268 else if(t == LDNS_RR_TYPE_PTR) 269 printf(" domain name pointer"); 270 else printf(" has %s record", tstr); 271 print_rd(t, data, len); 272 if(verb > 0) 273 printf(" %s", sec); 274 printf("\n"); 275 } 276 277 /** pretty line of output for results */ 278 static void 279 pretty_output(char* q, int t, int c, struct ub_result* result, int docname) 280 { 281 int i; 282 const char *secstatus = secure_str(result); 283 char tstr[16]; 284 char cstr[16]; 285 char rcodestr[16]; 286 pretty_type(tstr, 16, t); 287 pretty_class(cstr, 16, c); 288 pretty_rcode(rcodestr, 16, result->rcode); 289 290 if(!result->havedata && result->rcode) { 291 printf("Host %s not found: %d(%s).", 292 q, result->rcode, rcodestr); 293 if(verb > 0) 294 printf(" %s", secstatus); 295 printf("\n"); 296 if(result->bogus && result->why_bogus) 297 printf("%s\n", result->why_bogus); 298 return; 299 } 300 if(docname && result->canonname && 301 result->canonname != result->qname) { 302 printf("%s is an alias for %s", result->qname, 303 result->canonname); 304 if(verb > 0) 305 printf(" %s", secstatus); 306 printf("\n"); 307 } 308 /* remove trailing . from long canonnames for nicer output */ 309 if(result->canonname && strlen(result->canonname) > 1 && 310 result->canonname[strlen(result->canonname)-1] == '.') 311 result->canonname[strlen(result->canonname)-1] = 0; 312 if(!result->havedata) { 313 if(verb > 0) { 314 printf("%s", result->canonname?result->canonname:q); 315 if(strcmp(cstr, "IN") != 0) 316 printf(" in class %s", cstr); 317 if(t == LDNS_RR_TYPE_A) 318 printf(" has no address"); 319 else if(t == LDNS_RR_TYPE_AAAA) 320 printf(" has no IPv6 address"); 321 else if(t == LDNS_RR_TYPE_PTR) 322 printf(" has no domain name ptr"); 323 else if(t == LDNS_RR_TYPE_MX) 324 printf(" has no mail handler record"); 325 else if(t == LDNS_RR_TYPE_ANY) { 326 char* s = sldns_wire2str_pkt( 327 result->answer_packet, 328 (size_t)result->answer_len); 329 if(!s) { 330 fprintf(stderr, "alloc failure\n"); 331 exit(1); 332 } 333 printf("%s\n", s); 334 } else printf(" has no %s record", tstr); 335 printf(" %s\n", secstatus); 336 } 337 /* else: emptiness to indicate no data */ 338 if(result->bogus && result->why_bogus) 339 printf("%s\n", result->why_bogus); 340 return; 341 } 342 i=0; 343 while(result->data[i]) 344 { 345 pretty_rdata( 346 result->canonname?result->canonname:q, 347 cstr, tstr, t, secstatus, result->data[i], 348 (size_t)result->len[i]); 349 i++; 350 } 351 if(result->bogus && result->why_bogus) 352 printf("%s\n", result->why_bogus); 353 } 354 355 /** perform a lookup and printout return if domain existed */ 356 static int 357 dnslook(struct ub_ctx* ctx, char* q, int t, int c, int docname) 358 { 359 int ret; 360 struct ub_result* result; 361 362 ret = ub_resolve(ctx, q, t, c, &result); 363 if(ret != 0) { 364 fprintf(stderr, "resolve error: %s\n", ub_strerror(ret)); 365 exit(1); 366 } 367 pretty_output(q, t, c, result, docname); 368 ret = result->nxdomain; 369 ub_resolve_free(result); 370 return ret; 371 } 372 373 /** perform host lookup */ 374 static void 375 lookup(struct ub_ctx* ctx, const char* nm, const char* qt, const char* qc) 376 { 377 /* massage input into a query name, type and class */ 378 int multi = 0; /* no type, so do A, AAAA, MX */ 379 int reverse = 0; /* we are doing a reverse lookup */ 380 char* realq = massage_qname(nm, &reverse); 381 int t = massage_type(qt, reverse, &multi); 382 int c = massage_class(qc); 383 384 /* perform the query */ 385 if(multi) { 386 if(!dnslook(ctx, realq, LDNS_RR_TYPE_A, c, 1)) { 387 /* domain exists, lookup more */ 388 (void)dnslook(ctx, realq, LDNS_RR_TYPE_AAAA, c, 0); 389 (void)dnslook(ctx, realq, LDNS_RR_TYPE_MX, c, 0); 390 } 391 } else { 392 (void)dnslook(ctx, realq, t, c, 1); 393 } 394 ub_ctx_delete(ctx); 395 free(realq); 396 } 397 398 /** print error if any */ 399 static void 400 check_ub_res(int r) 401 { 402 if(r != 0) { 403 fprintf(stderr, "error: %s\n", ub_strerror(r)); 404 exit(1); 405 } 406 } 407 408 /** getopt global, in case header files fail to declare it. */ 409 extern int optind; 410 /** getopt global, in case header files fail to declare it. */ 411 extern char* optarg; 412 413 /** Main routine for unbound-host */ 414 int main(int argc, char* argv[]) 415 { 416 int c; 417 char* qclass = NULL; 418 char* qtype = NULL; 419 struct ub_ctx* ctx = NULL; 420 int debuglevel = 0; 421 422 ctx = ub_ctx_create(); 423 if(!ctx) { 424 fprintf(stderr, "error: out of memory\n"); 425 exit(1); 426 } 427 /* no need to fetch additional targets, we only do few lookups */ 428 check_ub_res(ub_ctx_set_option(ctx, "target-fetch-policy:", "0 0 0 0 0")); 429 430 /* parse the options */ 431 while( (c=getopt(argc, argv, "46DF:c:df:hrt:vy:C:")) != -1) { 432 switch(c) { 433 case '4': 434 check_ub_res(ub_ctx_set_option(ctx, "do-ip6:", "no")); 435 break; 436 case '6': 437 check_ub_res(ub_ctx_set_option(ctx, "do-ip4:", "no")); 438 break; 439 case 'c': 440 qclass = optarg; 441 break; 442 case 'C': 443 check_ub_res(ub_ctx_config(ctx, optarg)); 444 break; 445 case 'D': 446 check_ub_res(ub_ctx_add_ta_file(ctx, ROOT_ANCHOR_FILE)); 447 break; 448 case 'd': 449 debuglevel++; 450 if(debuglevel < 2) 451 debuglevel = 2; /* at least VERB_DETAIL */ 452 break; 453 case 'r': 454 check_ub_res(ub_ctx_resolvconf(ctx, "/etc/resolv.conf")); 455 break; 456 case 't': 457 qtype = optarg; 458 break; 459 case 'v': 460 verb++; 461 break; 462 case 'y': 463 check_ub_res(ub_ctx_add_ta(ctx, optarg)); 464 break; 465 case 'f': 466 check_ub_res(ub_ctx_add_ta_file(ctx, optarg)); 467 break; 468 case 'F': 469 check_ub_res(ub_ctx_trustedkeys(ctx, optarg)); 470 break; 471 case '?': 472 case 'h': 473 default: 474 usage(); 475 } 476 } 477 if(debuglevel != 0) /* set after possible -C options */ 478 check_ub_res(ub_ctx_debuglevel(ctx, debuglevel)); 479 if(ub_ctx_get_option(ctx, "use-syslog", &optarg) == 0) { 480 if(strcmp(optarg, "yes") == 0) /* disable use-syslog */ 481 check_ub_res(ub_ctx_set_option(ctx, 482 "use-syslog:", "no")); 483 free(optarg); 484 } 485 argc -= optind; 486 argv += optind; 487 if(argc != 1) 488 usage(); 489 490 #ifdef HAVE_NSS 491 if(NSS_NoDB_Init(".") != SECSuccess) { 492 fprintf(stderr, "could not init NSS\n"); 493 return 1; 494 } 495 #endif 496 lookup(ctx, argv[0], qtype, qclass); 497 return 0; 498 } 499