From f575292e20d4ab8ce4f5f534de0416a1fbb5809e Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Thu, 16 Sep 2021 12:08:57 +0200 Subject: [PATCH 1/3] sock_dodtls: Initial import of a DNS over DTLS client --- sys/Makefile | 3 + sys/Makefile.dep | 9 + sys/include/net/sock/dodtls.h | 132 +++++++++ sys/net/application_layer/Kconfig | 1 + sys/net/application_layer/sock_dodtls/Kconfig | 24 ++ .../application_layer/sock_dodtls/Makefile | 1 + .../sock_dodtls/sock_dodtls.c | 274 ++++++++++++++++++ 7 files changed, 444 insertions(+) create mode 100644 sys/include/net/sock/dodtls.h create mode 100644 sys/net/application_layer/sock_dodtls/Kconfig create mode 100644 sys/net/application_layer/sock_dodtls/Makefile create mode 100644 sys/net/application_layer/sock_dodtls/sock_dodtls.c diff --git a/sys/Makefile b/sys/Makefile index aa7e941897..8844fa43d8 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -146,6 +146,9 @@ endif ifneq (,$(filter sock_dns_mock,$(USEMODULE))) DIRS += net/application_layer/sock_dns_mock endif +ifneq (,$(filter sock_dodtls,$(USEMODULE))) + DIRS += net/application_layer/sock_dodtls +endif ifneq (,$(filter sock_util,$(USEMODULE))) DIRS += net/sock endif diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 7860bd77e9..0e02154a7b 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -602,6 +602,15 @@ ifneq (,$(filter sock_dns,$(USEMODULE))) USEMODULE += posix_headers endif +ifneq (,$(filter sock_dodtls,$(USEMODULE))) + USEMODULE += dns_msg + USEMODULE += sock_dtls + USEMODULE += sock_udp + USEMODULE += sock_util + USEMODULE += posix_headers + USEMODULE += ztimer_msec +endif + ifneq (,$(filter dns_cache,$(USEMODULE))) USEMODULE += ztimer_msec USEMODULE += checksum diff --git a/sys/include/net/sock/dodtls.h b/sys/include/net/sock/dodtls.h new file mode 100644 index 0000000000..feac88e050 --- /dev/null +++ b/sys/include/net/sock/dodtls.h @@ -0,0 +1,132 @@ +/* + * 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_sock_dodtls DNS over DTLS sock API + * @ingroup net_sock + * + * @brief Sock DNS over DTLS client + * + * @see [RFC 8094](https://datatracker.ietf.org/doc/html/rfc8094) + * + * @experimental This implementation is in an experimental state. + * RFC 8094 requires DNS over TLS (DoT) as a fall-back for the + * [PMTU issues](https://datatracker.ietf.org/doc/html/rfc8094#section-5)). + * This fallback is not in place in this implementation. + * Consequently, [EDNS(0)](https://datatracker.ietf.org/doc/html/rfc6891) + * to negotiate maximum response size is also not in place. + * + * @{ + * + * @file + * @brief DNS over DTLS sock definitions + * + * @author Martine S. Lenders + */ + +#ifndef NET_SOCK_DODTLS_H +#define NET_SOCK_DODTLS_H + +#include "net/sock/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name DNS over DTLS defines + * @{ + */ +#define SOCK_DODTLS_PORT (853) /**< Default DNS over DTLS server port */ + +/** + * @defgroup net_sock_dodtls_conf DNS over DTLS compile-time configuration + * @ingroup config + * @{ + */ +#ifndef CONFIG_SOCK_DODTLS_RETRIES +#define CONFIG_SOCK_DODTLS_RETRIES (2) /**< Number of DNS over DTLS query retries */ +#endif + +/** + * @brief Timeout for DNS over DTLS queries in milliseconds + */ +#ifndef CONFIG_SOCK_DODTLS_TIMEOUT_MS +#define CONFIG_SOCK_DODTLS_TIMEOUT_MS (1000U) +#endif +/** @} */ + +/** + * @brief Maximum name length for a DNS over DTLS query + */ +#define SOCK_DODTLS_MAX_NAME_LEN (CONFIG_DNS_MSG_LEN - sizeof(dns_hdr_t) - 4) +/** @} */ + +/** + * @brief Get IP address for DNS name + * + * This function will synchronously try to resolve a DNS A or AAAA record by contacting + * the DNS server specified in the global variable @ref sock_dns_server. + * + * By supplying AF_INET, AF_INET6 or AF_UNSPEC in @p family requesting of A + * records (IPv4), AAAA records (IPv6) or both can be selected. + * + * This function will return the first DNS record it receives. IF both A and + * AAAA are requested, AAAA will be preferred. + * + * @note @p addr_out needs to provide space for any possible result! + * (4byte when family==AF_INET, 16byte otherwise) + * + * @param[in] domain_name DNS name to resolve into address + * @param[out] addr_out buffer to write result into + * @param[in] family Either AF_INET, AF_INET6 or AF_UNSPEC + * + * @return the size of the resolved address on success + * @return -ECONNREFUSED, when a DNS over DTLS server is not configured. + * @return -ENOSPC, when the length of @p domain_name is greater than @ref + * SOCK_DODTLS_MAX_NAME_LEN. + * @return -EBADSG, when the DNS reply is not parseable. + */ +int sock_dodtls_query(const char *domain_name, void *addr_out, int family); + +/** + * @brief Get currently configured DNS over DTLS server endpoint + * + * @param[out] server The currently configured DNS over DTLS server endpoint. + * May not be NULL on input. + * + * @return 0 if @p server was set. + * @return -ENOTCONN, when currently no server is configured. + */ +int sock_dodtls_get_server(sock_udp_ep_t *server); + +/** + * @brief Configure and establish session with DNS over DTLS server + * + * @param[in] server A DNS over DTLS server endpoint. May be NULL to + * destroy the session with and unset the currently + * configured server. + * @param[in] creds DTLS credentials for the server (see @ref net_credman). + * May be NULL, when @p server is also NULL. + * + * @return 0 on success. + * @return -EINVAL, if @p cred contains invalid values. + * @return -ENOSPC, if @p cred does not fit into @ref net_credman. + * @return Any other negative errno potentially returned by @ref sock_udp_create(), + * @ref sock_dtls_create(), @ref sock_dtls_session_init() or + * @ref sock_dtls_recv(). + */ +int sock_dodtls_set_server(const sock_udp_ep_t *server, + const credman_credential_t *creds); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SOCK_DODTLS_H */ +/** @} */ diff --git a/sys/net/application_layer/Kconfig b/sys/net/application_layer/Kconfig index 59c6701441..0f521861fe 100644 --- a/sys/net/application_layer/Kconfig +++ b/sys/net/application_layer/Kconfig @@ -15,6 +15,7 @@ endmenu # CoAP rsource "cord/Kconfig" rsource "dhcpv6/Kconfig" rsource "dns/Kconfig" +rsource "sock_dodtls/Kconfig" menu "MQTT-SN" diff --git a/sys/net/application_layer/sock_dodtls/Kconfig b/sys/net/application_layer/sock_dodtls/Kconfig new file mode 100644 index 0000000000..b78f15550a --- /dev/null +++ b/sys/net/application_layer/sock_dodtls/Kconfig @@ -0,0 +1,24 @@ +# 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. +# +menuconfig KCONFIG_USEMODULE_SOCK_DODTLS + bool "Configure DNS over DTLS" + depends on USEMODULE_SOCK_DODTLS + help + Configure DNS over DTLS using Kconfig. + +if KCONFIG_USEMODULE_SOCK_DODTLS + +config SOCK_DODTLS_RETRIES + int "Number of DNS over DTLS query retries" + default 2 + +config SOCK_DODTLS_TIMEOUT_MS + int "Timeout for DNS over DTLS queries in milliseconds" + default 1000 + +endif # KCONFIG_USEMODULE_SOCK_DODTLS diff --git a/sys/net/application_layer/sock_dodtls/Makefile b/sys/net/application_layer/sock_dodtls/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/application_layer/sock_dodtls/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/sock_dodtls/sock_dodtls.c b/sys/net/application_layer/sock_dodtls/sock_dodtls.c new file mode 100644 index 0000000000..421638304e --- /dev/null +++ b/sys/net/application_layer/sock_dodtls/sock_dodtls.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * 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. + */ + +/** + * @ingroup net_sock_dodtls + * @{ + * @file + * @brief sock DNS client implementation + * @author Kaspar Schleiser + * @author Martine S. Lenders + * @} + */ + +#include +#include +#include + +#include "mutex.h" +#include "net/credman.h" +#include "net/dns.h" +#include "net/dns/cache.h" +#include "net/dns/msg.h" +#include "net/iana/portrange.h" +#include "net/sock/dtls.h" +#include "net/sock/udp.h" +#include "net/sock/dodtls.h" +#include "random.h" +#include "ztimer.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* min domain name length is 1, so minimum record length is 7 */ +#define SOCK_DODTLS_MIN_REPLY_LEN (unsigned)(sizeof(dns_hdr_t) + 7) +/* see https://datatracker.ietf.org/doc/html/rfc8094#section-3.1 */ +#define SOCK_DODTLS_SESSION_TIMEOUT_MS (15U * MS_PER_SEC) +#define SOCK_DODTLS_SESSION_RECV_TIMEOUT_MS (1U * MS_PER_SEC) + +/* Socks to the DNS over DTLS server */ +static uint8_t _dns_buf[CONFIG_DNS_MSG_LEN]; +static sock_udp_t _udp_sock; +static sock_dtls_t _dtls_sock; +static sock_dtls_session_t _server_session; +/* Mutex to access server sock */ +static mutex_t _server_mutex = MUTEX_INIT; +/* Type of the server credentials, stored for eventual credential deletion */ +static credman_type_t _cred_type = CREDMAN_TYPE_EMPTY; +/* Tag of the server credentials, stored for eventual credential deletion */ +static credman_tag_t _cred_tag = CREDMAN_TAG_EMPTY; +static uint16_t _id = 0; + +static inline bool _server_set(void); +static int _connect_server(const sock_udp_ep_t *server, + const credman_credential_t *creds); +static int _disconnect_server(void); +static uint32_t _now_ms(void); +static void _sleep_ms(uint32_t delay); + +int sock_dodtls_query(const char *domain_name, void *addr_out, int family) +{ + int res; + uint16_t id; + + if (strlen(domain_name) > SOCK_DODTLS_MAX_NAME_LEN) { + return -ENOSPC; + } + res = dns_cache_query(domain_name, addr_out, family); + if (res) { + return res; + } + if (!_server_set()) { + return -ECONNREFUSED; + } + + mutex_lock(&_server_mutex); + id = _id++; + for (int i = 0; i < CONFIG_SOCK_DODTLS_RETRIES; i++) { + uint32_t timeout = CONFIG_SOCK_DODTLS_TIMEOUT_MS * US_PER_MS; + uint32_t start, send_duration; + size_t buflen = dns_msg_compose_query(_dns_buf, domain_name, id, + family); + + start = _now_ms(); + res = sock_dtls_send(&_dtls_sock, &_server_session, + _dns_buf, buflen, timeout); + send_duration = _now_ms() - start; + if (send_duration > CONFIG_SOCK_DODTLS_TIMEOUT_MS) { + return -ETIMEDOUT; + } + timeout -= send_duration; + if (res <= 0) { + _sleep_ms(timeout); + } + res = sock_dtls_recv(&_dtls_sock, &_server_session, + _dns_buf, sizeof(_dns_buf), timeout); + if (res > 0) { + if (res > (int)SOCK_DODTLS_MIN_REPLY_LEN) { + uint32_t ttl = 0; + + if ((res = dns_msg_parse_reply(_dns_buf, res, family, + addr_out, &ttl)) > 0) { + dns_cache_add(domain_name, addr_out, res, ttl); + goto out; + } + } + else { + res = -EBADMSG; + } + } + } + +out: + memset(_dns_buf, 0, sizeof(_dns_buf)); /* flush-out unencrypted data */ + mutex_unlock(&_server_mutex); + return res; +} + +int sock_dodtls_get_server(sock_udp_ep_t *server) +{ + int res = -ENOTCONN; + + assert(server != NULL); + mutex_lock(&_server_mutex); + if (_server_set()) { + sock_udp_get_remote(&_udp_sock, server); + res = 0; + } + mutex_unlock(&_server_mutex); + return res; +} + +int sock_dodtls_set_server(const sock_udp_ep_t *server, + const credman_credential_t *creds) +{ + return (server == NULL) + ? _disconnect_server() + : _connect_server(server, creds); +} + +static inline bool _server_set(void) +{ + return _cred_type != CREDMAN_TYPE_EMPTY; +} + +static void _close_session(credman_tag_t creds_tag, credman_type_t creds_type) +{ + sock_dtls_session_destroy(&_dtls_sock, &_server_session); + sock_dtls_close(&_dtls_sock); + credman_delete(creds_tag, creds_type); + sock_udp_close(&_udp_sock); +} + +static int _connect_server(const sock_udp_ep_t *server, + const credman_credential_t *creds) +{ + int res = -EADDRINUSE; + uint32_t start, try_start, timeout = SOCK_DODTLS_SESSION_RECV_TIMEOUT_MS; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + + /* server != NULL is checked in sock_dodtls_set_server() */ + assert(creds != NULL); + mutex_lock(&_server_mutex); + while (res == -EADDRINUSE) { + /* choose random ephemeral port, since DTLS requires a local port */ + local.port = IANA_DYNAMIC_PORTRANGE_MIN + + (random_uint32() % (IANA_SYSTEM_PORTRANGE_MAX - IANA_DYNAMIC_PORTRANGE_MIN)); + if ((res = sock_udp_create(&_udp_sock, &local, server, 0)) < 0) { + if (res != -EADDRINUSE) { + DEBUG("Unable to create UDP sock\n"); + goto exit; + } + } + } + res = credman_add(creds); + if (res < 0 && res != CREDMAN_EXIST) { + DEBUG("Unable to add credential to credman\n"); + _close_session(creds->tag, creds->type); + switch (res) { + case CREDMAN_NO_SPACE: + res = -ENOSPC; + break; + case CREDMAN_ERROR: + case CREDMAN_INVALID: + case CREDMAN_TYPE_UNKNOWN: + default: + res = -EINVAL; + break; + } + goto exit; + } + if ((res = sock_dtls_create(&_dtls_sock, &_udp_sock, creds->tag, + SOCK_DTLS_1_2, SOCK_DTLS_CLIENT)) < 0) { + puts("Unable to create DTLS sock\n"); + _close_session(creds->tag, creds->type); + goto exit; + } + + start = _now_ms(); + try_start = start; + while (((try_start = _now_ms()) - start) < SOCK_DODTLS_SESSION_TIMEOUT_MS) { + memset(&_server_session, 0, sizeof(_server_session)); + if ((res = sock_dtls_session_init(&_dtls_sock, server, + &_server_session)) >= 0) { + uint32_t try_duration; + + res = sock_dtls_recv(&_dtls_sock, &_server_session, _dns_buf, + sizeof(_dns_buf), timeout * US_PER_MS); + if (res == -SOCK_DTLS_HANDSHAKE) { + break; + } + DEBUG("Unable to establish DTLS handshake: %d (timeout: %luus)\n", + -res, (long unsigned)timeout * US_PER_MS); + sock_dtls_session_destroy(&_dtls_sock, &_server_session); + try_duration = _now_ms() - try_start; + if (try_duration < timeout) { + _sleep_ms(timeout - try_duration); + } + /* see https://datatracker.ietf.org/doc/html/rfc6347#section-4.2.4.1 */ + timeout *= 2U; + } + else { + DEBUG("Unable to initialize DTLS session: %d\n", -res); + sock_dtls_session_destroy(&_dtls_sock, &_server_session); + } + } + if (res != -SOCK_DTLS_HANDSHAKE) { + res = -ETIMEDOUT; + _close_session(creds->tag, creds->type); + goto exit; + } + else { + res = 0; + } + + _cred_type = creds->type; + _cred_tag = creds->tag; + _id = (uint16_t)(random_uint32() & 0xffff); +exit: + memset(_dns_buf, 0, sizeof(_dns_buf)); /* flush-out unencrypted data */ + mutex_unlock(&_server_mutex); + return (res > 0) ? 0 : res; +} + +static int _disconnect_server(void) +{ + int res = 0; + + mutex_lock(&_server_mutex); + if (!_server_set()) { + goto exit; + } + _close_session(_cred_tag, _cred_type); + _cred_tag = CREDMAN_TAG_EMPTY; + _cred_type = CREDMAN_TYPE_EMPTY; +exit: + mutex_unlock(&_server_mutex); + return res; +} + +static uint32_t _now_ms(void) +{ + return ztimer_now(ZTIMER_MSEC); +} + +static void _sleep_ms(uint32_t delay) +{ + ztimer_sleep(ZTIMER_MSEC, delay); +} From 96b1d9c59e05f378cbca50a64318ef34c3477228 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Thu, 16 Sep 2021 12:10:01 +0200 Subject: [PATCH 2/3] tests/gnrc_sock_dodtls: provide DNS over DTLS test application --- .../sock_dodtls/sock_dodtls.c | 1 + tests/gnrc_sock_dodtls/Makefile | 47 +++++ tests/gnrc_sock_dodtls/Makefile.board.dep | 6 + tests/gnrc_sock_dodtls/Makefile.ci | 50 +++++ tests/gnrc_sock_dodtls/README.md | 118 +++++++++++ tests/gnrc_sock_dodtls/main.c | 188 ++++++++++++++++++ 6 files changed, 410 insertions(+) create mode 100644 tests/gnrc_sock_dodtls/Makefile create mode 100644 tests/gnrc_sock_dodtls/Makefile.board.dep create mode 100644 tests/gnrc_sock_dodtls/Makefile.ci create mode 100644 tests/gnrc_sock_dodtls/README.md create mode 100644 tests/gnrc_sock_dodtls/main.c diff --git a/sys/net/application_layer/sock_dodtls/sock_dodtls.c b/sys/net/application_layer/sock_dodtls/sock_dodtls.c index 421638304e..2af5e68d5e 100644 --- a/sys/net/application_layer/sock_dodtls/sock_dodtls.c +++ b/sys/net/application_layer/sock_dodtls/sock_dodtls.c @@ -96,6 +96,7 @@ int sock_dodtls_query(const char *domain_name, void *addr_out, int family) timeout -= send_duration; if (res <= 0) { _sleep_ms(timeout); + continue; } res = sock_dtls_recv(&_dtls_sock, &_server_session, _dns_buf, sizeof(_dns_buf), timeout); diff --git a/tests/gnrc_sock_dodtls/Makefile b/tests/gnrc_sock_dodtls/Makefile new file mode 100644 index 0000000000..5c65a19505 --- /dev/null +++ b/tests/gnrc_sock_dodtls/Makefile @@ -0,0 +1,47 @@ +include ../Makefile.tests_common + +RIOTBASE ?= $(CURDIR)/../.. + +export TAP ?= tap0 + +# TinyDTLS only has support for 32-bit architectures ATM +FEATURES_REQUIRED += arch_32bit + +USEMODULE += auto_init_gnrc_netif +USEMODULE += ipv4_addr +USEMODULE += ipv6_addr +USEMODULE += sock_dodtls +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_netif_single # Only one interface used and it makes + # shell commands easier +USEMODULE += posix_inet +# tinydtls needs crypto secure PRNG +USEMODULE += prng_sha1prng +USEMODULE += shell +USEMODULE += shell_commands + +USEPKG += tinydtls + +# use Ethernet as link-layer protocol +ifeq (native,$(BOARD)) + TERMFLAGS ?= $(TAP) +else + ETHOS_BAUDRATE ?= 115200 + CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE) + TERMDEPS += ethos + TERMPROG ?= sudo $(RIOTTOOLS)/ethos/ethos + TERMFLAGS ?= $(TAP) $(PORT) $(ETHOS_BAUDRATE) +endif + +CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\) + +# The test requires some setup and to be run as root +# So it cannot currently be run +TEST_ON_CI_BLACKLIST += all + +.PHONY: ethos + +ethos: + $(Q)env -u CC -u CFLAGS $(MAKE) -C $(RIOTTOOLS)/ethos + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sock_dodtls/Makefile.board.dep b/tests/gnrc_sock_dodtls/Makefile.board.dep new file mode 100644 index 0000000000..b595b8605c --- /dev/null +++ b/tests/gnrc_sock_dodtls/Makefile.board.dep @@ -0,0 +1,6 @@ +# Put board specific dependencies here +ifeq (native,$(BOARD)) + USEMODULE += netdev_tap +else + USEMODULE += stdio_ethos +endif diff --git a/tests/gnrc_sock_dodtls/Makefile.ci b/tests/gnrc_sock_dodtls/Makefile.ci new file mode 100644 index 0000000000..342ddb97e0 --- /dev/null +++ b/tests/gnrc_sock_dodtls/Makefile.ci @@ -0,0 +1,50 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega328p \ + atmega328p-xplained-mini \ + atxmega-a3bu-xplained \ + blackpill \ + bluepill \ + bluepill-stm32f030c8 \ + derfmega128 \ + hifive1 \ + hifive1b \ + i-nucleo-lrwan1 \ + im880b \ + mega-xplained \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + saml10-xpro \ + saml11-xpro \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + stm32f7508-dk \ + stm32g0316-disco \ + stm32mp157c-dk2 \ + telosb \ + thingy52 \ + waspmote-pro \ + z1 \ + zigduino \ + # diff --git a/tests/gnrc_sock_dodtls/README.md b/tests/gnrc_sock_dodtls/README.md new file mode 100644 index 0000000000..b59df9c8ef --- /dev/null +++ b/tests/gnrc_sock_dodtls/README.md @@ -0,0 +1,118 @@ +# Overview + +This folder contains a test application for RIOT's sock-based DNS over DTLS +client. + +# How to test with native + +## Setting up a tap interface + +1. Create a tap interface with a valid IPv6 address + + ```console + $ sudo ip tuntap add dev tap0 mode tap user $(id -u -n) + $ sudo ip a a 2001:db8::1/64 dev tap0 + $ sudo ip link set up dev tap0 + $ ip addr show dev tap0 + 4: tap0: mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 + link/ether e2:bc:7d:6b:8b:08 brd ff:ff:ff:ff:ff:ff + inet6 2001:db8::1/64 scope global + valid_lft forever preferred_lft forever + inet6 fe80::e0bc:7dff:fe6b:8b08/64 scope link + valid_lft forever preferred_lft forever + ``` + + **Note down** the link-local address. + +2. Run the test application + + ```console + $ make flash -j term + ``` + + And copy the link-local address using `ifconfig`: + + ``` + > ifconfig + ifconfig + Iface 5 HWaddr: E2:BC:7D:6B:8B:09 + L2-PDU:1500 MTU:1500 HL:64 Source address length: 6 + Link type: wired + inet6 addr: fe80::e0bc:7dff:fe6b:8b09 scope: link VAL + inet6 group: ff02::1 + inet6 group: ff02::1:ff6b:8b09 + + ``` + +3. Use it to configure a route to the `native` device (replace `2001:db8::/64` if you used a + different prefix in 1.): + + ```console + $ sudo ip route add 2001:db8::/64 via fe80::e0bc:7dff:fe6b:8b09 dev tap0 + ``` + +4. Run `make term` again to configure the global address for the `native` device and the route to + the host from the `native` device: + + ```console + > ifconfig 5 add 2001:db8::2 + ifconfig 5 add 2001:db8::2 + success: added 2001:db8::2/64 to interface 5 + > nib route add 5 default fe80::e0bc:7dff:fe6b:8b08 + nib route add 5 default fe80::e0bc:7dff:fe6b:8b08 + ``` + + **Keep the `native` instance open for [2.3](#configure-dns-over-dtls-client-and-query-a-name)** + +## Install and run a test server + +1. In a new terminal install `aiodnsprox` as your test server: + + ```console + $ sudo pip install git+https://github.com/anr-bmbf-pivot/aiodnsprox/ + ``` + +2. Provide a minimal configuration file containing the `TLS_PSK_WITH_AES_128_CCM_8` pre-shared key + credentials for the DTLS server: + + ```console + $ cat << EOF > test.yaml + dtls_credentials: + client_identity: Client_identity + psk: secretPSK + EOF + ``` + +3. Run the DNS server with a DTLS transport bound to the `tap0` interface (`-d 2001:db8::1`; replace + the address if you used a different one in [2.1](#setting-up-a-tap-interface)'s step 1), using a + public DNS server as upstream (`-U 9.9.9.9`). `sudo` is required to be able to bind to the + DNS over DTLS port 853: + + ```console + $ sudo aiodns-proxy -C test.yaml -U 9.9.9.9 -d 2001:db8::1 + ``` + +## Configure DNS over DTLS client and query a name + +Use the RIOT shell you kept open in [2.1](#setting-up-a-tap-interface) to configure the DNS over +DTLS server and request `example.org` from it + +1. Provide the DNS over DTLS server address, port (optional), credential tag (5853), + `TLS_PSK_WITH_AES_128_CCM_8` client identity (`Client_identity`) and + `TLS_PSK_WITH_AES_128_CCM_8` secret key (`secretPSK`) to the DNS over DTLS client: + + ```console + > dodtls server [2001:db8::1]:853 5853 Client_identity secretPSK + > dodtls server + DNS over DTLS server: [2001:db8::1]:853 + ``` + +2. Now you should be able to query a name: + ```console + > dodtls request example.org inet6 + dodtls request example.org inet6 + example.org resolves to 2606:2800:220:1:248:1893:25c8:1946 + > dodtls request example.org inet + dodtls request example.org inet + example.org resolves to 93.184.216.34 + ``` diff --git a/tests/gnrc_sock_dodtls/main.c b/tests/gnrc_sock_dodtls/main.c new file mode 100644 index 0000000000..26f28ede03 --- /dev/null +++ b/tests/gnrc_sock_dodtls/main.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief sock DNS over DTLS client test application + * + * @author Kaspar Schleiser + * @author Martine S. Lenders + * + * @} + */ + +#include +#include + +#include + +#include "net/credman.h" +#include "net/sock/dodtls.h" +#include "net/sock/util.h" +#include "shell.h" + +#define MAIN_QUEUE_SIZE 8U +#define PSK_ID_LEN 32U +#define PSK_LEN 32U + +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +static int _dodtls(int argc, char **argv); + +static const shell_command_t _shell_commands[] = { + { "dodtls", "configures and requests a DNS server", _dodtls }, + { NULL, NULL, NULL }, +}; +static char _shell_buffer[SHELL_DEFAULT_BUFSIZE]; +static char _psk_id[PSK_ID_LEN]; +static char _psk[PSK_LEN]; +static credman_credential_t _credential = { + .type = CREDMAN_TYPE_PSK, + .params = { + .psk = { + .id = { .s = _psk_id, .len = 0U, }, + .key = { .s = _psk, .len = 0U, }, + } + } +}; + +static void _usage(char *cmd) +{ + printf("usage: %s server " + " \n", cmd); + printf("usage: %s server -d\n", cmd); + printf(" %s request []\n", cmd); +} + +static int _dodtls_server(int argc, char **argv) +{ + sock_udp_ep_t server; + int res; + + if ((argc == 3) && (strcmp(argv[2], "-d") == 0)) { + sock_dodtls_set_server(NULL, NULL); + } + else if ((argc > 1) && (argc < 6)) { + _usage(argv[0]); + return 1; + } + else { + if (sock_udp_str2ep(&server, argv[2]) < 0) { + _usage(argv[0]); + return 1; + } + if (server.port == 0) { + server.port = SOCK_DODTLS_PORT; + } + if (server.netif == SOCK_ADDR_ANY_NETIF) { + netif_t *netif = netif_iter(NULL); + /* we only have one interface so take that one, otherwise + * TinyDTLS is not able to identify the peer */ + server.netif = netif_get_id(netif); + } + if ((_credential.tag = atoi(argv[3])) == 0) { + _usage(argv[0]); + return 1; + } + if ((_credential.params.psk.id.len = strlen(argv[4])) >= PSK_ID_LEN) { + printf("PSK ID too long (max. %u bytes allowed)", PSK_ID_LEN); + return 1; + } + if ((_credential.params.psk.key.len = strlen(argv[5])) >= PSK_LEN) { + printf("PSK too long (max. %u bytes allowed)", PSK_LEN); + return 1; + } + strcpy((char *)_credential.params.psk.id.s, argv[4]); + strcpy((char *)_credential.params.psk.key.s, argv[5]); + if ((res = sock_dodtls_set_server(&server, &_credential)) < 0) { + errno = -res; + perror("Unable to establish session with server"); + } + } + if (sock_dodtls_get_server(&server) == 0) { + char addrstr[INET6_ADDRSTRLEN + 8U]; /* + 8 for port + colons + [] */ + uint16_t port; + + sock_udp_ep_fmt(&server, addrstr, &port); + printf("DNS server: [%s]:%u\n", addrstr, port); + } + else { + puts("DNS server: -"); + } + return 0; +} + +static int _dodtls_request(int argc, char **argv) +{ + uint8_t addr[16] = {0}; + int res, family = AF_UNSPEC; + + if (argc > 3) { + if (strcmp(argv[3], "inet") == 0) { + family = AF_INET; + } + else if (strcmp(argv[3], "inet6") == 0) { + family = AF_INET6; + } + else { + _usage(argv[0]); + } + } + + res = sock_dodtls_query(argv[2], addr, family); + + if (res > 0) { + char addrstr[INET6_ADDRSTRLEN]; + + if (inet_ntop(res == 4 ? AF_INET : AF_INET6, addr, addrstr, + sizeof(addrstr))) { + printf("%s resolves to %s\n", argv[2], addrstr); + } + else { + printf("unable to print resolved address for %s.\n", argv[2]); + printf("Maybe address module is missing\n"); + return 1; + } + } + else { + printf("error resolving %s: ", argv[2]); + errno = -res; + perror(""); + return 1; + } + return 0; +} + +static int _dodtls(int argc, char **argv) +{ + if ((argc > 1) && (strcmp(argv[1], "server") == 0)) { + return _dodtls_server(argc, argv); + } + else if ((argc > 2) && (strcmp(argv[1], "request") == 0)) { + return _dodtls_request(argc, argv); + } + else { + _usage(argv[0]); + return 1; + } +} + +int main(void) +{ + /* we need a message queue for the thread running the shell in order to + * receive potentially fast incoming networking packets */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + + /* start shell */ + shell_run(_shell_commands, _shell_buffer, sizeof(_shell_buffer)); + return 0; +} From 1b6c9a8334a480b5d686c7dcb926751add2052b8 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 24 Sep 2021 14:29:06 +0200 Subject: [PATCH 3/3] sock_dodtls: provide getters for sock and session To be able to implement the DNS message exchange outside the module. --- sys/include/net/sock/dodtls.h | 15 +++++++++++++++ .../application_layer/sock_dodtls/sock_dodtls.c | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/sys/include/net/sock/dodtls.h b/sys/include/net/sock/dodtls.h index feac88e050..49508f73cb 100644 --- a/sys/include/net/sock/dodtls.h +++ b/sys/include/net/sock/dodtls.h @@ -32,6 +32,7 @@ #ifndef NET_SOCK_DODTLS_H #define NET_SOCK_DODTLS_H +#include "net/sock/dtls.h" #include "net/sock/udp.h" #ifdef __cplusplus @@ -105,6 +106,20 @@ int sock_dodtls_query(const char *domain_name, void *addr_out, int family); */ int sock_dodtls_get_server(sock_udp_ep_t *server); +/** + * @brief Return the sock used by the DNS over DTLS client + * + * @return The sock used by the DNS over DTLS client + */ +sock_dtls_t *sock_dodtls_get_dtls_sock(void); + +/** + * @brief Return the DTLS ssession used by the DNS over DTLS client + * + * @return The DTLS session used by the DNS over DTLS client + */ +sock_dtls_session_t *sock_dodtls_get_server_session(void); + /** * @brief Configure and establish session with DNS over DTLS server * diff --git a/sys/net/application_layer/sock_dodtls/sock_dodtls.c b/sys/net/application_layer/sock_dodtls/sock_dodtls.c index 2af5e68d5e..26259336dc 100644 --- a/sys/net/application_layer/sock_dodtls/sock_dodtls.c +++ b/sys/net/application_layer/sock_dodtls/sock_dodtls.c @@ -136,6 +136,16 @@ int sock_dodtls_get_server(sock_udp_ep_t *server) return res; } +sock_dtls_t *sock_dodtls_get_dtls_sock(void) +{ + return &_dtls_sock; +} + +sock_dtls_session_t *sock_dodtls_get_server_session(void) +{ + return &_server_session; +} + int sock_dodtls_set_server(const sock_udp_ep_t *server, const credman_credential_t *creds) {