dinoco

Query DNS records
git clone git://git.relim.de/dinoco.git
Log | Files | Refs | README | LICENSE

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 }