diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 22d23e6dcdab..b965e949fa3a 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -27,6 +27,7 @@ PSEUDOMODULES += dhcpv6_% PSEUDOMODULES += dhcpv6_client_dns PSEUDOMODULES += dhcpv6_client_ia_pd PSEUDOMODULES += dhcpv6_client_mud_url +PSEUDOMODULES += dns_msg PSEUDOMODULES += ecc_% PSEUDOMODULES += event_% PSEUDOMODULES += event_timeout_ztimer diff --git a/sys/Makefile b/sys/Makefile index 50a11546f538..5f36158cebbe 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -38,6 +38,9 @@ endif ifneq (,$(filter dhcpv6,$(USEMODULE))) DIRS += net/application_layer/dhcpv6 endif +ifneq (,$(filter dns,$(USEMODULE))) + DIRS += net/application_layer/dns +endif ifneq (,$(filter dsm,$(USEMODULE))) DIRS += net/dsm endif @@ -132,7 +135,7 @@ ifneq (,$(filter sock_async_event,$(USEMODULE))) DIRS += net/sock/async/event endif ifneq (,$(filter sock_dns,$(USEMODULE))) - DIRS += net/application_layer/dns + DIRS += net/application_layer/sock_dns endif ifneq (,$(filter sock_util,$(USEMODULE))) DIRS += net/sock diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 49bf49918f50..97f5fb55cea9 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -106,6 +106,10 @@ ifneq (,$(filter dhcpv6_client,$(USEMODULE))) endif endif +ifneq (,$(filter dns_%,$(USEMODULE))) + USEMODULE += dns +endif + ifneq (,$(filter fuzzing,$(USEMODULE))) USEMODULE += netdev_test USEMODULE += gnrc_netif @@ -470,6 +474,7 @@ ifneq (,$(filter sock_async,$(USEMODULE))) endif ifneq (,$(filter sock_dns,$(USEMODULE))) + USEMODULE += dns_msg USEMODULE += sock_udp USEMODULE += sock_util USEMODULE += posix_headers diff --git a/sys/include/net/dns.h b/sys/include/net/dns.h index 09ee148e16f0..6728815309e8 100644 --- a/sys/include/net/dns.h +++ b/sys/include/net/dns.h @@ -24,6 +24,15 @@ extern "C" { #endif +/** + * @name DNS defines + * @{ + */ +#define DNS_TYPE_A (1) +#define DNS_TYPE_AAAA (28) +#define DNS_CLASS_IN (1) +/** @} */ + /** * @name Field lengths * @{ diff --git a/sys/include/net/dns/msg.h b/sys/include/net/dns/msg.h new file mode 100644 index 000000000000..8faa54f4a1b6 --- /dev/null +++ b/sys/include/net/dns/msg.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2021 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_dns_msg DNS message parser and composer + * @ingroup net_dns + * @brief Parsing and composition of DNS messages + * @{ + * + * @file + * @brief Definitions for parsing and composition of DNS messages + * + * @author Martine Lenders + */ +#ifndef NET_DNS_MSG_H +#define NET_DNS_MSG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup net_dns_msg_conf DNS message parsing and composition configuration + * @ingroup config + * @{ + */ +/** + * @brief maximum DNS message length + */ +#ifndef CONFIG_DNS_MSG_LEN +#define CONFIG_DNS_MSG_LEN (128U) +#endif /* CONFIG_DNS_MSG_LEN */ +/** @} */ + +/** + * @brief DNS internal structure + * + * @see [RFC 1035, section 4.1.1](https://tools.ietf.org/html/rfc1035#section-4.1.1) + */ +typedef struct { + uint16_t id; /**< identifier */ + /** + * @brief flags + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA|Z |AD|CD| RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * - QR: Query (0) or Response (1) + * - Opcode: Kind of query + * - AA: Authoritative Answer + * - TC: Truncated + * - RD: Recursion Desired + * - RA: Recursion Available + * - Z: Reserved + * - AD: Authentic Data (see [RFC 4035](https://tools.ietf.org/html/4035)) + * - CD: Checking Disabled (see [RFC 4035](https://tools.ietf.org/html/4035)) + * - RCODE: Response Code + */ + uint16_t flags; + uint16_t qdcount; /**< number of question entries */ + uint16_t ancount; /**< number of answer resource records */ + uint16_t nscount; /**< number of name server resource records */ + uint16_t arcount; /**< number of additional records */ + uint8_t payload[]; /**< payload */ +} dns_hdr_t; + +/** + * @brief Composes a DNS query message + * + * The query will request an A or AAAA IN record for @p domain_name depending on + * @p family: + * + * - When @p family is `AF_INET` or `AF_UNSPEC` a query for an A record will be + * added + * - When @p family is `AF_INET6` or `AF_UNSPEC` a query for an AAAA record will + * be added + * + * @param[out] dns_buf A buffer of length @ref CONFIG_DNS_MSG_LEN + * @param[in] domain_name The domain name to query. + * @param[in] id The ID for the query. + * @param[in] family Either `AF_UNSPEC`, `AF_INET`, or `AF_INET6`. + * Determines the address records for @p domain_name + * queried. + * + * @return Size of the composed query in bytes. + */ +size_t dns_msg_compose_query(void *dns_buf, const char *domain_name, + uint16_t id, int family); + +/** + * @brief Parses a DNS response message + * + * @param[in] buf The message to parse. + * @param[in] len Length of @p buf. + * @param[in] family The address family used to compose the query for + * this response (see @ref dns_msg_compose_query()) + * @param[out] addr_out The IP address returned by the response. + * + * @return Length of the @p addr_out on success. + * @return -EBADMSG, when an address corresponding to @p family can not be found + * in @p buf. + */ +int dns_msg_parse_reply(const uint8_t *buf, size_t len, int family, + void *addr_out); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_DNS_MSG_H */ +/** @} */ diff --git a/sys/include/net/sock/dns.h b/sys/include/net/sock/dns.h index f7d4d82261df..5a9db35ea67f 100644 --- a/sys/include/net/sock/dns.h +++ b/sys/include/net/sock/dns.h @@ -27,38 +27,22 @@ #include #include +#include "net/dns/msg.h" + #include "net/sock/udp.h" #ifdef __cplusplus extern "C" { #endif -/** - * @brief DNS internal structure - */ -typedef struct { - uint16_t id; /**< read */ - uint16_t flags; /**< DNS */ - uint16_t qdcount; /**< RFC */ - uint16_t ancount; /**< for */ - uint16_t nscount; /**< detailed */ - uint16_t arcount; /**< explanations */ - uint8_t payload[]; /**< !! */ -} sock_dns_hdr_t; - /** * @name DNS defines * @{ */ -#define DNS_TYPE_A (1) -#define DNS_TYPE_AAAA (28) -#define DNS_CLASS_IN (1) - #define SOCK_DNS_PORT (53) #define SOCK_DNS_RETRIES (2) -#define SOCK_DNS_BUF_LEN (128) /* we're in embedded context. */ -#define SOCK_DNS_MAX_NAME_LEN (SOCK_DNS_BUF_LEN - sizeof(sock_dns_hdr_t) - 4) +#define SOCK_DNS_MAX_NAME_LEN (CONFIG_DNS_MSG_LEN - sizeof(dns_hdr_t) - 4) /** @} */ /** diff --git a/sys/net/application_layer/dns/Makefile b/sys/net/application_layer/dns/Makefile index 5e55b6e793a1..8e2c538ab6e6 100644 --- a/sys/net/application_layer/dns/Makefile +++ b/sys/net/application_layer/dns/Makefile @@ -1,2 +1,5 @@ -MODULE=sock_dns +SRC := + +SUBMODULE := 1 + include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/dns/dns.c b/sys/net/application_layer/dns/msg.c similarity index 60% rename from sys/net/application_layer/dns/dns.c rename to sys/net/application_layer/dns/msg.c index b9e80029c469..ea9d0854d3c5 100644 --- a/sys/net/application_layer/dns/dns.c +++ b/sys/net/application_layer/dns/msg.c @@ -7,31 +7,24 @@ */ /** - * @ingroup net_sock_dns * @{ + * * @file - * @brief sock DNS client implementation * @author Kaspar Schleiser - * @} + * @author Martine Lenders */ #include +#include #include -#include #include "net/dns.h" -#include "net/sock/udp.h" -#include "net/sock/dns.h" #ifdef RIOT_VERSION #include "byteorder.h" #endif -/* min domain name length is 1, so minimum record length is 7 */ -#define DNS_MIN_REPLY_LEN (unsigned)(sizeof(sock_dns_hdr_t ) + 7) - -/* global DNS server UDP endpoint */ -sock_udp_ep_t sock_dns_server; +#include "net/dns/msg.h" static ssize_t _enc_domain_name(uint8_t *out, const char *domain_name) { @@ -68,14 +61,15 @@ static unsigned _put_short(uint8_t *out, uint16_t val) return 2; } -static unsigned _get_short(uint8_t *buf) +static unsigned _get_short(const uint8_t *buf) { uint16_t _tmp; memcpy(&_tmp, buf, 2); return _tmp; } -static ssize_t _skip_hostname(const uint8_t *buf, size_t len, uint8_t *bufpos) +static ssize_t _skip_hostname(const uint8_t *buf, size_t len, + const uint8_t *bufpos) { const uint8_t *buflim = buf + len; unsigned res = 0; @@ -102,12 +96,47 @@ static ssize_t _skip_hostname(const uint8_t *buf, size_t len, uint8_t *bufpos) return res + 1; } -static int _parse_dns_reply(uint8_t *buf, size_t len, void* addr_out, int family) +size_t dns_msg_compose_query(void *dns_buf, const char *domain_name, + uint16_t id, int family) { - const uint8_t *buflim = buf + len; - sock_dns_hdr_t *hdr = (sock_dns_hdr_t*) buf; + uint8_t *buf = dns_buf; + + dns_hdr_t *hdr = (dns_hdr_t*) buf; + memset(hdr, 0, sizeof(*hdr)); + hdr->id = id; + hdr->flags = htons(0x0120); + hdr->qdcount = htons(1 + (family == AF_UNSPEC)); + uint8_t *bufpos = buf + sizeof(*hdr); + unsigned _name_ptr; + if ((family == AF_INET6) || (family == AF_UNSPEC)) { + _name_ptr = (bufpos - buf); + bufpos += _enc_domain_name(bufpos, domain_name); + bufpos += _put_short(bufpos, htons(DNS_TYPE_AAAA)); + bufpos += _put_short(bufpos, htons(DNS_CLASS_IN)); + } + + if ((family == AF_INET) || (family == AF_UNSPEC)) { + if (family == AF_UNSPEC) { + bufpos += _put_short(bufpos, htons((0xc000) | (_name_ptr))); + } + else { + bufpos += _enc_domain_name(bufpos, domain_name); + } + bufpos += _put_short(bufpos, htons(DNS_TYPE_A)); + bufpos += _put_short(bufpos, htons(DNS_CLASS_IN)); + } + return bufpos - buf; +} + +int dns_msg_parse_reply(const uint8_t *buf, size_t len, int family, + void *addr_out) +{ + const uint8_t *buflim = buf + len; + const dns_hdr_t *hdr = (dns_hdr_t *)buf; + const uint8_t *bufpos = buf + sizeof(*hdr); + /* skip all queries that are part of the reply */ for (unsigned n = 0; n < ntohs(hdr->qdcount); n++) { ssize_t tmp = _skip_hostname(buf, len, bufpos); @@ -166,78 +195,7 @@ static int _parse_dns_reply(uint8_t *buf, size_t len, void* addr_out, int family return addrlen; } - return -1; + return -EBADMSG; } -int sock_dns_query(const char *domain_name, void *addr_out, int family) -{ - static uint8_t dns_buf[SOCK_DNS_BUF_LEN]; - - if (sock_dns_server.port == 0) { - return -ECONNREFUSED; - } - - if (strlen(domain_name) > SOCK_DNS_MAX_NAME_LEN) { - return -ENOSPC; - } - - sock_udp_t sock_dns; - - ssize_t res = sock_udp_create(&sock_dns, NULL, &sock_dns_server, 0); - if (res) { - goto out; - } - - uint16_t id = 0; /* random? */ - for (int i = 0; i < SOCK_DNS_RETRIES; i++) { - uint8_t *buf = dns_buf; - - sock_dns_hdr_t *hdr = (sock_dns_hdr_t*) buf; - memset(hdr, 0, sizeof(*hdr)); - hdr->id = id; - hdr->flags = htons(0x0120); - hdr->qdcount = htons(1 + (family == AF_UNSPEC)); - - uint8_t *bufpos = buf + sizeof(*hdr); - - unsigned _name_ptr; - if ((family == AF_INET6) || (family == AF_UNSPEC)) { - _name_ptr = (bufpos - buf); - bufpos += _enc_domain_name(bufpos, domain_name); - bufpos += _put_short(bufpos, htons(DNS_TYPE_AAAA)); - bufpos += _put_short(bufpos, htons(DNS_CLASS_IN)); - } - - if ((family == AF_INET) || (family == AF_UNSPEC)) { - if (family == AF_UNSPEC) { - bufpos += _put_short(bufpos, htons((0xc000) | (_name_ptr))); - } - else { - bufpos += _enc_domain_name(bufpos, domain_name); - } - bufpos += _put_short(bufpos, htons(DNS_TYPE_A)); - bufpos += _put_short(bufpos, htons(DNS_CLASS_IN)); - } - - res = sock_udp_send(&sock_dns, buf, (bufpos-buf), NULL); - if (res <= 0) { - continue; - } - res = sock_udp_recv(&sock_dns, dns_buf, sizeof(dns_buf), 1000000LU, NULL); - if (res > 0) { - if (res > (int)DNS_MIN_REPLY_LEN) { - if ((res = _parse_dns_reply(dns_buf, res, addr_out, - family)) > 0) { - goto out; - } - } - else { - res = -EBADMSG; - } - } - } - -out: - sock_udp_close(&sock_dns); - return res; -} +/** @} */ diff --git a/sys/net/application_layer/sock_dns/Makefile b/sys/net/application_layer/sock_dns/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/net/application_layer/sock_dns/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/sock_dns/dns.c b/sys/net/application_layer/sock_dns/dns.c new file mode 100644 index 000000000000..3a8fa6ea3dd4 --- /dev/null +++ b/sys/net/application_layer/sock_dns/dns.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup net_sock_dns + * @{ + * @file + * @brief sock DNS client implementation + * @author Kaspar Schleiser + * @} + */ + +#include + +#include "net/dns.h" +#include "net/dns/msg.h" +#include "net/sock/udp.h" +#include "net/sock/dns.h" + +/* min domain name length is 1, so minimum record length is 7 */ +#define DNS_MIN_REPLY_LEN (unsigned)(sizeof(dns_hdr_t ) + 7) + +/* global DNS server UDP endpoint */ +sock_udp_ep_t sock_dns_server; + +int sock_dns_query(const char *domain_name, void *addr_out, int family) +{ + static uint8_t dns_buf[CONFIG_DNS_MSG_LEN]; + + if (sock_dns_server.port == 0) { + return -ECONNREFUSED; + } + + if (strlen(domain_name) > SOCK_DNS_MAX_NAME_LEN) { + return -ENOSPC; + } + + sock_udp_t sock_dns; + + ssize_t res = sock_udp_create(&sock_dns, NULL, &sock_dns_server, 0); + if (res) { + goto out; + } + + uint16_t id = 0; + for (int i = 0; i < SOCK_DNS_RETRIES; i++) { + size_t buflen = dns_msg_compose_query(dns_buf, domain_name, id, family); + + res = sock_udp_send(&sock_dns, dns_buf, buflen, NULL); + if (res <= 0) { + continue; + } + res = sock_udp_recv(&sock_dns, dns_buf, sizeof(dns_buf), 1000000LU, NULL); + if (res > 0) { + if (res > (int)DNS_MIN_REPLY_LEN) { + if ((res = dns_msg_parse_reply(dns_buf, res, family, + addr_out)) > 0) { + goto out; + } + } + else { + res = -EBADMSG; + } + } + } + +out: + sock_udp_close(&sock_dns); + return res; +}