dinoco

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

commit 3c0a63afbbd8509a597a584ec333426ae371a590
parent 81f6fe189ebf440b19b3a31b99c628a4813929dd
Author: Nibo <kroekerrobin@gmail.com>
Date:   Mon, 14 Apr 2025 08:53:36 +0200

Rewrite everything

Diffstat:
MMakefile | 7+++++--
Mdinoco.1 | 6+++---
Mdinoco.c | 1657++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdinoco.h | 161++++++++++++++++++++++++++++++-------------------------------------------------
4 files changed, 900 insertions(+), 931 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,10 @@ PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man -all: - $(CC) -g -O -Wall -Werror -o dinoco dinoco.c +dinoco: + $(CC) -O -Wall -Wextra -pedantic -o dinoco dinoco.c +debug: + $(CC) -DDEBUG -g -Wall -Wextra -pedantic -o dinoco dinoco.c clean: rm dinoco install: all @@ -15,3 +17,4 @@ install: all uninstall: rm "$(PREFIX)/bin/dinoco" rm "$(MANPREFIX)/man1/dinoco.1" +.PHONY: debug clean install uninstall diff --git a/dinoco.1 b/dinoco.1 @@ -31,15 +31,15 @@ Not supported types: WKS, MB, MD, MF, MG, MR, MINFO, NULL. Disable the printing of the header line. .SH EXAMPLES .EX -\fB\,$\/\fR dinoco -d blubiblub.org -t MX +\fB\,$\/\fR dinoco -t MX blubiblub.org Mail Exchange Preference mail.blubiblub.org 10 \fB\,$\/\fR -\fB\,$\/\fR dinoco -d blubiblub.org -t A +\fB\,$\/\fR dinoco -t A blubiblub.org Address 221.180.67.12 \fB\,$\/\fR -\fB\,$\/\fR dinoco -d honkaponka.org -t MX -h +\fB\,$\/\fR dinoco -h -t MX honkaponka.org mail.honkaponla.org 10 alt1.mail.honkaponla.org 20 \fB\,$\/\fR diff --git a/dinoco.c b/dinoco.c @@ -3,6 +3,7 @@ #include <stdlib.h> #include <stdbool.h> #include <stdint.h> +#include <unistd.h> #include <getopt.h> #include <ctype.h> #include <sys/socket.h> @@ -11,340 +12,400 @@ #include <inttypes.h> #include "dinoco.h" -inline char *stringCat(char *str1, char *str2) +static struct DnsHeader dns_header_default = { + .id = 55, + .qr = false, + .opcode = OPCODE_QUERY, + .aa = false, + .tc = false, + .rd = true, + .ra = false, + .rcode = RCODE_NO_ERROR, + .qdcount = 1, + .ancount = 0, + .nscount = 0, + .arcount = 0 +}; + +static void +byte_array_free(struct ByteArray *bytes) { - int str1Len = strlen(str1); - int str2Len = strlen(str2); - char *string = malloc((str1Len+str2Len+1) * sizeof(char)); - int i = 0; - int k = 0; - for (; i<str1Len; i++) - { - string[i] = str1[i]; - } - for (; k<str2Len; k++) - { - string[i+k] = str2[k]; - } - string[i+k] = '\0'; - free(str1); - free(str2); - return string; + free(bytes->b); + free(bytes); } -char *getRCODEString(enum rcode code) +static void +resource_record_free(struct DnsResourceRecord *record) { - switch (code) - { - case RCODE_FORMAT_ERROR: - return "Format Error"; - break; - case RCODE_SERVER_FAILURE: - return "Server Failure"; - break; - case RCODE_NAME_ERROR: - return "Name Error"; - break; - case RCODE_NOT_IMPLEMENTED: - return "Not Implemented"; - break; - case RCODE_REFUSED: - return "Refused"; - break; - default: - return ""; + free(record->domain); + byte_array_free(record->rdata); + free(record); +} + +static char * +str_cat(const char *str1, const char *str2) +{ + char *new, *n; + const char *c; + new = malloc((strlen(str1)+strlen(str2)+1) * sizeof(char)); + n = new; + for (c = str1; *c; c++, n++) { + *n = *c; + } + for (c = str2; *c; c++, n++) { + *n = *c; + } + *n = 0; + return new; +} + +static char * +str_to_upper(const char *str) +{ + char *new, *n; + const char *c; + new = malloc((strlen(str)+1) * sizeof(char)); + for (c = str, n = new; *c; c++, n++) { + *n = toupper(*c); + } + *n = 0; + return new; +} + +static const char * +response_code_to_string(enum ResponseCode code) +{ + switch (code) { + case RCODE_NO_ERROR: + return "No error condition"; + case RCODE_FORMAT_ERROR: + return "Format Error"; + case RCODE_SERVER_FAILURE: + return "Server Failure"; + case RCODE_NAME_ERROR: + return "Name Error"; + case RCODE_NOT_IMPLEMENTED: + return "Not Implemented"; + case RCODE_REFUSED: + return "Refused"; } + return ""; } -char *getDNSServerIP() +/* TODO: Find a better way to get a dns server ip. This is ugly. */ +static char * +dns_server_ip_get(void) { + FILE *fp; + char *ipv4; + size_t bytes_read; const char *cmd = "cat /etc/resolv.conf | grep 'nameserver.*\\..*' | cut -d' ' -f2 | tr -d '\n'"; - FILE *fp = popen(cmd, "r"); - char *buf = malloc(16 * sizeof(char)); - size_t bytesRead = fread(buf, 1, 15, fp); + fp = popen(cmd, "r"); + if (!fp) { + LOG_DEBUG("popen failed."); + return NULL; + } + ipv4 = malloc(16 * sizeof(char)); + bytes_read = fread(ipv4, 1, 15, fp); pclose(fp); - if (bytesRead >= MIN_IP_LENGTH) - { - buf[bytesRead] = 0; + if (bytes_read >= MIN_IP_LENGTH) { + ipv4[bytes_read] = 0; struct sockaddr_in sa; - int ret = inet_pton(AF_INET, buf, &(sa.sin_addr)); - if (ret != 0) - return buf; - else - return ""; + if (inet_pton(AF_INET, ipv4, &sa.sin_addr) != 0) { + return ipv4; + } } - return ""; + return NULL; } -char *toUpper(char *string) +static struct ByteArray * +dns_server_request(char *request, size_t request_len) { - char *newString = malloc((strlen(string)+1) * sizeof(char)); - int i = 0; - for (; i<strlen(string); i++) - { - newString[i] = toupper(string[i]); + int port = 53; + int fd; + size_t bytes_sent, bytes_received; + socklen_t size; + struct ByteArray *res; + char *ip = dns_server_ip_get(); + if (!ip) { + LOG_DEBUG("dns_server_ip_get failed."); + return NULL; + } + struct hostent *host = (struct hostent *)gethostbyname(ip); + free(ip); + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + perror("socket failed"); + return NULL; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr = *((struct in_addr *)host->h_addr); + memset(&addr.sin_zero, 0, 8); + size = (socklen_t)sizeof(addr); + bytes_sent = sendto(fd, request, request_len, 0, (struct sockaddr *)&addr, size); + if (bytes_sent == request_len) { + char response[MAX_UDP_MSG_LENGTH * sizeof(char)]; + bytes_received = recvfrom(fd, response, MAX_UDP_MSG_LENGTH, 0, (struct sockaddr *)&addr, &size); + if (bytes_received > 0) { + res = malloc(sizeof(struct ByteArray)); + res->b = malloc(bytes_received * sizeof(char)); + memcpy(res->b, &response, bytes_received); + res->len = bytes_received; + return res; + } else { + fprintf(stderr, "Didn't receive a response.\n"); + return NULL; + } + } else { + fprintf(stderr, "Didn't send whole request.\n"); + return NULL; } - newString[i] = 0; - free(string); - return newString; } -short parseType(char *arg) +static enum Type +type_parse(const char *str, bool *error) { - for (int i=0; i<17; i++) - { - if (strcmp(arg, types[i].string) == 0) - { - free(arg); - return types[i].type; - } - } - free(arg); - return -1; + *error = false; + if (!strcmp(str, "A")) { + return TYPE_A; + } else + if (!strcmp(str, "NS")) { + return TYPE_NS; + } else + if (!strcmp(str, "MD")) { + return TYPE_MD; + } else + if (!strcmp(str, "MF")) { + return TYPE_MF; + } else + if (!strcmp(str, "CNAME")) { + return TYPE_CNAME; + } else + if (!strcmp(str, "SOA")) { + return TYPE_SOA; + } else + if (!strcmp(str, "MB")) { + return TYPE_MB; + } else + if (!strcmp(str, "MG")) { + return TYPE_MG; + } else + if (!strcmp(str, "MR")) { + return TYPE_MR; + } else + if (!strcmp(str, "NULL")) { + return TYPE_NULL; + } else + if (!strcmp(str, "WKS")) { + return TYPE_WKS; + } else + if (!strcmp(str, "PTR")) { + return TYPE_PTR; + } else + if (!strcmp(str, "HINFO")) { + return TYPE_HINFO; + } else + if (!strcmp(str, "MINFO")) { + return TYPE_MINFO; + } else + if (!strcmp(str, "MX")) { + return TYPE_MX; + } else + if (!strcmp(str, "TXT")) { + return TYPE_TXT; + } else + if (!strcmp(str, "AAAA")) { + return TYPE_AAAA; + } + *error = true; + return TYPE_A; // unused } // e.g. "domain.com" -> 6, 'd', 'o', 'm', 'a', 'i', 'n', 3, 'c', 'o', 'm', 0 -struct byte_array *formDomain(char *domain) +static struct ByteArray * +domain_form(const char *str) { - char *dnsDomain = malloc(sizeof(char)); - int i = 0; - int q = 0; - int domainLabelCharCount = 0; - char c; - while ((c = domain[i]) != '\0') - { - if (c == '.') - { - dnsDomain = realloc(dnsDomain, (q + 1) * sizeof(char)); - dnsDomain[q] = domainLabelCharCount; - q++; - for (int k=i-domainLabelCharCount; k<i; k++) - { - dnsDomain = realloc(dnsDomain, (q + 1) * sizeof(char)); - dnsDomain[q] = domain[k]; - q++; + struct ByteArray *bytes; + char *domain = NULL; + int d = 0; + int k, i; + int char_count = 0; + for (i = 0; str[i]; i++) { + if (str[i] == '.') { + domain = realloc(domain, (d+char_count+1) * sizeof(char)); + domain[d] = char_count; + d++; + for (k = i-char_count; k<i; k++, d++) { + domain[d] = str[k]; } - domainLabelCharCount = 0; + char_count = 0; + } else { + char_count++; } - else - { - domainLabelCharCount++; - } - i++; } - dnsDomain = realloc(dnsDomain, (q + 1) * sizeof(char)); - dnsDomain[q] = domainLabelCharCount; - q++; - for (int k=i-domainLabelCharCount; k<i; k++) - { - dnsDomain = realloc(dnsDomain, (q + 1) * sizeof(char)); - dnsDomain[q] = domain[k]; - q++; - } - dnsDomain = realloc(dnsDomain, (q + 1) * sizeof(char)); - dnsDomain[q] = 0; - dnsDomain = realloc(dnsDomain, (q + 1 + 4) * sizeof(char)); - struct byte_array *b = malloc(sizeof(struct byte_array)); - b->bytes = dnsDomain; - b->length = q + 1; - return b; + domain = realloc(domain, (d+char_count+2) * sizeof(char)); + domain[d] = char_count; + d++; + for (k = i-char_count; k<i; k++, d++) { + domain[d] = str[k]; + } + domain[d] = 0; + bytes = malloc(sizeof(struct ByteArray)); + bytes->b = (uint8_t *)domain; + bytes->len = d + 1; + return bytes; } -char *parseIPv4(char *data) +static char * +domain_parse(const struct ByteArray *bytes, int start) { - char *ipv4 = NULL; - int e = 0; - uint8_t ipPart = 0; - for (int i=0; i<4; i++) - { - ipPart = data[i] & 0xFF; - char number[4]; - sprintf(number, "%d", ipPart); - number[3] = 0; - for (int k=0; k<strlen(number); k++) - { - ipv4 = realloc(ipv4, (e+1) * sizeof(char)); - ipv4[e] = number[k]; - e++; - } - if (i != 3) - { - ipv4 = realloc(ipv4, (e+1) * sizeof(char)); - ipv4[e] = '.'; - e++; - } - } - ipv4 = realloc(ipv4, (e+1) * sizeof(char)); - ipv4[e] = 0; - return ipv4; + char *merged_domain, *string; + char *domain = NULL; + int offset, len; + int d = 0; + int i = start; + do { + // if pointer + if (bytes->b[i] & 0b11000000) { + offset = bytes->b[i+1]; + string = domain_parse(bytes, offset); + domain = realloc(domain, (d+1) * sizeof(char)); + domain[d] = 0; + merged_domain = str_cat(domain, string); + free(domain); + free(string); + return merged_domain; + } + len = bytes->b[i]; + domain = realloc(domain, (d+len) * sizeof(char)); + memcpy(&domain[d], &bytes->b[i+1], len); + d += len; + i += len + 1; + if (bytes->b[i]) { + domain = realloc(domain, (d+1) * sizeof(char)); + domain[d] = '.'; + d++; + } + } while (bytes->b[i]); + domain = realloc(domain, (d+1) * sizeof(char)); + domain[d] = 0; + return domain; } -char *parseIPv6(char *data) +static char * +ipv4_parse(const uint8_t data[4]) { - char *ipv6 = NULL; - char ipv6Part[3]; - int s = 0; - for (int i=0; i<16; i++) - { - uint8_t n = data[i] & 0xFF; - sprintf(ipv6Part, "%02x", n); - ipv6Part[2] = 0; - for (int k=0; k<strlen(ipv6Part); k++) - { - ipv6 = realloc(ipv6, (s+1) * sizeof(char)); - ipv6[s] = ipv6Part[k]; - s++; - } - if (i%2 != 0 && i != 15) - { - ipv6 = realloc(ipv6, (s+1) * sizeof(char)); - ipv6[s] = ':'; - s++; - } - } - ipv6 = realloc(ipv6, (s+1) * sizeof(char)); - ipv6[s] = 0; - return ipv6; + char *ipv4 = NULL; + char number[4]; + uint8_t ip_part; + int i = 0; + int k, char_wrote; + for (k = 0; k<4; k++) { + ip_part = data[k] & 0xFF; + char_wrote = snprintf(number, 4, "%d", ip_part); + ipv4 = realloc(ipv4, (i+char_wrote+1) * sizeof(char)); + strcpy(&ipv4[i], number); + i += char_wrote; + if (k != 3) { + ipv4 = realloc(ipv4, (i+1) * sizeof(char)); + ipv4[i] = '.'; + i++; + } + } + return ipv4; } -char *parseCharString(char *data, int start) +static char * +ipv6_parse(const uint8_t data[16]) { - char *string = NULL; + char *ipv6 = malloc(40 * sizeof(char)); int i = 0; - for (; i<data[start]; i++) - { - string = realloc(string, (i+1) * sizeof(char)); - string[i] = data[start+i+1]; + char number[3]; + uint8_t ip_part; + int k; + for (k = 0; k<16; k++) { + ip_part = data[k] & 0xFF; + snprintf(number, 3, "%02x", ip_part); + memcpy(&ipv6[i], number, 2); + i += 2; + if (k % 2 != 0 && k != 15) { + ipv6[i] = ':'; + i++; + } } - string = realloc(string, (i+1) * sizeof(char)); - string[i] = 0; - return string; + ipv6[i] = 0; + return ipv6; } -char *parseDomain(struct byte_array *res, int start) +static char * +char_string_parse(uint8_t *data, int start) { - char *domain = NULL; - char *mergedDomain = NULL; - char *string = NULL; - int offset = 0; - int k = 0; - int i = start; - while (res->bytes[i] != 0) - { - // If pointer - if (res->bytes[i] & 0b11000000) - { - offset = res->bytes[i+1]; - string = parseDomain(res, offset); - domain = realloc(domain, (k+1) * sizeof(char)); - domain[k] = 0; - mergedDomain = stringCat(domain, string); - return mergedDomain; - } - for (int s=0; s<res->bytes[i]; s++) - { - domain = realloc(domain, (k+1) * sizeof(char)); - domain[k] = res->bytes[i+s+1]; - k++; - } - i += res->bytes[i] + 1; - if (res->bytes[i] != 0) - { - domain = realloc(domain, (k+1) * sizeof(char)); - domain[k] = '.'; - k++; - } - else - { - break; - } - } - domain = realloc(domain, (k+1) * sizeof(char)); - domain[k] = 0; - return domain; + int len = data[start]; + char *str = malloc((len+1) * sizeof(char)); + memcpy(str, &data[start+1], len); + str[len] = 0; + return str; } -struct byte_array *formHeader(struct dns_header *h) +static struct ByteArray * +header_form(struct DnsHeader *h) { - struct byte_array *header = malloc(sizeof(struct byte_array)); - header->bytes = malloc(DNS_HEADER_LENGTH * sizeof(char)); - header->length = DNS_HEADER_LENGTH; - header->bytes[0] = (h->id >> 8) & 0xFF; - header->bytes[1] = h->id & 0xFF; - char flagOne = 0; + struct ByteArray *header = malloc(sizeof(struct ByteArray)); + header->b = malloc(DNS_HEADER_LENGTH * sizeof(char)); + header->len = DNS_HEADER_LENGTH; + header->b[0] = (h->id >> 8) & 0xFF; + header->b[1] = h->id & 0xFF; + char flag_one = 0; if (h->qr) - flagOne = flagOne | 0b10000000; + flag_one = flag_one | 0b10000000; switch (h->opcode) { case OPCODE_QUERY: // The bit we would set to zero is already zero break; case OPCODE_IQUERY: - flagOne = flagOne | 0b10001000; + flag_one = flag_one | 0b10001000; break; case OPCODE_STATUS: - flagOne = flagOne | 0b10010000; + flag_one = flag_one | 0b10010000; break; } if (h->aa) - flagOne = flagOne | 0b00000100; + flag_one = flag_one | 0b00000100; if (h->tc) - flagOne = flagOne | 0b00000010; + flag_one = flag_one | 0b00000010; if (h->rd) - flagOne = flagOne | 0b00000001; - header->bytes[2] = flagOne; - char flagTwo = 0; + flag_one = flag_one | 0b00000001; + header->b[2] = flag_one; + char flag_two = 0; if (h->ra) - flagTwo = flagTwo | 0b10000000; - header->bytes[3] = flagTwo; - header->bytes[4] = (h->qdcount >> 8) & 0xFF; - header->bytes[5] = h->qdcount & 0xFF; - header->bytes[6] = (h->ancount >> 8) & 0xFF; - header->bytes[7] = h->ancount & 0xFF; - header->bytes[8] = (h->nscount >> 8) & 0xFF; - header->bytes[9] = h->nscount & 0xFF; - header->bytes[10] = (h->arcount >> 8) & 0xFF; - header->bytes[11] = h->arcount & 0xFF; + flag_two = flag_two | 0b10000000; + header->b[3] = flag_two; + header->b[4] = (h->qdcount >> 8) & 0xFF; + header->b[5] = h->qdcount & 0xFF; + header->b[6] = (h->ancount >> 8) & 0xFF; + header->b[7] = h->ancount & 0xFF; + header->b[8] = (h->nscount >> 8) & 0xFF; + header->b[9] = h->nscount & 0xFF; + header->b[10] = (h->arcount >> 8) & 0xFF; + header->b[11] = h->arcount & 0xFF; return header; } -struct byte_array *formQuestion(struct dns_question *qu) +static struct DnsHeader * +header_parse(const uint8_t *res) { - struct byte_array *question = malloc(sizeof(struct byte_array)); - question->bytes = NULL; - struct byte_array *d = formDomain(qu->domain); - int i = 0; - for (; i<d->length; i++) - { - question->bytes = realloc(question->bytes, (i+1) * sizeof(char)); - question->bytes[i] = d->bytes[i]; - } - question->length = d->length; - question->bytes = realloc(question->bytes, (i+4) * sizeof(char)); - question->bytes[i] = 0x00; - question->bytes[i+1] = qu->type; - question->bytes[i+2] = 0x00; - question->bytes[i+3] = qu->class; - question->length = d->length+4; - free(d->bytes); - free(d); - return question; -} - -struct dns_header *parseHeader(char *res) -{ - struct dns_header *header = malloc(sizeof(struct dns_header)); + struct DnsHeader *header = malloc(sizeof(struct DnsHeader)); header->id = res[1] + 256U*res[0]; header->qr = res[2] & 0b10000000 ? true : false; - if (res[2] & 0b00001000) - { + if (res[2] & 0b00001000) { header->opcode = OPCODE_IQUERY; - } - else if (res[2] & 0b00010000) - { + } else + if (res[2] & 0b00010000) { header->opcode = OPCODE_STATUS; - } - else { + } else { header->opcode = OPCODE_QUERY; } header->aa = res[2] & 0b00000100 ? true : false; @@ -359,620 +420,566 @@ struct dns_header *parseHeader(char *res) if (res[3] & 0b00000100) rcode += 4; header->rcode = rcode; - header->qdcount = res[5] + 256U*res[4]; - header->ancount = res[7] + 256U*res[6]; - header->nscount = res[9] + 256U*res[8]; - header->arcount = res[11] + 256U*res[10]; + header->qdcount = res[5] + 256U * res[4]; + header->ancount = res[7] + 256U * res[6]; + header->nscount = res[9] + 256U * res[8]; + header->arcount = res[11] + 256U * res[10]; return header; } -int findNameLength(struct byte_array *res, int start) +static struct ByteArray * +question_form(struct DnsQuestion *qu) { - int length = 0; - int i = start; - while (1) - { - if (res->bytes[i] == 0) - { - length++; + struct ByteArray *question = malloc(sizeof(struct ByteArray)); + struct ByteArray *domain = domain_form(qu->domain); + question->b = malloc((domain->len+4) * sizeof(char)); + question->len = domain->len+4; + memcpy(question->b, domain->b, domain->len); + question->b[domain->len] = 0x00; + question->b[domain->len+1] = qu->type; + question->b[domain->len+2] = 0x00; + question->b[domain->len+3] = qu->class; + byte_array_free(domain); + return question; +} + +static int +name_count_bytes(struct ByteArray *bytes, int start) +{ + int count = 0; + int i; + for (i = start; true; i++, count++) { + if (bytes->b[i] == 0) { + count++; break; } - // If pointer - if ((res->bytes[i] & 0xFF) == 0xC0) - { - length += 2; + // if pointer + if ((bytes->b[i] & 0xFF) == 0xC0) { + count += 2; break; } - length++; - i++; } - return length; + return count; } -struct dns_resource_record *parseAnswer(struct byte_array *res, int *start) +static struct DnsResourceRecord * +answer_parse(struct ByteArray *res, int *start) { - struct dns_resource_record *answer = malloc(sizeof(struct dns_resource_record)); - answer->domain = parseDomain(res, *start); - int nameLength = findNameLength(res, *start); - int b = *start + nameLength; - answer->type = res->bytes[b+1]; - answer->class = res->bytes[b+3]; - answer->ttl = res->bytes[b+7] - + 256U*res->bytes[b+6] - + 65536U*res->bytes[b+5] - + 16777216U*res->bytes[b+4]; - answer->rdlength = res->bytes[b+9] - + 256U*res->bytes[b+8]; - answer->rdata = malloc(sizeof(struct byte_array)); - answer->rdata->bytes = NULL; - answer->rdata->length = answer->rdlength; - int i = 0; - for (; i<answer->rdlength; i++) - { - answer->rdata->bytes = realloc(answer->rdata->bytes, (i+1) * sizeof(char)); - answer->rdata->bytes[i] = res->bytes[b+10+i]; - } - *start += nameLength + 10 + answer->rdlength; + struct DnsResourceRecord *answer = malloc(sizeof(struct DnsResourceRecord)); + answer->domain = domain_parse(res, *start); + int name_len = name_count_bytes(res, *start); + int b = *start + name_len; + answer->type = res->b[b+1] & 0xFF; + answer->class = res->b[b+3] & 0xFF; + answer->ttl = (res->b[b+7] & 0xFF) + + 256U * (res->b[b+6] & 0xFF) + + 65536U * (res->b[b+5] & 0xFF) + + 16777216U * (res->b[b+4] & 0xFF); + answer->rdlength = (res->b[b+9] & 0xFF) + 256U * (res->b[b+8] & 0xFF); + answer->rdata = malloc(sizeof(struct ByteArray)); + answer->rdata->b = malloc(answer->rdlength * sizeof(char)); + answer->rdata->len = answer->rdlength; + memcpy(answer->rdata->b, &res->b[b+10], answer->rdlength); + *start += name_len + 10 + answer->rdlength; return answer; } -union dns_type_result *parseAnswerByType -( - struct dns_resource_record *answer, - enum type type, - struct byte_array *res, - int *startOfNextAnswer +static union DnsTypeResult * +answer_parse_by_type( + struct DnsResourceRecord *answer, + struct ByteArray *res, + enum Type type, + int *start_of_next_answer ) { - union dns_type_result *ur = malloc(sizeof(union dns_type_result)); - switch (type) - { - case TYPE_A: - ur->a = malloc(sizeof(struct dns_a_result)); - ur->a->ipAddress = parseIPv4(answer->rdata->bytes); - break; - case TYPE_AAAA: - ur->aaaa = malloc(sizeof(struct dns_aaaa_result)); - ur->aaaa->ipv6 = parseIPv6(answer->rdata->bytes); - break; - case TYPE_NS: - ur->ns = malloc(sizeof(struct dns_ns_result)); - int startOfDomain = *startOfNextAnswer - answer->rdlength; - ur->ns->domain = parseDomain(res, startOfDomain); - break; - case TYPE_MD: - break; - case TYPE_MF: - break; - case TYPE_CNAME: - ur->cname = malloc(sizeof(struct dns_cname_result)); - int startOfCanonicalDomain = *startOfNextAnswer - answer->rdlength; - ur->cname->domain = parseDomain(res, startOfCanonicalDomain); - break; - case TYPE_SOA: - ur->soa = malloc(sizeof(struct dns_soa_result)); - int start = *startOfNextAnswer - answer->rdlength; - ur->soa->mname = parseDomain(res, start); - int mnameLength = findNameLength(res, start); - start += mnameLength; - ur->soa->rname = parseDomain(res, start); - int rnameLength = findNameLength(res, start); - start += rnameLength; - ur->soa->serial = ((uint32_t)(res->bytes[start] & 0xFF) << 24) - + ((uint32_t)(res->bytes[start+1] & 0xFF) << 16) - + ((uint32_t)(res->bytes[start+2] & 0xFF) << 8) - + (uint32_t)(res->bytes[start+3] & 0xFF); - start += 4; - ur->soa->refresh = ((uint32_t)(res->bytes[start] & 0xFF) << 24) - + ((uint32_t)(res->bytes[start+1] & 0xFF) << 16) - + ((uint32_t)(res->bytes[start+2] & 0xFF) << 8) - + (uint32_t)(res->bytes[start+3] & 0xFF); - start += 4; - ur->soa->retry = ((uint32_t)(res->bytes[start] & 0xFF) << 24) - + ((uint32_t)(res->bytes[start+1] & 0xFF) << 16) - + ((uint32_t)(res->bytes[start+2] & 0xFF) << 8) - + (uint32_t)(res->bytes[start+3] & 0xFF); - start += 4; - ur->soa->expire = ((uint32_t)(res->bytes[start] & 0xFF) << 24) - + ((uint32_t)(res->bytes[start+1] & 0xFF) << 16) - + ((uint32_t)(res->bytes[start+2] & 0xFF) << 8) - + (uint32_t)(res->bytes[start+3] & 0xFF); - start += 4; - ur->soa->minimum = ((uint32_t)(res->bytes[start] & 0xFF) << 24) - + ((uint32_t)(res->bytes[start+1] & 0xFF) << 16) - + ((uint32_t)(res->bytes[start+2] & 0xFF) << 8) - + (uint32_t)(res->bytes[start+3] & 0xFF); - break; - case TYPE_MB: - break; - case TYPE_MG: - break; - case TYPE_MR: - break; - case TYPE_NULL: - break; - case TYPE_WKS: - break; - case TYPE_PTR: - ur->ptr = malloc(sizeof(struct dns_ptr_result)); - int startOfPTRDomain = *startOfNextAnswer - answer->rdlength; - ur->ptr->domain = parseDomain(res, startOfPTRDomain); - break; - case TYPE_HINFO: - ur->hinfo = malloc(sizeof(struct dns_hinfo_result)); - ur->hinfo->cpu = parseCharString(answer->rdata->bytes, 0); - int startOfOS = answer->rdata->bytes[0] + 1; - ur->hinfo->os = parseCharString(answer->rdata->bytes, startOfOS); - break; - case TYPE_MINFO: - break; - case TYPE_MX: - uint16_t preference = answer->rdata->bytes[1] - + 256U*answer->rdata->bytes[0]; - int startOfMailDomain = *startOfNextAnswer - answer->rdlength + 2; - ur->mx = malloc(sizeof(struct dns_mx_result)); - ur->mx->preference = preference; - ur->mx->mailDomain = parseDomain(res, startOfMailDomain); - break; - case TYPE_TXT: - ur->txt = malloc(sizeof(struct dns_txt_result)); - ur->txt->data = NULL; - int txtLength = answer->rdata->bytes[0]; - int i = 0; - for (; i<txtLength; i++) - { - ur->txt->data = realloc(ur->txt->data, (i+1) * sizeof(char)); - ur->txt->data[i] = answer->rdata->bytes[i+1]; - } - ur->txt->data = realloc(ur->txt->data, (i+1) * sizeof(char)); - ur->txt->data[i] = 0; - break; + union DnsTypeResult *ur = malloc(sizeof(union DnsTypeResult)); + switch (type) { + case TYPE_A: + ur->a = malloc(sizeof(struct DnsAResult)); + ur->a->ipv4 = ipv4_parse(answer->rdata->b); + break; + case TYPE_AAAA: + ur->aaaa = malloc(sizeof(struct DnsAaaaResult)); + ur->aaaa->ipv6 = ipv6_parse(answer->rdata->b); + break; + case TYPE_NS: + ur->ns = malloc(sizeof(struct DnsNsResult)); + int start_of_domain = *start_of_next_answer - answer->rdlength; + ur->ns->domain = domain_parse(res, start_of_domain); + break; + case TYPE_MD: + break; + case TYPE_MF: + break; + case TYPE_CNAME: + ur->cname = malloc(sizeof(struct DnsCnameResult)); + int start_of_canonical_domain = *start_of_next_answer - answer->rdlength; + ur->cname->domain = domain_parse(res, start_of_canonical_domain); + break; + case TYPE_SOA: + ur->soa = malloc(sizeof(struct DnsSoaResult)); + int start = *start_of_next_answer - answer->rdlength; + ur->soa->mname = domain_parse(res, start); + int mname_len = name_count_bytes(res, start); + start += mname_len; + ur->soa->rname = domain_parse(res, start); + int rname_len = name_count_bytes(res, start); + start += rname_len; + ur->soa->serial = ((uint32_t)(res->b[start] & 0xFF) << 24) + + ((uint32_t)(res->b[start+1] & 0xFF) << 16) + + ((uint32_t)(res->b[start+2] & 0xFF) << 8) + + (uint32_t)(res->b[start+3] & 0xFF); + start += 4; + ur->soa->refresh = ((uint32_t)(res->b[start] & 0xFF) << 24) + + ((uint32_t)(res->b[start+1] & 0xFF) << 16) + + ((uint32_t)(res->b[start+2] & 0xFF) << 8) + + (uint32_t)(res->b[start+3] & 0xFF); + start += 4; + ur->soa->retry = ((uint32_t)(res->b[start] & 0xFF) << 24) + + ((uint32_t)(res->b[start+1] & 0xFF) << 16) + + ((uint32_t)(res->b[start+2] & 0xFF) << 8) + + (uint32_t)(res->b[start+3] & 0xFF); + start += 4; + ur->soa->expire = ((uint32_t)(res->b[start] & 0xFF) << 24) + + ((uint32_t)(res->b[start+1] & 0xFF) << 16) + + ((uint32_t)(res->b[start+2] & 0xFF) << 8) + + (uint32_t)(res->b[start+3] & 0xFF); + start += 4; + ur->soa->minimum = ((uint32_t)(res->b[start] & 0xFF) << 24) + + ((uint32_t)(res->b[start+1] & 0xFF) << 16) + + ((uint32_t)(res->b[start+2] & 0xFF) << 8) + + (uint32_t)(res->b[start+3] & 0xFF); + break; + case TYPE_MB: + break; + case TYPE_MG: + break; + case TYPE_MR: + break; + case TYPE_NULL: + break; + case TYPE_WKS: + break; + case TYPE_PTR: + ur->ptr = malloc(sizeof(struct DnsPtrResult)); + int start_of_ptr_domain = *start_of_next_answer - answer->rdlength; + ur->ptr->domain = domain_parse(res, start_of_ptr_domain); + break; + case TYPE_HINFO: + ur->hinfo = malloc(sizeof(struct DnsHinfoResult)); + ur->hinfo->cpu = char_string_parse(answer->rdata->b, 0); + int start_of_os = answer->rdata->b[0] + 1; + ur->hinfo->os = char_string_parse(answer->rdata->b, start_of_os); + break; + case TYPE_MINFO: + break; + case TYPE_MX: { + uint16_t preference = answer->rdata->b[1] + + 256U * answer->rdata->b[0]; + int start_of_mail_domain = *start_of_next_answer - answer->rdlength + 2; + ur->mx = malloc(sizeof(struct DnsMxResult)); + ur->mx->preference = preference; + ur->mx->mail_domain = domain_parse(res, start_of_mail_domain); + break; + } + case TYPE_TXT: { + int txt_len; + int txt_data_len = 0; + int b = 0; + ur->txt = malloc(sizeof(struct DnsTxtResult)); + ur->txt->data = NULL; + do { + txt_len = answer->rdata->b[b] & 0xFF; + txt_data_len += txt_len; + ur->txt->data = realloc(ur->txt->data, txt_data_len * sizeof(char)); + memcpy(&ur->txt->data[txt_data_len-txt_len], &answer->rdata->b[b+1], txt_len); + b += txt_len + 1; + } while (txt_len > 0); + ur->txt->data = realloc(ur->txt->data, (txt_data_len+1) * sizeof(char)); + ur->txt->data[txt_data_len] = 0; + break; + } } - free(answer->rdata->bytes); - free(answer->rdata); - free(answer->domain); - free(answer); return ur; } -void printAnswers -( - union dns_type_result **answers, - int answerCount, - enum type type, - struct byte_array *res, - int *startOfNextAnswer, - bool disableHeader +static void +answers_print( + union DnsTypeResult **answers, + int answer_count, + enum Type type, + bool disable_header ) { + int i; int len = 0; - switch (type) - { - case TYPE_A: - if (!disableHeader) - printf(ANSI_COLOR_GREEN"Address\n"ANSI_COLOR_RESET); - for (int i=0; i<answerCount; i++) - { - printf("%s\n", answers[i]->a->ipAddress); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->a->ipAddress); - free(answers[i]->a); - free(answers[i]); - } - break; - case TYPE_AAAA: - if (!disableHeader) - { - printf(ANSI_COLOR_GREEN); - printf("AAAA Address\n"); - printf(ANSI_COLOR_RESET); - } - for (int i=0; i<answerCount; i++) - { - printf("%s\n", answers[i]->aaaa->ipv6); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->aaaa->ipv6); - free(answers[i]->aaaa); - free(answers[i]); - } - break; - case TYPE_NS: - if (!disableHeader) - printf(ANSI_COLOR_GREEN"Authoritative Host\n"ANSI_COLOR_RESET); - for (int i=0; i<answerCount; i++) - { - printf("%s\n", answers[i]->ns->domain); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->ns->domain); - free(answers[i]->ns); - free(answers[i]); - } - break; - case TYPE_MD: - break; - case TYPE_MF: - break; - case TYPE_CNAME: - if (!disableHeader) - printf(ANSI_COLOR_GREEN"Canonical Domain\n"ANSI_COLOR_RESET); - for (int i=0; i<answerCount; i++) - { - printf("%s\n", answers[i]->cname->domain); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->cname->domain); - free(answers[i]->cname); - free(answers[i]); - } - break; - case TYPE_SOA: - int longestMName = 0; - for (int i=0; i<answerCount; i++) - { - if ((len = strlen(answers[i]->soa->mname)) > longestMName) - longestMName = len; - } - len = 0; - if (longestMName < 19) - longestMName = 19; - int longestRName = 0; - for (int i=0; i<answerCount; i++) - { - if ((len = strlen(answers[i]->soa->rname)) > longestRName) - longestRName = len; - } - if (longestRName < 31) - longestRName = 31; - if (!disableHeader) - { - printf(ANSI_COLOR_GREEN); - printf("%-*s\t", longestMName, "Primary Name Server"); - printf("%-*s\t", longestRName, "Responsible authority's mailbox"); - printf("%-13s\t", "Serial Number"); - printf("%-16s\t", "Refresh Interval"); - printf("%-14s\t", "Retry Interval"); - printf("%-12s\t", "Expire Limit"); - printf("%-11s", "Minimum TTL"); - printf("\n"); - printf(ANSI_COLOR_RESET); - } - for (int i=0; i<answerCount; i++) - { - printf("%-*s\t", longestMName, answers[i]->soa->mname); - printf("%-*s\t", longestRName, answers[i]->soa->rname); - printf("%-13u\t", answers[i]->soa->serial); - printf("%-16u\t", answers[i]->soa->refresh); - printf("%-14u\t", answers[i]->soa->retry); - printf("%-12u\t", answers[i]->soa->expire); - printf("%-11u", answers[i]->soa->minimum); - printf("\n"); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->soa->mname); - free(answers[i]->soa->rname); - free(answers[i]->soa); - free(answers[i]); - } - break; - case TYPE_MB: - break; - case TYPE_MG: - break; - case TYPE_MR: - break; - case TYPE_NULL: - break; - case TYPE_WKS: - break; - case TYPE_PTR: - if (!disableHeader) - { - printf(ANSI_COLOR_GREEN); - printf("Domain Name\n"); - printf(ANSI_COLOR_RESET); - } - for (int i=0; i<answerCount; i++) - { - printf("%s\n", answers[i]->ptr->domain); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->ptr->domain); - free(answers[i]->ptr); - free(answers[i]); - } - break; - case TYPE_HINFO: - int longestCPU = 0; - len = 0; - for (int i=0; i<answerCount; i++) - { - if ((len = strlen(answers[i]->hinfo->cpu)) > longestCPU) - longestCPU = len; - } - if (longestCPU < 3) - longestCPU = 3; - if (!disableHeader) - { - printf(ANSI_COLOR_GREEN); - printf("CPU\t"); - printf("OS\n"); - printf(ANSI_COLOR_RESET); - } - for (int i=0; i<answerCount; i++) - { - printf("%-*s\t", longestCPU, answers[i]->hinfo->cpu); - printf("%-*s", longestCPU, answers[i]->hinfo->os); - printf("\n"); - } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->hinfo->cpu); - free(answers[i]->hinfo->os); - free(answers[i]->hinfo); - free(answers[i]); - } - break; - case TYPE_MINFO: - break; - case TYPE_MX: - int longestMailDomain = 0; - size_t len = 0; - for (int i=0; i<answerCount; i++) - { - if ((len = strlen(answers[i]->mx->mailDomain)) > longestMailDomain) - longestMailDomain = len; - } - if (!disableHeader) - printf(ANSI_COLOR_GREEN"%-*s\t%s\n"ANSI_COLOR_RESET, longestMailDomain, "Mail Exchange", "Preference"); - for (int i=0; i<answerCount; i++) - { - printf("%-*s\t", longestMailDomain, answers[i]->mx->mailDomain); - printf("%d", answers[i]->mx->preference); - printf("\n"); + switch (type) { + case TYPE_A: + if (!disable_header) { + printf(ANSI_COLOR_GREEN"Address\n"ANSI_COLOR_RESET); + } + for (i = 0; i < answer_count; i++) { + printf("%s\n", answers[i]->a->ipv4); + } + break; + case TYPE_AAAA: + if (!disable_header) { + printf(ANSI_COLOR_GREEN"AAAA Address\n"ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%s\n", answers[i]->aaaa->ipv6); + } + break; + case TYPE_NS: + if (!disable_header) { + printf(ANSI_COLOR_GREEN"Authoritative Host\n"ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%s\n", answers[i]->ns->domain); + } + break; + case TYPE_MD: + break; + case TYPE_MF: + break; + case TYPE_CNAME: + if (!disable_header) { + printf(ANSI_COLOR_GREEN"Canonical Domain\n"ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%s\n", answers[i]->cname->domain); + } + break; + case TYPE_SOA: { + const char *mname_header = "Primary Name Server"; + const char *rname_header = "Responsible authority's mailbox"; + int mname_header_len = strlen(mname_header); + int rname_header_len = strlen(rname_header); + int longest_mname = 0; + int longest_rname = 0; + for (i = 0; i<answer_count; i++) { + len = strlen(answers[i]->soa->mname); + if (len > longest_mname) { + longest_mname = len; } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->mx->mailDomain); - free(answers[i]->mx); - free(answers[i]); + len = strlen(answers[i]->soa->rname); + if (len > longest_rname) { + longest_rname = len; } - break; - case TYPE_TXT: - if (!disableHeader) - printf(ANSI_COLOR_GREEN"TXT Data\n"ANSI_COLOR_RESET); - for (int i=0; i<answerCount; i++) - { - printf("%s\n", answers[i]->txt->data); + } + if (longest_mname < mname_header_len) { + longest_mname = mname_header_len; + } + if (longest_rname < rname_header_len) { + longest_rname = rname_header_len; + } + if (!disable_header) { + printf(ANSI_COLOR_GREEN); + printf("%-*s\t", longest_mname, "Primary Name Server"); + printf("%-*s\t", longest_rname, "Responsible authority's mailbox"); + printf("%-13s\t", "Serial Number"); + printf("%-16s\t", "Refresh Interval"); + printf("%-14s\t", "Retry Interval"); + printf("%-12s\t", "Expire Limit"); + printf("%-11s\n", "Minimum TTL"); + printf(ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%-*s\t", longest_mname, answers[i]->soa->mname); + printf("%-*s\t", longest_rname, answers[i]->soa->rname); + printf("%-13u\t", answers[i]->soa->serial); + printf("%-16u\t", answers[i]->soa->refresh); + printf("%-14u\t", answers[i]->soa->retry); + printf("%-12u\t", answers[i]->soa->expire); + printf("%-11u\n", answers[i]->soa->minimum); + } + break; + } + case TYPE_MB: + break; + case TYPE_MG: + break; + case TYPE_MR: + break; + case TYPE_NULL: + break; + case TYPE_WKS: + break; + case TYPE_PTR: + if (!disable_header) { + printf(ANSI_COLOR_GREEN"Domain Name\n"ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%s\n", answers[i]->ptr->domain); + } + break; + case TYPE_HINFO: { + const char *cpu_header = "CPU"; + int cpu_header_len = strlen(cpu_header); + int longest_cpu = 0; + for (i = 0; i<answer_count; i++) { + len = strlen(answers[i]->hinfo->cpu); + if (len > longest_cpu) { + longest_cpu = len; } - for (int i=0; i<answerCount; i++) - { - free(answers[i]->txt->data); - free(answers[i]->txt); - free(answers[i]); + } + if (longest_cpu < cpu_header_len) { + longest_cpu = cpu_header_len; + } + if (!disable_header) { + printf(ANSI_COLOR_GREEN); + printf("CPU\t"); + printf("OS\n"); + printf(ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%-*s\t", longest_cpu, answers[i]->hinfo->cpu); + printf("%-*s\n", longest_cpu, answers[i]->hinfo->os); + } + break; + } + case TYPE_MINFO: + break; + case TYPE_MX: { + int longest_mail_domain = 0; + for (i = 0; i<answer_count; i++) { + len = strlen(answers[i]->mx->mail_domain); + if (len > longest_mail_domain) { + longest_mail_domain = len; } - break; + } + if (!disable_header) { + printf(ANSI_COLOR_GREEN"%-*s\t%s\n"ANSI_COLOR_RESET, longest_mail_domain, "Mail Exchange", "Preference"); + } + for (i = 0; i<answer_count; i++) { + printf("%-*s\t", longest_mail_domain, answers[i]->mx->mail_domain); + printf("%d\n", answers[i]->mx->preference); + } + break; + } + case TYPE_TXT: + if (!disable_header) { + printf(ANSI_COLOR_GREEN"TXT Data\n"ANSI_COLOR_RESET); + } + for (i = 0; i<answer_count; i++) { + printf("%s\n", answers[i]->txt->data); + } + break; } - free(answers); } -struct byte_array *reqServer(char *req, int length) +static void +answers_free( + union DnsTypeResult **answers, + int answer_count, + enum Type type +) { - char *ip = getDNSServerIP(); - if (strlen(ip) == 0) - { - fprintf(stderr, "getDNSServerIP failed.\n"); - return NULL; - } - struct hostent *host = (struct hostent *) gethostbyname(ip); - free(ip); - int port = 53; - int fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd == -1) - { - perror("socket failed.\n"); - return NULL; - } - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr = *((struct in_addr *)host->h_addr); - bzero(&(addr.sin_zero), 8); - socklen_t size = (socklen_t) sizeof(addr); - size_t bytesSent = sendto(fd, req, length, 0, (struct sockaddr *) &addr, size); - if (bytesSent == length) - { - char *res = malloc(MAX_UDP_MSG_LENGTH * sizeof(char)); - size_t bytesReceived = recvfrom(fd, res, MAX_UDP_MSG_LENGTH, 0, (struct sockaddr *) &addr, &size); - if (bytesReceived > 0) - { - struct byte_array *response = malloc(sizeof(struct byte_array)); - response->bytes = NULL; - for (int i=0; i<bytesReceived; i++) - { - response->bytes = realloc(response->bytes, (i+1) * sizeof(char)); - response->bytes[i] = res[i]; - } - response->length = bytesReceived; - free(res); - return response; + int i; + switch (type) { + case TYPE_A: + for (i = 0; i<answer_count; i++) { + free(answers[i]->a->ipv4); + free(answers[i]->a); + free(answers[i]); } - else - { - free(res); - fprintf(stderr, "Didn't receive a response.\n"); - return NULL; + break; + case TYPE_AAAA: + for (i = 0; i<answer_count; i++) { + free(answers[i]->aaaa->ipv6); + free(answers[i]->aaaa); + free(answers[i]); } + break; + case TYPE_NS: + for (i = 0; i<answer_count; i++) { + free(answers[i]->ns->domain); + free(answers[i]->ns); + free(answers[i]); + } + break; + case TYPE_MD: + break; + case TYPE_MF: + break; + case TYPE_CNAME: + for (i = 0; i<answer_count; i++) { + free(answers[i]->cname->domain); + free(answers[i]->cname); + free(answers[i]); + } + break; + case TYPE_SOA: + for (i = 0; i<answer_count; i++) { + free(answers[i]->soa->mname); + free(answers[i]->soa->rname); + free(answers[i]->soa); + free(answers[i]); + } + break; + case TYPE_MB: + break; + case TYPE_MG: + break; + case TYPE_MR: + break; + case TYPE_NULL: + break; + case TYPE_WKS: + break; + case TYPE_PTR: + for (i = 0; i<answer_count; i++) { + free(answers[i]->ptr->domain); + free(answers[i]->ptr); + free(answers[i]); + } + break; + case TYPE_HINFO: + for (i = 0; i<answer_count; i++) { + free(answers[i]->hinfo->cpu); + free(answers[i]->hinfo->os); + free(answers[i]->hinfo); + free(answers[i]); + } + break; + case TYPE_MINFO: + break; + case TYPE_MX: + for (i = 0; i<answer_count; i++) { + free(answers[i]->mx->mail_domain); + free(answers[i]->mx); + free(answers[i]); + } + break; + case TYPE_TXT: + for (i = 0; i<answer_count; i++) { + free(answers[i]->txt->data); + free(answers[i]->txt); + free(answers[i]); + } + break; } - else - { - fprintf(stderr, "Didn't send whole request.\n"); - return NULL; - } + free(answers); } -bool isValidResponse(struct dns_header *reqHeader, struct dns_header *resHeader) +static bool +response_is_valid(struct DnsHeader *req_header, struct DnsHeader *res_header) { - if (reqHeader->id != resHeader->id) + if (req_header->id != res_header->id) { return false; - if (resHeader->rcode != RCODE_NO_ERROR) - { - fprintf(stderr, "%s\n", getRCODEString(resHeader->rcode)); + } + if (res_header->rcode != RCODE_NO_ERROR) { + fprintf(stderr, "Response code: %s\n", response_code_to_string(res_header->rcode)); return false; } - if (resHeader->ancount < reqHeader->qdcount) + if (res_header->ancount < req_header->qdcount) { + fprintf(stderr, "The server didn't provide an answer.\n"); return false; + } return true; } -int main(int argc, char *argv[]) +int +main(int argc, char *argv[]) { static struct option options[] = { { "type", required_argument, 0, 't' }, { "header", no_argument, 0, 'h' }, { 0, 0, 0, 0 } }; - int optionIndex = 0; + int option_index = 0; int o = 0; - char *domain; - char *type = NULL; - bool isType = false; - bool disableHeader = false; - while ((o = getopt_long(argc, argv, "t:h", options, &optionIndex)) != -1) - { - switch (o) - { - case 't': - type = malloc((strlen(optarg)+1) * sizeof(char)); - strcpy(type, optarg); - isType = true; - break; - case 'h': - disableHeader = true; - break; - } - } - if (argc == optind) - { + char *domain, *upper_type; + char type[5+1]; + type[0] = 0; + bool disable_header = false; + bool error; + enum Type dns_type; + while ((o = getopt_long(argc, argv, "t:h", options, &option_index)) != -1) { + switch (o) { + case 't': + if (strlen(optarg) > 5) { + fprintf(stderr, "The provided type is too long.\n"); + return 1; + } + strcpy(type, optarg); + break; + case 'h': + disable_header = true; + break; + } + } + if (argc == optind) { fprintf(stderr, "Provide a domain!\n"); - free(type); - return -1; + return 1; } - if (argc > optind+1) - { + if (argc > optind+1) { fprintf(stderr, "Provide only one domain!\n"); - free(type); - return -1; - } - domain = malloc((strlen(argv[optind])+1) * sizeof(char)); - strcpy(domain, argv[optind]); - if (!isType) - { - fprintf(stderr, "Provide a type (-t/--type)!\n"); - free(domain); - return -1; - } - short tp = parseType(toUpper(type)); - if (tp == -1) - { - printf("You provided an invalid type.\n"); - free(domain); - return -1; - } - enum type t = tp; - struct byte_array *reqHeader = formHeader(&DNS_HEADER_DEFAULT); - char *req = malloc(reqHeader->length * sizeof(char)); - int i = 0; - for (; i<reqHeader->length; i++) - { - req[i] = reqHeader->bytes[i]; - } - struct dns_question qu = { + return 1; + } + if (strlen(argv[optind]) > MAX_DOMAIN_LENGTH) { + fprintf(stderr, "The provided domain is too long.\n"); + return 1; + } + domain = argv[optind]; + if (type[0] == 0) { + fprintf(stderr, "Provide a type (-t/--type)!\n"); + return 1; + } + upper_type = str_to_upper(type); + dns_type = type_parse(upper_type, &error); + if (error) { + fprintf(stderr, "You provided an invalid type.\n"); + return 1; + } + free(upper_type); + struct ByteArray *header, *question, *res; + struct DnsHeader *res_header; + header = header_form(&dns_header_default); + struct DnsQuestion qu = { .domain = domain, - .type = t, + .type = dns_type, .class = CLASS_IN }; - struct byte_array *question = formQuestion(&qu); - free(qu.domain); - req = realloc(req, (reqHeader->length + question->length) * sizeof(char)); - for (int e=0; e<question->length; e++) - { - req[i] = question->bytes[e]; - i++; - } - int length = reqHeader->length + question->length; - free(reqHeader->bytes); - free(reqHeader); - struct byte_array *res = reqServer(req, length); - free(req); - if (res == NULL) - { - fprintf(stderr, "reqServer failed.\n"); - return -1; - } - if (res->length >= DNS_HEADER_LENGTH) - { - /* printf("res:\n"); - for (int i=0; i<res->length; i++) - { - printf("%d: %02X\n", i, res->bytes[i]); - } - printf("\n"); */ - struct dns_header *resHeader = parseHeader(res->bytes); - if (isValidResponse(&DNS_HEADER_DEFAULT, resHeader)) - { - int answerCount = resHeader->ancount; - int *startOfAnswer = malloc(sizeof(int)); - *startOfAnswer = DNS_HEADER_LENGTH + question->length; - free(question->bytes); - free(question); - free(resHeader); - /* - This doesn't mean it's long enough to parse an answer - but there is at least part of an answer. - */ - if (res->length > *startOfAnswer) - { - struct dns_resource_record *answer; - union dns_type_result **answers = NULL; - for (int i=0; i<answerCount; i++) - { - answer = parseAnswer(res, startOfAnswer); - union dns_type_result *r = parseAnswerByType(answer, t, res, startOfAnswer); - answers = realloc(answers, (i+1) * sizeof(union dns_type_result)); - answers[i] = r; - } - printAnswers(answers, answerCount, t, res, startOfAnswer, disableHeader); - } - free(startOfAnswer); - free(res->bytes); - free(res); - } - else - { - free(res->bytes); - free(res); - free(resHeader); - free(question->bytes); - free(question); - fprintf(stderr, "isValidResponse failed.\n"); - return -1; - } - } - else - { - free(res->bytes); - free(res); - fprintf(stderr, "Response too short.\n"); - return -1; - } + question = question_form(&qu); + char req[header->len + question->len]; + memcpy(req, header->b, header->len); + memcpy(&req[header->len], question->b, question->len); + res = dns_server_request((char *)&req, header->len + question->len); + if (!res) { + LOG_DEBUG("dns_server_request failed."); + return 1; + } + byte_array_free(header); + if (res->len <= DNS_HEADER_LENGTH) { + fprintf(stderr, "Response is too short.\n"); + return 1; + } + res_header = header_parse(res->b); + if (!response_is_valid(&dns_header_default, res_header)) { + fprintf(stderr, "Response isn't valid.\n"); + return 1; + } + int answer_count = res_header->ancount; + free(res_header); + int *start_of_answer = malloc(sizeof(int)); + *start_of_answer = DNS_HEADER_LENGTH + question->len; + byte_array_free(question); + if (res->len < *start_of_answer) { + fprintf(stderr, "Response doesn't include an answer.\n"); + return 1; + } + struct DnsResourceRecord *answer; + union DnsTypeResult **answers = malloc(answer_count * sizeof(union DnsTypeResult *)); + int i; + for (i = 0; i<answer_count; i++) { + answer = answer_parse(res, start_of_answer); + answers[i] = answer_parse_by_type(answer, res, dns_type, start_of_answer); + resource_record_free(answer); + } + answers_print(answers, answer_count, dns_type, disable_header); + answers_free(answers, answer_count, dns_type); + free(start_of_answer); + byte_array_free(res); return 0; } diff --git a/dinoco.h b/dinoco.h @@ -1,17 +1,28 @@ +#include <stdint.h> + +#define MAX_DOMAIN_LENGTH 253 #define MAX_UDP_MSG_LENGTH 512 #define DNS_HEADER_LENGTH 12 #define MIN_IP_LENGTH 7 #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_RESET "\x1b[0m" -struct byte_array -{ - char *bytes; - int length; +#ifdef DEBUG +#define LOG_DEBUG(msg) fprintf(stderr, msg"\n"); +#else +#define LOG_DEBUG(msg) +#endif + +enum ResponseCode { + RCODE_NO_ERROR, + RCODE_FORMAT_ERROR, + RCODE_SERVER_FAILURE, + RCODE_NAME_ERROR, + RCODE_NOT_IMPLEMENTED, + RCODE_REFUSED }; -enum type -{ +enum Type { TYPE_A = 1, TYPE_NS, TYPE_MD, @@ -31,133 +42,81 @@ enum type TYPE_AAAA = 28 }; -struct typeMapping -{ - char *string; - enum type type; -}; - -const struct typeMapping types[] = { - { "A", TYPE_A }, - { "NS", TYPE_NS }, - { "MD", TYPE_MD }, - { "MF", TYPE_MF }, - { "CNAME", TYPE_CNAME }, - { "SOA", TYPE_SOA }, - { "MB", TYPE_MB }, - { "MG", TYPE_MG }, - { "MR", TYPE_MR }, - { "NULL", TYPE_NULL }, - { "WKS", TYPE_WKS }, - { "PTR", TYPE_PTR }, - { "HINFO", TYPE_HINFO }, - { "MINFO", TYPE_MINFO }, - { "MX", TYPE_MX }, - { "TXT", TYPE_TXT }, - { "AAAA", TYPE_AAAA } -}; - -enum class -{ +enum Class { CLASS_IN = 1, CLASS_CS, CLASS_CH, CLASS_HS }; -enum opcode -{ +enum OperationCode { OPCODE_QUERY, OPCODE_IQUERY, OPCODE_STATUS }; -enum rcode -{ - RCODE_NO_ERROR, - RCODE_FORMAT_ERROR, - RCODE_SERVER_FAILURE, - RCODE_NAME_ERROR, - RCODE_NOT_IMPLEMENTED, - RCODE_REFUSED +struct ByteArray { + uint8_t *b; + size_t len; }; -struct dns_header -{ - uint16_t id; // char ID[2]; +struct DnsHeader { + uint16_t id; bool qr; - enum opcode opcode; + enum OperationCode opcode; bool aa; bool tc; bool rd; bool ra; - enum rcode rcode; - uint16_t qdcount; // char QDCOUNT[2]; - uint16_t ancount; // char ANCOUNT[2]; - uint16_t nscount; // char NSCOUNT[2]; - uint16_t arcount; // char ARCOUNT[2]; -}; - -struct dns_header DNS_HEADER_DEFAULT = { - .id = 55, - .qr = false, - .opcode = OPCODE_QUERY, - .aa = false, - .tc = false, - .rd = true, - .ra = false, - .rcode = RCODE_NO_ERROR, - .qdcount = 1, - .ancount = 0, - .nscount = 0, - .arcount = 0 -}; - -struct dns_question -{ + enum ResponseCode rcode; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +}; + +struct DnsQuestion { char *domain; - enum type type; - enum class class; + enum Type type; + enum Class class; }; // Either Answer, Authority or Additional -struct dns_resource_record -{ +struct DnsResourceRecord { char *domain; - enum type type; - enum class class; + enum Type type; + enum Class class; int32_t ttl; uint16_t rdlength; - struct byte_array *rdata; + struct ByteArray *rdata; }; -struct dns_mx_result -{ +struct DnsMxResult { uint16_t preference; - char *mailDomain; + char *mail_domain; }; -struct dns_a_result +struct DnsAResult { - char *ipAddress; + char *ipv4; }; -struct dns_ns_result +struct DnsNsResult { char *domain; }; -struct dns_cname_result +struct DnsCnameResult { char *domain; }; -struct dns_txt_result +struct DnsTxtResult { char *data; }; -struct dns_soa_result +struct DnsSoaResult { char *mname; char *rname; @@ -168,31 +127,31 @@ struct dns_soa_result uint32_t minimum; }; -struct dns_ptr_result +struct DnsPtrResult { char *domain; }; -struct dns_hinfo_result +struct DnsHinfoResult { char *cpu; char *os; }; -struct dns_aaaa_result +struct DnsAaaaResult { char *ipv6; }; -union dns_type_result +union DnsTypeResult { - struct dns_mx_result *mx; - struct dns_a_result *a; - struct dns_ns_result *ns; - struct dns_cname_result *cname; - struct dns_txt_result *txt; - struct dns_soa_result *soa; - struct dns_ptr_result *ptr; - struct dns_hinfo_result *hinfo; - struct dns_aaaa_result *aaaa; + struct DnsMxResult *mx; + struct DnsAResult *a; + struct DnsNsResult *ns; + struct DnsCnameResult *cname; + struct DnsTxtResult *txt; + struct DnsSoaResult *soa; + struct DnsPtrResult *ptr; + struct DnsHinfoResult *hinfo; + struct DnsAaaaResult *aaaa; };