From 3583eb1a470ae6d1ccd2f12e5664594414e33a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20De=20Keersmaeker?= <francois.dekeersmaeker@uclouvain.be> Date: Wed, 10 Jul 2024 11:09:38 +0200 Subject: [PATCH] Updated DNS lib --- include/dns.h | 42 ++++++++++++-- src/dns.c | 149 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 170 insertions(+), 21 deletions(-) diff --git a/include/dns.h b/include/dns.h index 7b6975e..0384b90 100644 --- a/include/dns.h +++ b/include/dns.h @@ -1,5 +1,6 @@ /** - * @file include/dns.h + * @file include/parsers/dns.h + * @author François De Keersmaeker (francois.dekeersmaeker@uclouvain.be) * @brief DNS message parser * @date 2022-09-09 * @@ -15,6 +16,9 @@ #include <stdint.h> #include <stdbool.h> #include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> #include <arpa/inet.h> #include "packet_utils.h" #include "dns_map.h" @@ -71,7 +75,7 @@ typedef struct dns_header { */ typedef struct dns_question { char *qname; - dns_rr_type_t qtype; + uint16_t qtype; uint16_t qclass; } dns_question_t; @@ -89,7 +93,7 @@ typedef union { */ typedef struct dns_resource_record { char *name; - dns_rr_type_t rtype; + uint16_t rtype; uint16_t rclass; uint32_t ttl; uint16_t rdlength; @@ -187,10 +191,10 @@ dns_question_t* dns_get_question(dns_question_t *questions, uint16_t qdcount, ch /** * @brief Retrieve the IP addresses corresponding to a given domain name in a DNS Answers list. - * + * * Searches a DNS Answer list for a specific domain name and returns the corresponding IP address. * Processes each Answer recursively if the Answer Type is a CNAME. - * + * * @param answers DNS Answers list to search in * @param ancount number of Answers in the list * @param domain_name domain name to search for @@ -199,6 +203,34 @@ dns_question_t* dns_get_question(dns_question_t *questions, uint16_t qdcount, ch ip_list_t dns_get_ip_from_name(dns_resource_record_t *answers, uint16_t ancount, char *domain_name); +///// COMMUNICATE ///// + +/** + * @brief Convert domain name to message format. + * + * @param dst converted domain name + * @param src domain name to convert + */ +void dns_convert_qname(char *dst, char *src, uint16_t len); + +/** + * @brief Send a DNS query for the given domain name. + * + * @param qname domain name to query for + * @param sockfd socket file descriptor + * @param server_addr DNS server IPv4 address + */ +void dns_send_query(char *qname, int sockfd, struct sockaddr_in *server_addr); + +/** + * @brief Receive a DNS response + * + * @param sockfd socket file descriptor + * @param server_addr DNS server IPv4 address + */ +dns_message_t dns_receive_response(int sockfd, struct sockaddr_in *server_addr); + + ///// DESTROY ///// /** diff --git a/src/dns.c b/src/dns.c index 5c31265..cf69502 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1,5 +1,6 @@ /** - * @file src/dns.c + * @file src/parsers/dns.c + * @author François De Keersmaeker (francois.dekeersmaeker@uclouvain.be) * @brief DNS message parser * @date 2022-09-09 * @@ -191,7 +192,7 @@ dns_resource_record_t* dns_parse_rrs(uint16_t count, uint8_t *data, uint16_t *of // Parse domain name (rrs + i)->name = dns_parse_domain_name(data, offset); // Parse rtype, rclass and TTL - dns_rr_type_t rtype = ntohs(*((uint16_t *) (data + *offset))); + uint16_t rtype = ntohs(*((uint16_t *) (data + *offset))); (rrs + i)->rtype = rtype; (rrs + i)->rclass = ntohs(*((uint16_t *) (data + *offset + 2))) & DNS_CLASS_MASK; (rrs + i)->ttl = ntohl(*((uint32_t *) (data + *offset + 4))); @@ -199,7 +200,7 @@ dns_resource_record_t* dns_parse_rrs(uint16_t count, uint8_t *data, uint16_t *of uint16_t rdlength = ntohs(*((uint16_t *) (data + *offset + 8))); (rrs + i)->rdlength = rdlength; *offset += 10; - (rrs + i)->rdata = dns_parse_rdata(rtype, rdlength, data, offset); + (rrs + i)->rdata = dns_parse_rdata((dns_rr_type_t) rtype, rdlength, data, offset); } return rrs; } @@ -326,39 +327,48 @@ dns_question_t* dns_get_question(dns_question_t *questions, uint16_t qdcount, ch /** * @brief Retrieve the IP addresses corresponding to a given domain name in a DNS Answers list. - * + * * Searches a DNS Answer list for a specific domain name and returns the corresponding IP address. * Processes each Answer recursively if the Answer Type is a CNAME. - * + * * @param answers DNS Answers list to search in * @param ancount number of Answers in the list * @param domain_name domain name to search for * @return struct ip_list representing the list of corresponding IP addresses */ -ip_list_t dns_get_ip_from_name(dns_resource_record_t *answers, uint16_t ancount, char *domain_name) { +ip_list_t dns_get_ip_from_name(dns_resource_record_t *answers, uint16_t ancount, char *domain_name) +{ ip_list_t ip_list; ip_list.ip_count = 0; ip_list.ip_addresses = NULL; char *cname = domain_name; - for (uint16_t i = 0; i < ancount; i++) { - if (strcmp((answers + i)->name, cname) == 0) { + for (uint16_t i = 0; i < ancount; i++) + { + if (strcmp((answers + i)->name, cname) == 0) + { dns_rr_type_t rtype = (answers + i)->rtype; if (rtype == A || rtype == AAAA) { // Handle IP list length - if (ip_list.ip_addresses == NULL) { - ip_list.ip_addresses = (ip_addr_t *) malloc(sizeof(ip_addr_t)); - } else { + if (ip_list.ip_addresses == NULL) + { + ip_list.ip_addresses = (ip_addr_t *)malloc(sizeof(ip_addr_t)); + } + else + { void *realloc_ptr = realloc(ip_list.ip_addresses, (ip_list.ip_count + 1) * sizeof(ip_addr_t)); - if (realloc_ptr == NULL) { + if (realloc_ptr == NULL) + { // Handle realloc error free(ip_list.ip_addresses); fprintf(stderr, "Error reallocating memory for IP list.\n"); ip_list.ip_count = 0; ip_list.ip_addresses = NULL; return ip_list; - } else { - ip_list.ip_addresses = (ip_addr_t*) realloc_ptr; + } + else + { + ip_list.ip_addresses = (ip_addr_t *)realloc_ptr; } } // Handle IP version and value @@ -375,6 +385,113 @@ ip_list_t dns_get_ip_from_name(dns_resource_record_t *answers, uint16_t ancount, } +///// COMMUNICATE ///// + +/** + * @brief Convert domain name to message format. + * + * @param dst converted domain name + * @param src domain name to convert + */ +void dns_convert_qname(char *dst, char *src, uint16_t len) { + int lock = 0; // Points to the next index to fill in the output array + + // Start by iterating over each character in the input domain name + for (int i = 0; i < len; i++) + { + int segment_len = 0; // Length of the current segment + + // Count the length of the current segment until we hit a dot or the end of the string + while (*(src + i) != '.' && *(src + i) != '\0') + { + *(dst + lock + 1 + segment_len) = *(src + i); + segment_len++; + i++; + } + + *(dst + lock) = segment_len; // Prefix the segment with its length + lock += segment_len + 1; // Move the index past the current segment + } + + *(dst + lock) = '\0'; // Null terminate the DNS label format string +} + +/** + * @brief Send a DNS query for the given domain name. + * + * @param qname domain name to query for + * @param sockfd socket file descriptor + * @param server_addr DNS server IPv4 address + */ +void dns_send_query(char *qname, int sockfd, struct sockaddr_in *server_addr) { + // Buffer that will contain the message + uint16_t qname_len = strlen(qname); + uint16_t qname_labels_len = qname_len + sizeof(uint8_t) * 2; + uint16_t dns_questions_size = qname_labels_len + sizeof(uint16_t) * 2; + uint16_t dns_message_size = DNS_HEADER_SIZE + dns_questions_size; + uint8_t *buffer = (uint8_t *) malloc(dns_message_size); + + // Populate DNS Header fields + dns_header_t dns_header; + dns_header.id = htons((uint16_t) getpid()); + dns_header.flags = htons((uint16_t) (0b0000000100000000)); + dns_header.qr = 0; // Query: qr = 0 + dns_header.qdcount = htons((uint16_t) 1); // Only 1 question + dns_header.ancount = 0; + dns_header.nscount = 0; + dns_header.arcount = 0; + + // Populate DNS Question fields + dns_question_t dns_question; + dns_question.qname = (char *)malloc(qname_labels_len); + dns_convert_qname(dns_question.qname, qname, qname_len); + dns_question.qtype = htons((uint16_t) A); + dns_question.qclass = htons((uint16_t) 1); + + // Copy all DNS fields + memcpy(buffer, &dns_header, sizeof(uint16_t) * 2); + memcpy(buffer + sizeof(uint16_t) * 2, &(dns_header.qdcount), sizeof(uint16_t) * 4); + memcpy(buffer + DNS_HEADER_SIZE, dns_question.qname, qname_labels_len); + memcpy(buffer + DNS_HEADER_SIZE + qname_labels_len, &(dns_question.qtype), sizeof(uint16_t) * 2); + + // Send DNS message + if (sendto(sockfd, buffer, dns_message_size, 0, (struct sockaddr *)server_addr, sizeof(*server_addr)) < 0) + { + perror("Failed sending DNS query."); + } + + // Free memory + free(dns_question.qname); + free(buffer); +} + +/** + * @brief Receive a DNS response. + * + * @param sockfd socket file descriptor + * @param server_addr DNS server IPv4 address + * @return received DNS message + */ +dns_message_t dns_receive_response(int sockfd, struct sockaddr_in *server_addr) +{ + // Receiving buffer + int bufsize = 65536; + uint8_t *buffer = (uint8_t *)malloc(bufsize); + + // Await response + int n = recvfrom(sockfd, (char *)buffer, bufsize, 0, NULL, NULL); + if (n < 0) + { + perror("Failed receiving DNS response."); + exit(-1); + } + + // Parse received DNS message + dns_message_t dns_message = dns_parse_message(buffer); + free(buffer); + return dns_message; +} + ///// DESTROY ///// /** @@ -410,7 +527,7 @@ static void dns_free_rrs(dns_resource_record_t *rrs, uint16_t count) { dns_resource_record_t rr = *(rrs + i); if (rr.rdlength > 0) { free(rr.name); - dns_free_rdata(rr.rdata, rr.rtype); + dns_free_rdata(rr.rdata, (dns_rr_type_t) rr.rtype); } } free(rrs); @@ -539,7 +656,7 @@ void dns_print_rr(char* section_name, dns_resource_record_t rr) { printf(" Class: %hd\n", rr.rclass); printf(" TTL [s]: %d\n", rr.ttl); printf(" Data length: %hd\n", rr.rdlength); - printf(" RDATA: %s\n", dns_rdata_to_str(rr.rtype, rr.rdlength, rr.rdata)); + printf(" RDATA: %s\n", dns_rdata_to_str((dns_rr_type_t) rr.rtype, rr.rdlength, rr.rdata)); } /** -- GitLab