dinoco.c (25472B)
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <stdbool.h> 5 #include <stdint.h> 6 #include <unistd.h> 7 #include <getopt.h> 8 #include <ctype.h> 9 #include <sys/socket.h> 10 #include <netdb.h> 11 #include <arpa/inet.h> 12 #include <inttypes.h> 13 #include "dinoco.h" 14 15 static struct DnsHeader dns_header_default = { 16 .id = 55, 17 .qr = false, 18 .opcode = OPCODE_QUERY, 19 .aa = false, 20 .tc = false, 21 .rd = true, 22 .ra = false, 23 .rcode = RCODE_NO_ERROR, 24 .qdcount = 1, 25 .ancount = 0, 26 .nscount = 0, 27 .arcount = 0 28 }; 29 30 static void 31 byte_array_free(struct ByteArray *bytes) 32 { 33 free(bytes->b); 34 free(bytes); 35 } 36 37 static void 38 resource_record_free(struct DnsResourceRecord *record) 39 { 40 free(record->domain); 41 byte_array_free(record->rdata); 42 free(record); 43 } 44 45 static char * 46 str_cat(const char *str1, const char *str2) 47 { 48 char *new, *n; 49 const char *c; 50 new = malloc((strlen(str1)+strlen(str2)+1) * sizeof(char)); 51 n = new; 52 for (c = str1; *c; c++, n++) { 53 *n = *c; 54 } 55 for (c = str2; *c; c++, n++) { 56 *n = *c; 57 } 58 *n = 0; 59 return new; 60 } 61 62 static char * 63 str_to_upper(const char *str) 64 { 65 char *new, *n; 66 const char *c; 67 new = malloc((strlen(str)+1) * sizeof(char)); 68 for (c = str, n = new; *c; c++, n++) { 69 *n = toupper(*c); 70 } 71 *n = 0; 72 return new; 73 } 74 75 static const char * 76 response_code_to_string(enum ResponseCode code) 77 { 78 switch (code) { 79 case RCODE_NO_ERROR: 80 return "No error condition"; 81 case RCODE_FORMAT_ERROR: 82 return "Format Error"; 83 case RCODE_SERVER_FAILURE: 84 return "Server Failure"; 85 case RCODE_NAME_ERROR: 86 return "Name Error"; 87 case RCODE_NOT_IMPLEMENTED: 88 return "Not Implemented"; 89 case RCODE_REFUSED: 90 return "Refused"; 91 } 92 return ""; 93 } 94 95 static struct addrinfo *get_addr_info(const char *host) 96 { 97 struct addrinfo *addr_info, hints; 98 memset(&hints, 0, sizeof hints); 99 hints.ai_family = AF_INET; 100 hints.ai_socktype = SOCK_DGRAM; 101 hints.ai_flags = AI_NUMERICSERV; 102 hints.ai_protocol = 0; 103 int ret = getaddrinfo(host, "53", &hints, &addr_info); 104 if (ret != 0) { 105 fprintf(stderr, "getaddrinfo failed. %s.\n", gai_strerror(ret)); 106 return NULL; 107 } 108 return addr_info; 109 } 110 111 static enum Type 112 type_parse(const char *str, bool *error) 113 { 114 *error = false; 115 if (!strcmp(str, "A")) { 116 return TYPE_A; 117 } else 118 if (!strcmp(str, "NS")) { 119 return TYPE_NS; 120 } else 121 if (!strcmp(str, "MD")) { 122 return TYPE_MD; 123 } else 124 if (!strcmp(str, "MF")) { 125 return TYPE_MF; 126 } else 127 if (!strcmp(str, "CNAME")) { 128 return TYPE_CNAME; 129 } else 130 if (!strcmp(str, "SOA")) { 131 return TYPE_SOA; 132 } else 133 if (!strcmp(str, "MB")) { 134 return TYPE_MB; 135 } else 136 if (!strcmp(str, "MG")) { 137 return TYPE_MG; 138 } else 139 if (!strcmp(str, "MR")) { 140 return TYPE_MR; 141 } else 142 if (!strcmp(str, "NULL")) { 143 return TYPE_NULL; 144 } else 145 if (!strcmp(str, "WKS")) { 146 return TYPE_WKS; 147 } else 148 if (!strcmp(str, "PTR")) { 149 return TYPE_PTR; 150 } else 151 if (!strcmp(str, "HINFO")) { 152 return TYPE_HINFO; 153 } else 154 if (!strcmp(str, "MINFO")) { 155 return TYPE_MINFO; 156 } else 157 if (!strcmp(str, "MX")) { 158 return TYPE_MX; 159 } else 160 if (!strcmp(str, "TXT")) { 161 return TYPE_TXT; 162 } else 163 if (!strcmp(str, "AAAA")) { 164 return TYPE_AAAA; 165 } 166 *error = true; 167 return TYPE_A; // unused 168 } 169 170 // e.g. "domain.com" -> 6, 'd', 'o', 'm', 'a', 'i', 'n', 3, 'c', 'o', 'm', 0 171 static struct ByteArray * 172 domain_form(const char *str) 173 { 174 struct ByteArray *bytes; 175 char *domain = NULL; 176 int d = 0; 177 int k, i; 178 int char_count = 0; 179 for (i = 0; str[i]; i++) { 180 if (str[i] == '.') { 181 domain = realloc(domain, (d+char_count+1) * sizeof(char)); 182 domain[d] = char_count; 183 d++; 184 for (k = i-char_count; k<i; k++, d++) { 185 domain[d] = str[k]; 186 } 187 char_count = 0; 188 } else { 189 char_count++; 190 } 191 } 192 domain = realloc(domain, (d+char_count+2) * sizeof(char)); 193 domain[d] = char_count; 194 d++; 195 for (k = i-char_count; k<i; k++, d++) { 196 domain[d] = str[k]; 197 } 198 domain[d] = 0; 199 bytes = malloc(sizeof(struct ByteArray)); 200 bytes->b = (uint8_t *)domain; 201 bytes->len = d + 1; 202 return bytes; 203 } 204 205 static char * 206 domain_parse(const struct ByteArray *bytes, int start) 207 { 208 char *merged_domain, *string; 209 char *domain = NULL; 210 int offset, len; 211 int d = 0; 212 int i = start; 213 do { 214 // if pointer 215 if (bytes->b[i] & 0b11000000) { 216 offset = bytes->b[i+1]; 217 string = domain_parse(bytes, offset); 218 domain = realloc(domain, (d+1) * sizeof(char)); 219 domain[d] = 0; 220 merged_domain = str_cat(domain, string); 221 free(domain); 222 free(string); 223 return merged_domain; 224 } 225 len = bytes->b[i]; 226 domain = realloc(domain, (d+len) * sizeof(char)); 227 memcpy(&domain[d], &bytes->b[i+1], len); 228 d += len; 229 i += len + 1; 230 if (bytes->b[i]) { 231 domain = realloc(domain, (d+1) * sizeof(char)); 232 domain[d] = '.'; 233 d++; 234 } 235 } while (bytes->b[i]); 236 domain = realloc(domain, (d+1) * sizeof(char)); 237 domain[d] = 0; 238 return domain; 239 } 240 241 static char * 242 ipv4_parse(const uint8_t data[4]) 243 { 244 char *ipv4 = NULL; 245 char number[4]; 246 uint8_t ip_part; 247 int i = 0; 248 int k, char_wrote; 249 for (k = 0; k<4; k++) { 250 ip_part = data[k] & 0xFF; 251 char_wrote = snprintf(number, 4, "%d", ip_part); 252 ipv4 = realloc(ipv4, (i+char_wrote+1) * sizeof(char)); 253 strcpy(&ipv4[i], number); 254 i += char_wrote; 255 if (k != 3) { 256 ipv4 = realloc(ipv4, (i+1) * sizeof(char)); 257 ipv4[i] = '.'; 258 i++; 259 } 260 } 261 return ipv4; 262 } 263 264 static char * 265 ipv6_parse(const uint8_t data[16]) 266 { 267 char *ipv6 = malloc(40 * sizeof(char)); 268 int i = 0; 269 char number[3]; 270 uint8_t ip_part; 271 int k; 272 for (k = 0; k<16; k++) { 273 ip_part = data[k] & 0xFF; 274 snprintf(number, 3, "%02x", ip_part); 275 memcpy(&ipv6[i], number, 2); 276 i += 2; 277 if (k % 2 != 0 && k != 15) { 278 ipv6[i] = ':'; 279 i++; 280 } 281 } 282 ipv6[i] = 0; 283 return ipv6; 284 } 285 286 static char * 287 char_string_parse(uint8_t *data, int start) 288 { 289 int len = data[start]; 290 char *str = malloc((len+1) * sizeof(char)); 291 memcpy(str, &data[start+1], len); 292 str[len] = 0; 293 return str; 294 } 295 296 static struct ByteArray * 297 header_form(struct DnsHeader *h) 298 { 299 struct ByteArray *header = malloc(sizeof(struct ByteArray)); 300 header->b = malloc(DNS_HEADER_LENGTH * sizeof(char)); 301 header->len = DNS_HEADER_LENGTH; 302 header->b[0] = (h->id >> 8) & 0xFF; 303 header->b[1] = h->id & 0xFF; 304 char flag_one = 0; 305 if (h->qr) 306 flag_one = flag_one | 0b10000000; 307 switch (h->opcode) 308 { 309 case OPCODE_QUERY: 310 // The bit we would set to zero is already zero 311 break; 312 case OPCODE_IQUERY: 313 flag_one = flag_one | 0b10001000; 314 break; 315 case OPCODE_STATUS: 316 flag_one = flag_one | 0b10010000; 317 break; 318 } 319 if (h->aa) 320 flag_one = flag_one | 0b00000100; 321 if (h->tc) 322 flag_one = flag_one | 0b00000010; 323 if (h->rd) 324 flag_one = flag_one | 0b00000001; 325 header->b[2] = flag_one; 326 char flag_two = 0; 327 if (h->ra) 328 flag_two = flag_two | 0b10000000; 329 header->b[3] = flag_two; 330 header->b[4] = (h->qdcount >> 8) & 0xFF; 331 header->b[5] = h->qdcount & 0xFF; 332 header->b[6] = (h->ancount >> 8) & 0xFF; 333 header->b[7] = h->ancount & 0xFF; 334 header->b[8] = (h->nscount >> 8) & 0xFF; 335 header->b[9] = h->nscount & 0xFF; 336 header->b[10] = (h->arcount >> 8) & 0xFF; 337 header->b[11] = h->arcount & 0xFF; 338 return header; 339 } 340 341 static struct DnsHeader * 342 header_parse(const uint8_t *res) 343 { 344 struct DnsHeader *header = malloc(sizeof(struct DnsHeader)); 345 header->id = res[1] + 256U*res[0]; 346 header->qr = res[2] & 0b10000000 ? true : false; 347 if (res[2] & 0b00001000) { 348 header->opcode = OPCODE_IQUERY; 349 } else 350 if (res[2] & 0b00010000) { 351 header->opcode = OPCODE_STATUS; 352 } else { 353 header->opcode = OPCODE_QUERY; 354 } 355 header->aa = res[2] & 0b00000100 ? true : false; 356 header->tc = res[2] & 0b00000010 ? true : false; 357 header->rd = res[2] & 0b00000001 ? true : false; 358 header->ra = res[3] & 0b10000000 ? true : false; 359 int rcode = 0; 360 if (res[3] & 0b00000001) 361 rcode++; 362 if (res[3] & 0b00000010) 363 rcode += 2; 364 if (res[3] & 0b00000100) 365 rcode += 4; 366 header->rcode = rcode; 367 header->qdcount = res[5] + 256U * res[4]; 368 header->ancount = res[7] + 256U * res[6]; 369 header->nscount = res[9] + 256U * res[8]; 370 header->arcount = res[11] + 256U * res[10]; 371 return header; 372 } 373 374 static struct ByteArray * 375 question_form(struct DnsQuestion *qu) 376 { 377 struct ByteArray *question = malloc(sizeof(struct ByteArray)); 378 struct ByteArray *domain = domain_form(qu->domain); 379 question->b = malloc((domain->len+4) * sizeof(char)); 380 question->len = domain->len+4; 381 memcpy(question->b, domain->b, domain->len); 382 question->b[domain->len] = 0x00; 383 question->b[domain->len+1] = qu->type; 384 question->b[domain->len+2] = 0x00; 385 question->b[domain->len+3] = qu->class; 386 byte_array_free(domain); 387 return question; 388 } 389 390 static int 391 name_count_bytes(struct ByteArray *bytes, int start) 392 { 393 int count = 0; 394 int i; 395 for (i = start; true; i++, count++) { 396 if (bytes->b[i] == 0) { 397 count++; 398 break; 399 } 400 // if pointer 401 if ((bytes->b[i] & 0xFF) == 0xC0) { 402 count += 2; 403 break; 404 } 405 } 406 return count; 407 } 408 409 static struct DnsResourceRecord * 410 answer_parse(struct ByteArray *res, size_t *start) 411 { 412 struct DnsResourceRecord *answer = malloc(sizeof(struct DnsResourceRecord)); 413 answer->domain = domain_parse(res, *start); 414 int name_len = name_count_bytes(res, *start); 415 int b = *start + name_len; 416 answer->type = res->b[b+1] & 0xFF; 417 answer->class = res->b[b+3] & 0xFF; 418 answer->ttl = (res->b[b+7] & 0xFF) 419 + 256U * (res->b[b+6] & 0xFF) 420 + 65536U * (res->b[b+5] & 0xFF) 421 + 16777216U * (res->b[b+4] & 0xFF); 422 answer->rdlength = (res->b[b+9] & 0xFF) + 256U * (res->b[b+8] & 0xFF); 423 answer->rdata = malloc(sizeof(struct ByteArray)); 424 answer->rdata->b = malloc(answer->rdlength * sizeof(char)); 425 answer->rdata->len = answer->rdlength; 426 memcpy(answer->rdata->b, &res->b[b+10], answer->rdlength); 427 *start += name_len + 10 + answer->rdlength; 428 return answer; 429 } 430 431 static union DnsTypeResult * 432 answer_parse_by_type( 433 struct DnsResourceRecord *answer, 434 struct ByteArray *res, 435 enum Type type, 436 size_t *start_of_next_answer 437 ) 438 { 439 union DnsTypeResult *ur = malloc(sizeof(union DnsTypeResult)); 440 switch (type) { 441 case TYPE_A: 442 ur->a = malloc(sizeof(struct DnsAResult)); 443 ur->a->ipv4 = ipv4_parse(answer->rdata->b); 444 break; 445 case TYPE_AAAA: 446 ur->aaaa = malloc(sizeof(struct DnsAaaaResult)); 447 ur->aaaa->ipv6 = ipv6_parse(answer->rdata->b); 448 break; 449 case TYPE_NS: 450 ur->ns = malloc(sizeof(struct DnsNsResult)); 451 int start_of_domain = *start_of_next_answer - answer->rdlength; 452 ur->ns->domain = domain_parse(res, start_of_domain); 453 break; 454 case TYPE_MD: 455 break; 456 case TYPE_MF: 457 break; 458 case TYPE_CNAME: 459 ur->cname = malloc(sizeof(struct DnsCnameResult)); 460 int start_of_canonical_domain = *start_of_next_answer - answer->rdlength; 461 ur->cname->domain = domain_parse(res, start_of_canonical_domain); 462 break; 463 case TYPE_SOA: 464 ur->soa = malloc(sizeof(struct DnsSoaResult)); 465 int start = *start_of_next_answer - answer->rdlength; 466 ur->soa->mname = domain_parse(res, start); 467 int mname_len = name_count_bytes(res, start); 468 start += mname_len; 469 ur->soa->rname = domain_parse(res, start); 470 int rname_len = name_count_bytes(res, start); 471 start += rname_len; 472 ur->soa->serial = ((uint32_t)(res->b[start] & 0xFF) << 24) 473 + ((uint32_t)(res->b[start+1] & 0xFF) << 16) 474 + ((uint32_t)(res->b[start+2] & 0xFF) << 8) 475 + (uint32_t)(res->b[start+3] & 0xFF); 476 start += 4; 477 ur->soa->refresh = ((uint32_t)(res->b[start] & 0xFF) << 24) 478 + ((uint32_t)(res->b[start+1] & 0xFF) << 16) 479 + ((uint32_t)(res->b[start+2] & 0xFF) << 8) 480 + (uint32_t)(res->b[start+3] & 0xFF); 481 start += 4; 482 ur->soa->retry = ((uint32_t)(res->b[start] & 0xFF) << 24) 483 + ((uint32_t)(res->b[start+1] & 0xFF) << 16) 484 + ((uint32_t)(res->b[start+2] & 0xFF) << 8) 485 + (uint32_t)(res->b[start+3] & 0xFF); 486 start += 4; 487 ur->soa->expire = ((uint32_t)(res->b[start] & 0xFF) << 24) 488 + ((uint32_t)(res->b[start+1] & 0xFF) << 16) 489 + ((uint32_t)(res->b[start+2] & 0xFF) << 8) 490 + (uint32_t)(res->b[start+3] & 0xFF); 491 start += 4; 492 ur->soa->minimum = ((uint32_t)(res->b[start] & 0xFF) << 24) 493 + ((uint32_t)(res->b[start+1] & 0xFF) << 16) 494 + ((uint32_t)(res->b[start+2] & 0xFF) << 8) 495 + (uint32_t)(res->b[start+3] & 0xFF); 496 break; 497 case TYPE_MB: 498 break; 499 case TYPE_MG: 500 break; 501 case TYPE_MR: 502 break; 503 case TYPE_NULL: 504 break; 505 case TYPE_WKS: 506 break; 507 case TYPE_PTR: 508 ur->ptr = malloc(sizeof(struct DnsPtrResult)); 509 int start_of_ptr_domain = *start_of_next_answer - answer->rdlength; 510 ur->ptr->domain = domain_parse(res, start_of_ptr_domain); 511 break; 512 case TYPE_HINFO: 513 ur->hinfo = malloc(sizeof(struct DnsHinfoResult)); 514 ur->hinfo->cpu = char_string_parse(answer->rdata->b, 0); 515 int start_of_os = answer->rdata->b[0] + 1; 516 ur->hinfo->os = char_string_parse(answer->rdata->b, start_of_os); 517 break; 518 case TYPE_MINFO: 519 break; 520 case TYPE_MX: { 521 uint16_t preference = answer->rdata->b[1] 522 + 256U * answer->rdata->b[0]; 523 int start_of_mail_domain = *start_of_next_answer - answer->rdlength + 2; 524 ur->mx = malloc(sizeof(struct DnsMxResult)); 525 ur->mx->preference = preference; 526 ur->mx->mail_domain = domain_parse(res, start_of_mail_domain); 527 break; 528 } 529 case TYPE_TXT: { 530 int txt_len; 531 int txt_data_len = 0; 532 int b = 0; 533 ur->txt = malloc(sizeof(struct DnsTxtResult)); 534 ur->txt->data = NULL; 535 do { 536 txt_len = answer->rdata->b[b] & 0xFF; 537 txt_data_len += txt_len; 538 ur->txt->data = realloc(ur->txt->data, txt_data_len * sizeof(char)); 539 memcpy(&ur->txt->data[txt_data_len-txt_len], &answer->rdata->b[b+1], txt_len); 540 b += txt_len + 1; 541 } while (txt_len > 0); 542 ur->txt->data = realloc(ur->txt->data, (txt_data_len+1) * sizeof(char)); 543 ur->txt->data[txt_data_len] = 0; 544 break; 545 } 546 } 547 return ur; 548 } 549 550 static void 551 answers_print( 552 union DnsTypeResult **answers, 553 int answer_count, 554 enum Type type, 555 bool disable_header 556 ) 557 { 558 int i; 559 int len = 0; 560 switch (type) { 561 case TYPE_A: 562 if (!disable_header) { 563 printf(ANSI_COLOR_GREEN"Address\n"ANSI_COLOR_RESET); 564 } 565 for (i = 0; i < answer_count; i++) { 566 printf("%s\n", answers[i]->a->ipv4); 567 } 568 break; 569 case TYPE_AAAA: 570 if (!disable_header) { 571 printf(ANSI_COLOR_GREEN"AAAA Address\n"ANSI_COLOR_RESET); 572 } 573 for (i = 0; i<answer_count; i++) { 574 printf("%s\n", answers[i]->aaaa->ipv6); 575 } 576 break; 577 case TYPE_NS: 578 if (!disable_header) { 579 printf(ANSI_COLOR_GREEN"Authoritative Host\n"ANSI_COLOR_RESET); 580 } 581 for (i = 0; i<answer_count; i++) { 582 printf("%s\n", answers[i]->ns->domain); 583 } 584 break; 585 case TYPE_MD: 586 break; 587 case TYPE_MF: 588 break; 589 case TYPE_CNAME: 590 if (!disable_header) { 591 printf(ANSI_COLOR_GREEN"Canonical Domain\n"ANSI_COLOR_RESET); 592 } 593 for (i = 0; i<answer_count; i++) { 594 printf("%s\n", answers[i]->cname->domain); 595 } 596 break; 597 case TYPE_SOA: { 598 const char *mname_header = "Primary Name Server"; 599 const char *rname_header = "Responsible authority's mailbox"; 600 int mname_header_len = strlen(mname_header); 601 int rname_header_len = strlen(rname_header); 602 int longest_mname = 0; 603 int longest_rname = 0; 604 for (i = 0; i<answer_count; i++) { 605 len = strlen(answers[i]->soa->mname); 606 if (len > longest_mname) { 607 longest_mname = len; 608 } 609 len = strlen(answers[i]->soa->rname); 610 if (len > longest_rname) { 611 longest_rname = len; 612 } 613 } 614 if (longest_mname < mname_header_len) { 615 longest_mname = mname_header_len; 616 } 617 if (longest_rname < rname_header_len) { 618 longest_rname = rname_header_len; 619 } 620 if (!disable_header) { 621 printf(ANSI_COLOR_GREEN); 622 printf("%-*s\t", longest_mname, "Primary Name Server"); 623 printf("%-*s\t", longest_rname, "Responsible authority's mailbox"); 624 printf("%-13s\t", "Serial Number"); 625 printf("%-16s\t", "Refresh Interval"); 626 printf("%-14s\t", "Retry Interval"); 627 printf("%-12s\t", "Expire Limit"); 628 printf("%-11s\n", "Minimum TTL"); 629 printf(ANSI_COLOR_RESET); 630 } 631 for (i = 0; i<answer_count; i++) { 632 printf("%-*s\t", longest_mname, answers[i]->soa->mname); 633 printf("%-*s\t", longest_rname, answers[i]->soa->rname); 634 printf("%-13u\t", answers[i]->soa->serial); 635 printf("%-16u\t", answers[i]->soa->refresh); 636 printf("%-14u\t", answers[i]->soa->retry); 637 printf("%-12u\t", answers[i]->soa->expire); 638 printf("%-11u\n", answers[i]->soa->minimum); 639 } 640 break; 641 } 642 case TYPE_MB: 643 break; 644 case TYPE_MG: 645 break; 646 case TYPE_MR: 647 break; 648 case TYPE_NULL: 649 break; 650 case TYPE_WKS: 651 break; 652 case TYPE_PTR: 653 if (!disable_header) { 654 printf(ANSI_COLOR_GREEN"Domain Name\n"ANSI_COLOR_RESET); 655 } 656 for (i = 0; i<answer_count; i++) { 657 printf("%s\n", answers[i]->ptr->domain); 658 } 659 break; 660 case TYPE_HINFO: { 661 const char *cpu_header = "CPU"; 662 int cpu_header_len = strlen(cpu_header); 663 int longest_cpu = 0; 664 for (i = 0; i<answer_count; i++) { 665 len = strlen(answers[i]->hinfo->cpu); 666 if (len > longest_cpu) { 667 longest_cpu = len; 668 } 669 } 670 if (longest_cpu < cpu_header_len) { 671 longest_cpu = cpu_header_len; 672 } 673 if (!disable_header) { 674 printf(ANSI_COLOR_GREEN); 675 printf("CPU\t"); 676 printf("OS\n"); 677 printf(ANSI_COLOR_RESET); 678 } 679 for (i = 0; i<answer_count; i++) { 680 printf("%-*s\t", longest_cpu, answers[i]->hinfo->cpu); 681 printf("%-*s\n", longest_cpu, answers[i]->hinfo->os); 682 } 683 break; 684 } 685 case TYPE_MINFO: 686 break; 687 case TYPE_MX: { 688 int longest_mail_domain = 0; 689 for (i = 0; i<answer_count; i++) { 690 len = strlen(answers[i]->mx->mail_domain); 691 if (len > longest_mail_domain) { 692 longest_mail_domain = len; 693 } 694 } 695 if (!disable_header) { 696 printf(ANSI_COLOR_GREEN"%-*s\t%s\n"ANSI_COLOR_RESET, longest_mail_domain, "Mail Exchange", "Preference"); 697 } 698 for (i = 0; i<answer_count; i++) { 699 printf("%-*s\t", longest_mail_domain, answers[i]->mx->mail_domain); 700 printf("%d\n", answers[i]->mx->preference); 701 } 702 break; 703 } 704 case TYPE_TXT: 705 if (!disable_header) { 706 printf(ANSI_COLOR_GREEN"TXT Data\n"ANSI_COLOR_RESET); 707 } 708 for (i = 0; i<answer_count; i++) { 709 printf("%s\n", answers[i]->txt->data); 710 } 711 break; 712 } 713 } 714 715 static void 716 answers_free( 717 union DnsTypeResult **answers, 718 int answer_count, 719 enum Type type 720 ) 721 { 722 int i; 723 switch (type) { 724 case TYPE_A: 725 for (i = 0; i<answer_count; i++) { 726 free(answers[i]->a->ipv4); 727 free(answers[i]->a); 728 free(answers[i]); 729 } 730 break; 731 case TYPE_AAAA: 732 for (i = 0; i<answer_count; i++) { 733 free(answers[i]->aaaa->ipv6); 734 free(answers[i]->aaaa); 735 free(answers[i]); 736 } 737 break; 738 case TYPE_NS: 739 for (i = 0; i<answer_count; i++) { 740 free(answers[i]->ns->domain); 741 free(answers[i]->ns); 742 free(answers[i]); 743 } 744 break; 745 case TYPE_MD: 746 break; 747 case TYPE_MF: 748 break; 749 case TYPE_CNAME: 750 for (i = 0; i<answer_count; i++) { 751 free(answers[i]->cname->domain); 752 free(answers[i]->cname); 753 free(answers[i]); 754 } 755 break; 756 case TYPE_SOA: 757 for (i = 0; i<answer_count; i++) { 758 free(answers[i]->soa->mname); 759 free(answers[i]->soa->rname); 760 free(answers[i]->soa); 761 free(answers[i]); 762 } 763 break; 764 case TYPE_MB: 765 break; 766 case TYPE_MG: 767 break; 768 case TYPE_MR: 769 break; 770 case TYPE_NULL: 771 break; 772 case TYPE_WKS: 773 break; 774 case TYPE_PTR: 775 for (i = 0; i<answer_count; i++) { 776 free(answers[i]->ptr->domain); 777 free(answers[i]->ptr); 778 free(answers[i]); 779 } 780 break; 781 case TYPE_HINFO: 782 for (i = 0; i<answer_count; i++) { 783 free(answers[i]->hinfo->cpu); 784 free(answers[i]->hinfo->os); 785 free(answers[i]->hinfo); 786 free(answers[i]); 787 } 788 break; 789 case TYPE_MINFO: 790 break; 791 case TYPE_MX: 792 for (i = 0; i<answer_count; i++) { 793 free(answers[i]->mx->mail_domain); 794 free(answers[i]->mx); 795 free(answers[i]); 796 } 797 break; 798 case TYPE_TXT: 799 for (i = 0; i<answer_count; i++) { 800 free(answers[i]->txt->data); 801 free(answers[i]->txt); 802 free(answers[i]); 803 } 804 break; 805 } 806 free(answers); 807 } 808 809 static bool 810 response_is_valid(struct DnsHeader *req_header, struct DnsHeader *res_header) 811 { 812 if (res_header->tc) { 813 LOG_ERR("Response is marked as truncated."); 814 return false; 815 } 816 if (!res_header->qr) { 817 LOG_ERR("Response is marked as request (QR). That makes no sense."); 818 return false; 819 } 820 if (req_header->id != res_header->id) { 821 LOG_ERR("Response has a different id than the request."); 822 return false; 823 } 824 if (res_header->rcode != RCODE_NO_ERROR) { 825 fprintf(stderr, "Response code: %s\n", response_code_to_string(res_header->rcode)); 826 return false; 827 } 828 if (res_header->ancount < req_header->qdcount) { 829 fprintf(stderr, "The server didn't provide an answer.\n"); 830 return false; 831 } 832 return true; 833 } 834 835 /* TODO: Find a better way to get a dns server ip. This is ugly. */ 836 static char * 837 dns_server_ip_get(void) 838 { 839 FILE *fp; 840 char *ipv4; 841 size_t bytes_read; 842 const char *cmd = "cat /etc/resolv.conf | grep 'nameserver.*\\..*' | cut -d' ' -f2 | tr -d '\n'"; 843 fp = popen(cmd, "r"); 844 if (!fp) { 845 LOG_DEBUG("popen failed."); 846 return NULL; 847 } 848 ipv4 = malloc(16 * sizeof(char)); 849 bytes_read = fread(ipv4, 1, 15, fp); 850 pclose(fp); 851 if (bytes_read >= MIN_IP_LENGTH) { 852 ipv4[bytes_read] = 0; 853 struct sockaddr_in sa; 854 if (inet_pton(AF_INET, ipv4, &sa.sin_addr) != 0) { 855 return ipv4; 856 } 857 } 858 return NULL; 859 } 860 861 static struct ByteArray * 862 dns_server_request(char *request, size_t request_len, const char *dns_server) 863 { 864 int fd; 865 size_t bytes_sent, bytes_received; 866 struct addrinfo *addr_info; 867 struct ByteArray *res; 868 if (dns_server) { 869 addr_info = get_addr_info(dns_server); 870 } else { 871 char *ip = dns_server_ip_get(); 872 if (!ip) { 873 LOG_DEBUG("dns_server_ip_get failed."); 874 return NULL; 875 } 876 addr_info = get_addr_info(ip); 877 free(ip); 878 } 879 if (!addr_info) { 880 LOG_DEBUG("get_addr_info failed."); 881 return NULL; 882 } 883 fd = socket(AF_INET, SOCK_DGRAM, 0); 884 if (fd == -1) { 885 perror("socket failed"); 886 return NULL; 887 } 888 bytes_sent = sendto(fd, request, request_len, 0, addr_info->ai_addr, addr_info->ai_addrlen); 889 if (bytes_sent == request_len) { 890 char response[MAX_UDP_MSG_LENGTH * sizeof(char)]; 891 bytes_received = recvfrom(fd, response, MAX_UDP_MSG_LENGTH, 0, addr_info->ai_addr, &addr_info->ai_addrlen); 892 if (bytes_received > 0) { 893 res = malloc(sizeof(struct ByteArray)); 894 res->b = malloc(bytes_received * sizeof(char)); 895 memcpy(res->b, &response, bytes_received); 896 res->len = bytes_received; 897 freeaddrinfo(addr_info); 898 return res; 899 } else { 900 fprintf(stderr, "Didn't receive a response.\n"); 901 return NULL; 902 } 903 } else { 904 fprintf(stderr, "Didn't send whole request.\n"); 905 return NULL; 906 } 907 } 908 909 static bool 910 dns_server_ask( 911 const char *dns_server, 912 const char *domain, 913 enum Type dns_type, 914 bool disable_header 915 ) 916 { 917 int answer_count, i; 918 size_t start_of_answer; 919 struct ByteArray *header, *question, *res; 920 struct DnsHeader *res_header; 921 struct DnsResourceRecord *answer; 922 union DnsTypeResult **answers; 923 struct DnsQuestion qu = { 924 .domain = domain, 925 .type = dns_type, 926 .class = CLASS_IN 927 }; 928 929 dns_header_default.id += 1; 930 header = header_form(&dns_header_default); 931 question = question_form(&qu); 932 char req[header->len + question->len]; 933 memcpy(req, header->b, header->len); 934 memcpy(&req[header->len], question->b, question->len); 935 res = dns_server_request((char *)&req, header->len + question->len, dns_server); 936 if (!res) { 937 LOG_DEBUG("dns_server_request failed."); 938 return false; 939 } 940 byte_array_free(header); 941 if (res->len <= DNS_HEADER_LENGTH) { 942 fprintf(stderr, "Response is too short.\n"); 943 return false; 944 } 945 res_header = header_parse(res->b); 946 if (!response_is_valid(&dns_header_default, res_header)) { 947 return false; 948 } 949 if (memcmp(question->b, &res->b[DNS_HEADER_LENGTH], question->len)) { 950 fprintf(stderr, "The question section differs in the request and the response.\n"); 951 return false; 952 } 953 answer_count = res_header->ancount; 954 free(res_header); 955 start_of_answer = DNS_HEADER_LENGTH + question->len; 956 byte_array_free(question); 957 if (res->len < start_of_answer) { 958 fprintf(stderr, "Response doesn't include an answer.\n"); 959 return false; 960 } 961 answers = malloc(answer_count * sizeof(union DnsTypeResult *)); 962 for (i = 0; i<answer_count; i++) { 963 answer = answer_parse(res, &start_of_answer); 964 answers[i] = answer_parse_by_type(answer, res, dns_type, &start_of_answer); 965 resource_record_free(answer); 966 } 967 answers_print(answers, answer_count, dns_type, disable_header); 968 answers_free(answers, answer_count, dns_type); 969 byte_array_free(res); 970 return true; 971 } 972 973 int 974 main(int argc, char *argv[]) 975 { 976 static struct option options[] = { 977 { "dns-server", required_argument, 0, 's' }, 978 { "type", required_argument, 0, 't' }, 979 { "header", no_argument, 0, 'h' }, 980 { 0, 0, 0, 0 } 981 }; 982 int option_index = 0; 983 int o = 0; 984 char *domain, *upper_type; 985 char type[5+1]; 986 type[0] = 0; 987 char dns_server[MAX_DOMAIN_LENGTH+1]; 988 dns_server[0] = 0; 989 bool disable_header = false; 990 bool error; 991 enum Type dns_type; 992 while ((o = getopt_long(argc, argv, "s:t:h", options, &option_index)) != -1) { 993 switch (o) { 994 case 's': 995 if (strlen(optarg) > MAX_DOMAIN_LENGTH) { 996 fprintf(stderr, "The provided dns server ip is too long.\n"); 997 return 1; 998 } 999 strcpy(dns_server, optarg); 1000 break; 1001 case 't': 1002 if (strlen(optarg) > 5) { 1003 fprintf(stderr, "The provided type is too long.\n"); 1004 return 1; 1005 } 1006 strcpy(type, optarg); 1007 break; 1008 case 'h': 1009 disable_header = true; 1010 break; 1011 } 1012 } 1013 if (argc == optind) { 1014 fprintf(stderr, "Provide a domain!\n"); 1015 return 1; 1016 } 1017 if (argc > optind+1) { 1018 fprintf(stderr, "Provide only one domain!\n"); 1019 return 1; 1020 } 1021 if (strlen(argv[optind]) > MAX_DOMAIN_LENGTH) { 1022 fprintf(stderr, "The provided domain is too long.\n"); 1023 return 1; 1024 } 1025 domain = argv[optind]; 1026 if (type[0] == 0) { 1027 fprintf(stderr, "Provide a type (-t/--type)!\n"); 1028 return 1; 1029 } 1030 upper_type = str_to_upper(type); 1031 dns_type = type_parse(upper_type, &error); 1032 if (error) { 1033 fprintf(stderr, "You provided an invalid type.\n"); 1034 return 1; 1035 } 1036 free(upper_type); 1037 if (!dns_server_ask(dns_server[0] == 0 ? NULL : dns_server, domain, dns_type, disable_header)) { 1038 LOG_DEBUG("dns_server_ask failed."); 1039 return 1; 1040 } 1041 return 0; 1042 }