From 51ddf9de718acbb858d336b183dafd79153da3f7 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Sat, 13 Apr 2019 12:15:28 +0100 Subject: [PATCH] Add DTLS support to libcoap using MbedTLS This update supports DTLS, TLS is a future TODO components/coap/CMakeLists.txt: components/coap/component.mk: Add in the new files that have to be built Replace libcoap/src/coap_notls.c with libcoap/src/coap_mbedtls.c components/coap/libcoap: Update the version to include the current version for supporting MbedTLS components/coap/port/coap_debug.c: components/coap/port/coap_mbedtls.c: components/coap/port/include/coap/coap_dtls.h: New port files for DTLS components/coap/port/include/coap_config_posix.h: Include building with MbedTLS examples/protocols/coap_client/README.md: examples/protocols/coap_client/main/CMakeLists.txt: examples/protocols/coap_client/main/Kconfig.projbuild: examples/protocols/coap_client/main/coap_client_example_main.c: examples/protocols/coap_client/main/component.mk: Update CoAP client to support DTLS examples/protocols/coap_client/main/coap_ca.pem examples/protocols/coap_client/main/coap_client.crt examples/protocols/coap_client/main/coap_client.key New PKI Certs for CoAP client (copied from wpa2_enterprise example) examples/protocols/coap_server/README.md: examples/protocols/coap_server/main/CMakeLists.txt: examples/protocols/coap_server/main/Kconfig.projbuild: examples/protocols/coap_server/main/coap_server_example_main.c: examples/protocols/coap_server/main/component.mk: Update CoAP server to support DTLS Change "no data" to "Hello World!" to prevent confusion examples/protocols/coap_server/main/coap_ca.pem examples/protocols/coap_server/main/coap_server.crt examples/protocols/coap_server/main/coap_server.key New PKI Certs for CoAP server (copied from wpa2_enterprise example) See Issue #1379 --- components/coap/CMakeLists.txt | 8 +- components/coap/component.mk | 4 +- components/coap/libcoap | 2 +- components/coap/port/coap_debug.c | 888 ++++++++ components/coap/port/coap_io.c | 1417 ------------- components/coap/port/coap_mbedtls.c | 1796 +++++++++++++++++ components/coap/port/include/coap/coap_dtls.h | 631 ++++++ .../coap/port/include/coap_config_posix.h | 11 +- components/mbedtls/CMakeLists.txt | 3 +- components/mbedtls/port/esp_timing.c | 102 + examples/protocols/coap_client/README.md | 54 +- .../protocols/coap_client/main/CMakeLists.txt | 4 +- .../coap_client/main/Kconfig.projbuild | 91 +- .../protocols/coap_client/main/coap_ca.pem | 23 + .../coap_client/main/coap_client.crt | 70 + .../coap_client/main/coap_client.key | 27 + .../main/coap_client_example_main.c | 228 ++- .../protocols/coap_client/main/component.mk | 5 + examples/protocols/coap_server/README.md | 52 +- .../protocols/coap_server/main/CMakeLists.txt | 3 +- .../coap_server/main/Kconfig.projbuild | 74 + .../protocols/coap_server/main/coap_ca.pem | 23 + .../coap_server/main/coap_server.crt | 70 + .../coap_server/main/coap_server.key | 27 + .../main/coap_server_example_main.c | 176 +- .../protocols/coap_server/main/component.mk | 5 + 26 files changed, 4293 insertions(+), 1501 deletions(-) create mode 100644 components/coap/port/coap_debug.c delete mode 100644 components/coap/port/coap_io.c create mode 100644 components/coap/port/coap_mbedtls.c create mode 100644 components/coap/port/include/coap/coap_dtls.h create mode 100644 components/mbedtls/port/esp_timing.c create mode 100644 examples/protocols/coap_client/main/coap_ca.pem create mode 100644 examples/protocols/coap_client/main/coap_client.crt create mode 100644 examples/protocols/coap_client/main/coap_client.key create mode 100644 examples/protocols/coap_server/main/Kconfig.projbuild create mode 100644 examples/protocols/coap_server/main/coap_ca.pem create mode 100644 examples/protocols/coap_server/main/coap_server.crt create mode 100644 examples/protocols/coap_server/main/coap_server.key diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index aef9d316826..6d7dbb45174 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -8,7 +8,7 @@ set(srcs "libcoap/src/coap_hashkey.c" "libcoap/src/coap_session.c" "libcoap/src/coap_time.c" - "libcoap/src/coap_debug.c" + "port/coap_debug.c" "libcoap/src/encode.c" "libcoap/src/mem.c" "libcoap/src/net.c" @@ -18,8 +18,8 @@ set(srcs "libcoap/src/str.c" "libcoap/src/subscribe.c" "libcoap/src/uri.c" - "libcoap/src/coap_notls.c" - "port/coap_io.c") + "libcoap/src/coap_io.c" + "port/coap_mbedtls.c") set(COMPONENT_REQUIRES lwip) @@ -28,7 +28,7 @@ idf_component_register(SRCS "${srcs}" REQUIRES lwip) # Silence format truncation warning, until it is fixed upstream -set_source_files_properties(libcoap/src/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation) +set_source_files_properties(port/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation) # Needed for coap headers in public builds, also. # diff --git a/components/coap/component.mk b/components/coap/component.mk index 2eb07afd2c9..46f70398706 100644 --- a/components/coap/component.mk +++ b/components/coap/component.mk @@ -4,11 +4,11 @@ COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap2 -COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o libcoap/src/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o libcoap/src/coap_notls.o port/coap_io.o +COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o port/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_mbedtls.o libcoap/src/coap_io.o COMPONENT_SRCDIRS := libcoap/src libcoap port COMPONENT_SUBMODULES += libcoap # Silence format truncation warning, until it is fixed upstream -libcoap/src/coap_debug.o: CFLAGS += -Wno-format-truncation +port/coap_debug.o: CFLAGS += -Wno-format-truncation diff --git a/components/coap/libcoap b/components/coap/libcoap index cfec0d072c5..98954eb30a2 160000 --- a/components/coap/libcoap +++ b/components/coap/libcoap @@ -1 +1 @@ -Subproject commit cfec0d072c5b99ed3e54828ca50ea2f6b91e1f50 +Subproject commit 98954eb30a2e728e172a6cd29430ae5bc999b585 diff --git a/components/coap/port/coap_debug.c b/components/coap/port/coap_debug.c new file mode 100644 index 00000000000..64d6a018008 --- /dev/null +++ b/components/coap/port/coap_debug.c @@ -0,0 +1,888 @@ +/* debug.c -- debug utilities + * + * Copyright (C) 2010--2012,2014--2019 Olaf Bergmann and others + * + * This file is part of the CoAP library libcoap. Please see + * README for terms of use. + */ + +#include "coap_config.h" + +#if defined(HAVE_STRNLEN) && defined(__GNUC__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE 1 +#endif + +#if defined(HAVE_ASSERT_H) && !defined(assert) +# include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_WS2TCPIP_H +#include +#endif + +#ifdef HAVE_TIME_H +#include +#endif + +#include "libcoap.h" +#include "coap_dtls.h" +#include "block.h" +#include "coap_debug.h" +#include "encode.h" +#include "net.h" +#include "coap_mutex.h" + +#ifdef WITH_LWIP +# define fprintf(fd, ...) LWIP_PLATFORM_DIAG((__VA_ARGS__)) +# define fflush(...) +#endif + +#ifdef WITH_CONTIKI +# ifndef DEBUG +# define DEBUG DEBUG_PRINT +# endif /* DEBUG */ +#include "net/ip/uip-debug.h" +#endif + +static coap_log_t maxlog = LOG_WARNING; /* default maximum log level */ + +static int use_fprintf_for_show_pdu = 1; /* non zero to output with fprintf */ + +const char *coap_package_name(void) { + return PACKAGE_NAME; +} + +const char *coap_package_version(void) { + return PACKAGE_STRING; +} + +void +coap_set_show_pdu_output(int use_fprintf) { + use_fprintf_for_show_pdu = use_fprintf; +} + +coap_log_t +coap_get_log_level(void) { + return maxlog; +} + +void +coap_set_log_level(coap_log_t level) { + maxlog = level; +} + +/* this array has the same order as the type log_t */ +static const char *loglevels[] = { + "EMRG", "ALRT", "CRIT", "ERR ", "WARN", "NOTE", "INFO", "DEBG" +}; + +#ifdef HAVE_TIME_H + +COAP_STATIC_INLINE size_t +print_timestamp(char *s, size_t len, coap_tick_t t) { + struct tm *tmp; + time_t now = coap_ticks_to_rt(t); + tmp = localtime(&now); + return strftime(s, len, "%b %d %H:%M:%S", tmp); +} + +#else /* alternative implementation: just print the timestamp */ + +COAP_STATIC_INLINE size_t +print_timestamp(char *s, size_t len, coap_tick_t t) { +#ifdef HAVE_SNPRINTF + return snprintf(s, len, "%u.%03u", + (unsigned int)coap_ticks_to_rt(t), + (unsigned int)(t % COAP_TICKS_PER_SECOND)); +#else /* HAVE_SNPRINTF */ + /* @todo do manual conversion of timestamp */ + return 0; +#endif /* HAVE_SNPRINTF */ +} + +#endif /* HAVE_TIME_H */ + +#ifndef HAVE_STRNLEN +/** + * A length-safe strlen() fake. + * + * @param s The string to count characters != 0. + * @param maxlen The maximum length of @p s. + * + * @return The length of @p s. + */ +static inline size_t +strnlen(const char *s, size_t maxlen) { + size_t n = 0; + while(*s++ && n < maxlen) + ++n; + return n; +} +#endif /* HAVE_STRNLEN */ + +static size_t +print_readable( const uint8_t *data, size_t len, + unsigned char *result, size_t buflen, int encode_always ) { + const uint8_t hex[] = "0123456789ABCDEF"; + size_t cnt = 0; + assert(data || len == 0); + + if (buflen == 0) { /* there is nothing we can do here but return */ + return 0; + } + + while (len) { + if (!encode_always && isprint(*data)) { + if (cnt+1 < buflen) { /* keep one byte for terminating zero */ + *result++ = *data; + ++cnt; + } else { + break; + } + } else { + if (cnt+4 < buflen) { /* keep one byte for terminating zero */ + *result++ = '\\'; + *result++ = 'x'; + *result++ = hex[(*data & 0xf0) >> 4]; + *result++ = hex[*data & 0x0f]; + cnt += 4; + } else + break; + } + + ++data; --len; + } + + *result = '\0'; /* add a terminating zero */ + return cnt; +} + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +size_t +coap_print_addr(const struct coap_address_t *addr, unsigned char *buf, size_t len) { +#if defined( HAVE_ARPA_INET_H ) || defined( HAVE_WS2TCPIP_H ) + const void *addrptr = NULL; + in_port_t port; + unsigned char *p = buf; + size_t need_buf; + + switch (addr->addr.sa.sa_family) { + case AF_INET: + addrptr = &addr->addr.sin.sin_addr; + port = ntohs(addr->addr.sin.sin_port); + need_buf = INET_ADDRSTRLEN; + break; + case AF_INET6: + if (len < 7) /* do not proceed if buffer is even too short for [::]:0 */ + return 0; + + *p++ = '['; + + addrptr = &addr->addr.sin6.sin6_addr; + port = ntohs(addr->addr.sin6.sin6_port); + need_buf = INET6_ADDRSTRLEN; + + break; + default: + memcpy(buf, "(unknown address type)", min(22, len)); + return min(22, len); + } + + /* Cast needed for Windows, since it doesn't have the correct API signature. */ + if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)p, + min(len, need_buf)) == 0) { + perror("coap_print_addr"); + return 0; + } + + p += strnlen((char *)p, len); + + if (addr->addr.sa.sa_family == AF_INET6) { + if (p < buf + len) { + *p++ = ']'; + } else + return 0; + } + + p += snprintf((char *)p, buf + len - p + 1, ":%d", port); + + return buf + len - p; +#else /* HAVE_ARPA_INET_H */ +# if WITH_CONTIKI + unsigned char *p = buf; + uint8_t i; +# if NETSTACK_CONF_WITH_IPV6 + const uint8_t hex[] = "0123456789ABCDEF"; + + if (len < 41) + return 0; + + *p++ = '['; + + for (i=0; i < 16; i += 2) { + if (i) { + *p++ = ':'; + } + *p++ = hex[(addr->addr.u8[i] & 0xf0) >> 4]; + *p++ = hex[(addr->addr.u8[i] & 0x0f)]; + *p++ = hex[(addr->addr.u8[i+1] & 0xf0) >> 4]; + *p++ = hex[(addr->addr.u8[i+1] & 0x0f)]; + } + *p++ = ']'; +# else /* WITH_UIP6 */ +# warning "IPv4 network addresses will not be included in debug output" + + if (len < 21) + return 0; +# endif /* WITH_UIP6 */ + if (buf + len - p < 6) + return 0; + +#ifdef HAVE_SNPRINTF + p += snprintf((char *)p, buf + len - p + 1, ":%d", uip_htons(addr->port)); +#else /* HAVE_SNPRINTF */ + /* @todo manual conversion of port number */ +#endif /* HAVE_SNPRINTF */ + + return p - buf; +# else /* WITH_CONTIKI */ + /* TODO: output addresses manually */ +# warning "inet_ntop() not available, network addresses will not be included in debug output" +# endif /* WITH_CONTIKI */ + return 0; +#endif +} + +#ifdef WITH_CONTIKI +# define fprintf(fd, ...) PRINTF(__VA_ARGS__) +# define fflush(...) + +# ifdef HAVE_VPRINTF +# define vfprintf(fd, ...) vprintf(__VA_ARGS__) +# else /* HAVE_VPRINTF */ +# define vfprintf(fd, ...) PRINTF(__VA_ARGS__) +# endif /* HAVE_VPRINTF */ +#endif /* WITH_CONTIKI */ + +/** Returns a textual description of the message type @p t. */ +static const char * +msg_type_string(uint16_t t) { + static const char *types[] = { "CON", "NON", "ACK", "RST", "???" }; + + return types[min(t, sizeof(types)/sizeof(char *) - 1)]; +} + +/** Returns a textual description of the method or response code. */ +static const char * +msg_code_string(uint16_t c) { + static const char *methods[] = { "0.00", "GET", "POST", "PUT", "DELETE", + "FETCH", "PATCH", "iPATCH" }; + static const char *signals[] = { "7.00", "CSM", "Ping", "Pong", "Release", + "Abort" }; + static char buf[5]; + + if (c < sizeof(methods)/sizeof(const char *)) { + return methods[c]; + } else if (c >= 224 && c - 224 < (int)(sizeof(signals)/sizeof(const char *))) { + return signals[c-224]; + } else { + snprintf(buf, sizeof(buf), "%u.%02u", (c >> 5) & 0x7, c & 0x1f); + return buf; + } +} + +/** Returns a textual description of the option name. */ +static const char * +msg_option_string(uint8_t code, uint16_t option_type) { + struct option_desc_t { + uint16_t type; + const char *name; + }; + + static struct option_desc_t options[] = { + { COAP_OPTION_IF_MATCH, "If-Match" }, + { COAP_OPTION_URI_HOST, "Uri-Host" }, + { COAP_OPTION_ETAG, "ETag" }, + { COAP_OPTION_IF_NONE_MATCH, "If-None-Match" }, + { COAP_OPTION_OBSERVE, "Observe" }, + { COAP_OPTION_URI_PORT, "Uri-Port" }, + { COAP_OPTION_LOCATION_PATH, "Location-Path" }, + { COAP_OPTION_URI_PATH, "Uri-Path" }, + { COAP_OPTION_CONTENT_FORMAT, "Content-Format" }, + { COAP_OPTION_MAXAGE, "Max-Age" }, + { COAP_OPTION_URI_QUERY, "Uri-Query" }, + { COAP_OPTION_ACCEPT, "Accept" }, + { COAP_OPTION_LOCATION_QUERY, "Location-Query" }, + { COAP_OPTION_BLOCK2, "Block2" }, + { COAP_OPTION_BLOCK1, "Block1" }, + { COAP_OPTION_PROXY_URI, "Proxy-Uri" }, + { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" }, + { COAP_OPTION_SIZE1, "Size1" }, + { COAP_OPTION_SIZE2, "Size2" }, + { COAP_OPTION_NORESPONSE, "No-Response" } + }; + + static struct option_desc_t options_csm[] = { + { COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" }, + { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer" } + }; + + static struct option_desc_t options_pingpong[] = { + { COAP_SIGNALING_OPTION_CUSTODY, "Custody" } + }; + + static struct option_desc_t options_release[] = { + { COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address" }, + { COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off" } + }; + + static struct option_desc_t options_abort[] = { + { COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option" } + }; + + static char buf[6]; + size_t i; + + if (code == COAP_SIGNALING_CSM) { + for (i = 0; i < sizeof(options_csm)/sizeof(struct option_desc_t); i++) { + if (option_type == options_csm[i].type) { + return options_csm[i].name; + } + } + } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) { + for (i = 0; i < sizeof(options_pingpong)/sizeof(struct option_desc_t); i++) { + if (option_type == options_pingpong[i].type) { + return options_pingpong[i].name; + } + } + } else if (code == COAP_SIGNALING_RELEASE) { + for (i = 0; i < sizeof(options_release)/sizeof(struct option_desc_t); i++) { + if (option_type == options_release[i].type) { + return options_release[i].name; + } + } + } else if (code == COAP_SIGNALING_ABORT) { + for (i = 0; i < sizeof(options_abort)/sizeof(struct option_desc_t); i++) { + if (option_type == options_abort[i].type) { + return options_abort[i].name; + } + } + } else { + /* search option_type in list of known options */ + for (i = 0; i < sizeof(options)/sizeof(struct option_desc_t); i++) { + if (option_type == options[i].type) { + return options[i].name; + } + } + } + /* unknown option type, just print to buf */ + snprintf(buf, sizeof(buf), "%u", option_type); + return buf; +} + +static unsigned int +print_content_format(unsigned int format_type, + unsigned char *result, unsigned int buflen) { + struct desc_t { + unsigned int type; + const char *name; + }; + + static struct desc_t formats[] = { + { COAP_MEDIATYPE_TEXT_PLAIN, "text/plain" }, + { COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format" }, + { COAP_MEDIATYPE_APPLICATION_XML, "application/xml" }, + { COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream" }, + { COAP_MEDIATYPE_APPLICATION_EXI, "application/exi" }, + { COAP_MEDIATYPE_APPLICATION_JSON, "application/json" }, + { COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor" }, + { COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\"" }, + { COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\"" }, + { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\"" }, + { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\"" }, + { COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\"" }, + { COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\"" }, + { COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key" }, + { COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set" }, + { COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json" }, + { COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json" }, + { COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor" }, + { COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor" }, + { COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi" }, + { COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi" }, + { COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml" }, + { COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml" }, + { 75, "application/dcaf+cbor" } + }; + + size_t i; + + /* search format_type in list of known content formats */ + for (i = 0; i < sizeof(formats)/sizeof(struct desc_t); i++) { + if (format_type == formats[i].type) { + return snprintf((char *)result, buflen, "%s", formats[i].name); + } + } + + /* unknown content format, just print numeric value to buf */ + return snprintf((char *)result, buflen, "%d", format_type); +} + +/** + * Returns 1 if the given @p content_format is either unknown or known + * to carry binary data. The return value @c 0 hence indicates + * printable data which is also assumed if @p content_format is @c 01. + */ +COAP_STATIC_INLINE int +is_binary(int content_format) { + return !(content_format == -1 || + content_format == COAP_MEDIATYPE_TEXT_PLAIN || + content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT || + content_format == COAP_MEDIATYPE_APPLICATION_XML || + content_format == COAP_MEDIATYPE_APPLICATION_JSON); +} + +#define COAP_DO_SHOW_OUTPUT_LINE \ + do { \ + if (use_fprintf_for_show_pdu) { \ + fprintf(COAP_DEBUG_FD, "%s", outbuf); \ + } \ + else { \ + coap_log(level, "%s", outbuf); \ + } \ + } while (0) + +void +coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { +#if COAP_CONSTRAINED_STACK + static coap_mutex_t static_show_pdu_mutex = COAP_MUTEX_INITIALIZER; + static unsigned char buf[1024]; /* need some space for output creation */ + static char outbuf[COAP_DEBUG_BUF_SIZE]; +#else /* ! COAP_CONSTRAINED_STACK */ + unsigned char buf[1024]; /* need some space for output creation */ + char outbuf[COAP_DEBUG_BUF_SIZE]; +#endif /* ! COAP_CONSTRAINED_STACK */ + size_t buf_len = 0; /* takes the number of bytes written to buf */ + int encode = 0, have_options = 0, i; + coap_opt_iterator_t opt_iter; + coap_opt_t *option; + int content_format = -1; + size_t data_len; + unsigned char *data; + int outbuflen = 0; + + /* Save time if not needed */ + if (level > coap_get_log_level()) + return; + +#if COAP_CONSTRAINED_STACK + coap_mutex_lock(&static_show_pdu_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + + snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {", + COAP_DEFAULT_VERSION, msg_type_string(pdu->type), + msg_code_string(pdu->code), pdu->tid); + + for (i = 0; i < pdu->token_length; i++) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, + "%02x", pdu->token[i]); + } + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "}"); + + /* show options, if any */ + coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " ["); + while ((option = coap_option_next(&opt_iter))) { + if (!have_options) { + have_options = 1; + } else { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ","); + } + + if (pdu->code == COAP_SIGNALING_CSM) switch(opt_iter.type) { + case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: + buf_len = snprintf((char *)buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + default: + buf_len = 0; + break; + } else if (pdu->code == COAP_SIGNALING_PING + || pdu->code == COAP_SIGNALING_PONG) { + buf_len = 0; + } else if (pdu->code == COAP_SIGNALING_RELEASE) switch(opt_iter.type) { + case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS: + buf_len = print_readable(coap_opt_value(option), + coap_opt_length(option), + buf, sizeof(buf), 0); + break; + case COAP_SIGNALING_OPTION_HOLD_OFF: + buf_len = snprintf((char *)buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + default: + buf_len = 0; + break; + } else if (pdu->code == COAP_SIGNALING_ABORT) switch(opt_iter.type) { + case COAP_SIGNALING_OPTION_BAD_CSM_OPTION: + buf_len = snprintf((char *)buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + default: + buf_len = 0; + break; + } else switch (opt_iter.type) { + case COAP_OPTION_CONTENT_FORMAT: + content_format = (int)coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option)); + + buf_len = print_content_format(content_format, buf, sizeof(buf)); + break; + + case COAP_OPTION_BLOCK1: + case COAP_OPTION_BLOCK2: + /* split block option into number/more/size where more is the + * letter M if set, the _ otherwise */ + buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/%u", + coap_opt_block_num(option), /* block number */ + COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ + (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ + + break; + + case COAP_OPTION_URI_PORT: + case COAP_OPTION_MAXAGE: + case COAP_OPTION_OBSERVE: + case COAP_OPTION_SIZE1: + case COAP_OPTION_SIZE2: + /* show values as unsigned decimal value */ + buf_len = snprintf((char *)buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + + default: + /* generic output function for all other option types */ + if (opt_iter.type == COAP_OPTION_URI_PATH || + opt_iter.type == COAP_OPTION_PROXY_URI || + opt_iter.type == COAP_OPTION_URI_HOST || + opt_iter.type == COAP_OPTION_LOCATION_PATH || + opt_iter.type == COAP_OPTION_LOCATION_QUERY || + opt_iter.type == COAP_OPTION_URI_QUERY) { + encode = 0; + } else { + encode = 1; + } + + buf_len = print_readable(coap_opt_value(option), + coap_opt_length(option), + buf, sizeof(buf), encode); + } + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, + " %s:%.*s", msg_option_string(pdu->code, opt_iter.type), + (int)buf_len, buf); + } + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " ]"); + + if (coap_get_data(pdu, &data_len, &data)) { + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " :: "); + + if (is_binary(content_format)) { + int keep_data_len = data_len; + uint8_t *keep_data = data; + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, + "binary data length %zu\n", data_len); + COAP_DO_SHOW_OUTPUT_LINE; + /* + * Output hex dump of binary data as a continuous entry + */ + outbuf[0] = '\000'; + snprintf(outbuf, sizeof(outbuf), "<<"); + while (data_len--) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, + "%02x", *data++); + } + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ">>"); + data_len = keep_data_len; + data = keep_data; + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "\n"); + COAP_DO_SHOW_OUTPUT_LINE; + /* + * Output ascii readable (if possible), immediately under the + * hex value of the character output above to help binary debugging + */ + outbuf[0] = '\000'; + snprintf(outbuf, sizeof(outbuf), "<<"); + while (data_len--) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, + "%c ", isprint (*data) ? *data : '.'); + data++; + } + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ">>"); + } else { + if (print_readable(data, data_len, buf, sizeof(buf), 0)) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "'%s'", buf); + } + } + } + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "\n"); + COAP_DO_SHOW_OUTPUT_LINE; + +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&static_show_pdu_mutex); +#endif /* COAP_CONSTRAINED_STACK */ +} + +void coap_show_tls_version(coap_log_t level) +{ + char buffer[64]; + coap_string_tls_version(buffer, sizeof(buffer)); + coap_log(level, "%s\n", buffer); +} + +char *coap_string_tls_version(char *buffer, size_t bufsize) +{ + coap_tls_version_t *tls_version = coap_get_tls_library_version(); + char beta[8]; + char sub[2]; + char b_beta[8]; + char b_sub[2]; + + switch (tls_version->type) { + case COAP_TLS_LIBRARY_NOTLS: + snprintf(buffer, bufsize, "TLS Library: None"); + break; + case COAP_TLS_LIBRARY_TINYDTLS: + snprintf(buffer, bufsize, "TLS Library: TinyDTLS - runtime %lu.%lu.%lu, " + "libcoap built for %lu.%lu.%lu", + (unsigned long)(tls_version->version >> 16), + (unsigned long)((tls_version->version >> 8) & 0xff), + (unsigned long)(tls_version->version & 0xff), + (unsigned long)(tls_version->built_version >> 16), + (unsigned long)((tls_version->built_version >> 8) & 0xff), + (unsigned long)(tls_version->built_version & 0xff)); + break; + case COAP_TLS_LIBRARY_OPENSSL: + switch (tls_version->version &0xf) { + case 0: + strcpy(beta, "-dev"); + break; + case 0xf: + strcpy(beta, ""); + break; + default: + strcpy(beta, "-beta"); + beta[5] = (tls_version->version &0xf) + '0'; + beta[6] = '\000'; + break; + } + sub[0] = ((tls_version->version >> 4) & 0xff) ? + ((tls_version->version >> 4) & 0xff) + 'a' -1 : '\000'; + sub[1] = '\000'; + switch (tls_version->built_version &0xf) { + case 0: + strcpy(b_beta, "-dev"); + break; + case 0xf: + strcpy(b_beta, ""); + break; + default: + strcpy(b_beta, "-beta"); + b_beta[5] = (tls_version->built_version &0xf) + '0'; + b_beta[6] = '\000'; + break; + } + b_sub[0] = ((tls_version->built_version >> 4) & 0xff) ? + ((tls_version->built_version >> 4) & 0xff) + 'a' -1 : '\000'; + b_sub[1] = '\000'; + snprintf(buffer, bufsize, "TLS Library: OpenSSL - runtime " + "%lu.%lu.%lu%s%s, libcoap built for %lu.%lu.%lu%s%s", + (unsigned long)(tls_version->version >> 28), + (unsigned long)((tls_version->version >> 20) & 0xff), + (unsigned long)((tls_version->version >> 12) & 0xff), sub, beta, + (unsigned long)(tls_version->built_version >> 28), + (unsigned long)((tls_version->built_version >> 20) & 0xff), + (unsigned long)((tls_version->built_version >> 12) & 0xff), + b_sub, b_beta); + break; + case COAP_TLS_LIBRARY_GNUTLS: + snprintf(buffer, bufsize, "TLS Library: GnuTLS - runtime %lu.%lu.%lu, " + "libcoap built for %lu.%lu.%lu", + (unsigned long)(tls_version->version >> 16), + (unsigned long)((tls_version->version >> 8) & 0xff), + (unsigned long)(tls_version->version & 0xff), + (unsigned long)(tls_version->built_version >> 16), + (unsigned long)((tls_version->built_version >> 8) & 0xff), + (unsigned long)(tls_version->built_version & 0xff)); + break; + case COAP_TLS_LIBRARY_MBEDTLS: + snprintf(buffer, bufsize, "TLS Library: MbedTLS - runtime %lu.%lu.%lu, " + "libcoap built for %lu.%lu.%lu", + (unsigned long)(tls_version->version >> 24), + (unsigned long)((tls_version->version >> 16) & 0xff), + (unsigned long)((tls_version->version >> 8) & 0xff), + (unsigned long)(tls_version->built_version >> 24), + (unsigned long)((tls_version->built_version >> 16) & 0xff), + (unsigned long)((tls_version->built_version >> 8) & 0xff)); + break; + default: + snprintf(buffer, bufsize, "Library type %d unknown", tls_version->type); + break; + } + return buffer; +} + +static coap_log_handler_t log_handler = NULL; + +void coap_set_log_handler(coap_log_handler_t handler) { + log_handler = handler; +} + +void +coap_log_impl(coap_log_t level, const char *format, ...) { + + if (maxlog < level) + return; + + if (log_handler) { +#if COAP_CONSTRAINED_STACK + static coap_mutex_t static_log_mutex = COAP_MUTEX_INITIALIZER; + static char message[COAP_DEBUG_BUF_SIZE]; +#else /* ! COAP_CONSTRAINED_STACK */ + char message[COAP_DEBUG_BUF_SIZE]; +#endif /* ! COAP_CONSTRAINED_STACK */ + va_list ap; + va_start(ap, format); +#if COAP_CONSTRAINED_STACK + coap_mutex_lock(&static_log_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + + vsnprintf( message, sizeof(message), format, ap); + va_end(ap); + log_handler(level, message); +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&static_log_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + } else { + char timebuf[32]; + coap_tick_t now; + va_list ap; + FILE *log_fd; + + log_fd = level <= LOG_CRIT ? COAP_ERR_FD : COAP_DEBUG_FD; + + coap_ticks(&now); + if (print_timestamp(timebuf,sizeof(timebuf), now)) + fprintf(log_fd, "%s ", timebuf); + + if (level <= LOG_DEBUG) + fprintf(log_fd, "%s ", loglevels[level]); + + va_start(ap, format); + vfprintf(log_fd, format, ap); + va_end(ap); + fflush(log_fd); + } +} + +static struct packet_num_interval { + int start; + int end; +} packet_loss_intervals[10]; +static int num_packet_loss_intervals = 0; +static int packet_loss_level = 0; +static int send_packet_count = 0; + +int coap_debug_set_packet_loss(const char *loss_level) { + const char *p = loss_level; + char *end = NULL; + int n = (int)strtol(p, &end, 10), i = 0; + if (end == p || n < 0) + return 0; + if (*end == '%') { + if (n > 100) + n = 100; + packet_loss_level = n * 65536 / 100; + coap_log(LOG_DEBUG, "packet loss level set to %d%%\n", n); + } else { + if (n <= 0) + return 0; + while (i < 10) { + packet_loss_intervals[i].start = n; + if (*end == '-') { + p = end + 1; + n = (int)strtol(p, &end, 10); + if (end == p || n <= 0) + return 0; + } + packet_loss_intervals[i++].end = n; + if (*end == 0) + break; + if (*end != ',') + return 0; + p = end + 1; + n = (int)strtol(p, &end, 10); + if (end == p || n <= 0) + return 0; + } + if (i == 10) + return 0; + num_packet_loss_intervals = i; + } + send_packet_count = 0; + return 1; +} + +int coap_debug_send_packet(void) { + ++send_packet_count; + if (num_packet_loss_intervals > 0) { + int i; + for (i = 0; i < num_packet_loss_intervals; i++) { + if (send_packet_count >= packet_loss_intervals[i].start + && send_packet_count <= packet_loss_intervals[i].end) + return 0; + } + } + if ( packet_loss_level > 0 ) { + uint16_t r = 0; + prng( (uint8_t*)&r, 2 ); + if ( r < packet_loss_level ) + return 0; + } + return 1; +} diff --git a/components/coap/port/coap_io.c b/components/coap/port/coap_io.c deleted file mode 100644 index a8f35c180f3..00000000000 --- a/components/coap/port/coap_io.c +++ /dev/null @@ -1,1417 +0,0 @@ -/* coap_io.c -- Default network I/O functions for libcoap - * - * Copyright (C) 2012,2014,2016-2019 Olaf Bergmann and others - * - * This file is part of the CoAP library libcoap. Please see - * README for terms of use. - */ - -#include "coap_config.h" - -#ifdef HAVE_STDIO_H -# include -#endif - -#ifdef HAVE_SYS_SELECT_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -# define OPTVAL_T(t) (t) -# define OPTVAL_GT(t) (t) -#endif -#ifdef HAVE_SYS_IOCTL_H - #include -#endif -#ifdef HAVE_NETINET_IN_H -# include -#endif -#ifdef HAVE_WS2TCPIP_H -#include -# define OPTVAL_T(t) (const char*)(t) -# define OPTVAL_GT(t) (char*)(t) -# undef CMSG_DATA -# define CMSG_DATA WSA_CMSG_DATA -#endif -#ifdef HAVE_SYS_UIO_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -#include - -#ifdef WITH_CONTIKI -# include "uip.h" -#endif - -#include "libcoap.h" -#include "coap_debug.h" -#include "mem.h" -#include "net.h" -#include "coap_io.h" -#include "pdu.h" -#include "utlist.h" -#include "resource.h" - -#if !defined(WITH_CONTIKI) - /* define generic PKTINFO for IPv4 */ -#if defined(IP_PKTINFO) -# define GEN_IP_PKTINFO IP_PKTINFO -#elif defined(IP_RECVDSTADDR) -# define GEN_IP_PKTINFO IP_RECVDSTADDR -#else -# error "Need IP_PKTINFO or IP_RECVDSTADDR to request ancillary data from OS." -#endif /* IP_PKTINFO */ - -/* define generic KTINFO for IPv6 */ -#ifdef IPV6_RECVPKTINFO -# define GEN_IPV6_PKTINFO IPV6_RECVPKTINFO -#elif defined(IPV6_PKTINFO) -# define GEN_IPV6_PKTINFO IPV6_PKTINFO -#else -# error "Need IPV6_PKTINFO or IPV6_RECVPKTINFO to request ancillary data from OS." -#endif /* IPV6_RECVPKTINFO */ -#endif - -void coap_free_endpoint(coap_endpoint_t *ep); - -#ifdef WITH_CONTIKI -static int ep_initialized = 0; - -struct coap_endpoint_t * - coap_malloc_endpoint() { - static struct coap_endpoint_t ep; - - if (ep_initialized) { - return NULL; - } else { - ep_initialized = 1; - return &ep; - } -} - -void -coap_mfree_endpoint(struct coap_endpoint_t *ep) { - ep_initialized = 0; - coap_session_mfree(&ep->hello); -} - -int -coap_socket_bind_udp(coap_socket_t *sock, - const coap_address_t *listen_addr, - coap_address_t *bound_addr) { - sock->conn = udp_new(NULL, 0, NULL); - - if (!sock->conn) { - coap_log(LOG_WARNING, "coap_socket_bind_udp"); - return 0; - } - - coap_address_init(bound_addr); - uip_ipaddr_copy(&bound_addr->addr, &listen_addr->addr); - bound_addr->port = listen_addr->port; - udp_bind((struct uip_udp_conn *)sock->conn, bound_addr->port); - return 1; -} - -int -coap_socket_connect_udp(coap_socket_t *sock, - const coap_address_t *local_if, - const coap_address_t *server, - int default_port, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - return 0; -} - -int -coap_socket_connect_tcp1(coap_socket_t *sock, - const coap_address_t *local_if, - const coap_address_t *server, - int default_port, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - return 0; -} - -int -coap_socket_connect_tcp2(coap_socket_t *sock, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - return 0; -} - -int -coap_socket_bind_tcp(coap_socket_t *sock, - const coap_address_t *listen_addr, - coap_address_t *bound_addr) { - return 0; -} - -int -coap_socket_accept_tcp(coap_socket_t *server, - coap_socket_t *new_client, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - return 0; -} - -ssize_t -coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) { - return -1; -} - -ssize_t -coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { - return -1; -} - -void coap_socket_close(coap_socket_t *sock) { - if (sock->conn) - uip_udp_remove((struct uip_udp_conn *)sock->conn); - sock->flags = COAP_SOCKET_EMPTY; -} - -#else - -static const char *coap_socket_format_errno( int error ); - -struct coap_endpoint_t * - coap_malloc_endpoint(void) { - return (struct coap_endpoint_t *)coap_malloc_type(COAP_ENDPOINT, sizeof(struct coap_endpoint_t)); -} - -void -coap_mfree_endpoint(struct coap_endpoint_t *ep) { - coap_session_mfree(&ep->hello); - coap_free_type(COAP_ENDPOINT, ep); -} - -int -coap_socket_bind_udp(coap_socket_t *sock, - const coap_address_t *listen_addr, - coap_address_t *bound_addr) { - int on = 1, off = 0; -#ifdef _WIN32 - u_long u_on = 1; -#endif - - sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_DGRAM, 0); - - if (sock->fd == COAP_INVALID_SOCKET) { - coap_log(LOG_WARNING, - "coap_socket_bind_udp: socket: %s\n", coap_socket_strerror()); - goto error; - } - -#ifdef _WIN32 - if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) { -#else - if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) { -#endif - coap_log(LOG_WARNING, - "coap_socket_bind_udp: ioctl FIONBIO: %s\n", coap_socket_strerror()); - } - - if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_bind_udp: setsockopt SO_REUSEADDR: %s\n", - coap_socket_strerror()); - - switch (listen_addr->addr.sa.sa_family) { - case AF_INET: - if (setsockopt(sock->fd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) - coap_log(LOG_ALERT, - "coap_socket_bind_udp: setsockopt IP_PKTINFO: %s\n", - coap_socket_strerror()); - break; - case AF_INET6: - /* Configure the socket as dual-stacked */ - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off), sizeof(off)) == COAP_SOCKET_ERROR) - coap_log(LOG_ALERT, - "coap_socket_bind_udp: setsockopt IPV6_V6ONLY: %s\n", - coap_socket_strerror()); - if (setsockopt(sock->fd, IPPROTO_IPV6, GEN_IPV6_PKTINFO, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) - coap_log(LOG_ALERT, - "coap_socket_bind_udp: setsockopt IPV6_PKTINFO: %s\n", - coap_socket_strerror()); - setsockopt(sock->fd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), sizeof(on)); /* ignore error, because the likely cause is that IPv4 is disabled at the os level */ - break; - default: - coap_log(LOG_ALERT, "coap_socket_bind_udp: unsupported sa_family\n"); - break; - } - - if (bind(sock->fd, &listen_addr->addr.sa, listen_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_bind_udp: bind: %s\n", - coap_socket_strerror()); - goto error; - } - - bound_addr->size = (socklen_t)sizeof(*bound_addr); - if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) { - coap_log(LOG_WARNING, - "coap_socket_bind_udp: getsockname: %s\n", - coap_socket_strerror()); - goto error; - } - - return 1; - -error: - coap_socket_close(sock); - return 0; -} - -int -coap_socket_connect_tcp1(coap_socket_t *sock, - const coap_address_t *local_if, - const coap_address_t *server, - int default_port, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - int on = 1, off = 0; -#ifdef _WIN32 - u_long u_on = 1; -#endif - coap_address_t connect_addr; - coap_address_copy( &connect_addr, server ); - - sock->flags &= ~COAP_SOCKET_CONNECTED; - sock->fd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0); - - if (sock->fd == COAP_INVALID_SOCKET) { - coap_log(LOG_WARNING, - "coap_socket_connect_tcp1: socket: %s\n", - coap_socket_strerror()); - goto error; - } - -#ifdef _WIN32 - if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) { -#else - if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) { -#endif - coap_log(LOG_WARNING, - "coap_socket_connect_tcp1: ioctl FIONBIO: %s\n", - coap_socket_strerror()); - } - - switch (server->addr.sa.sa_family) { - case AF_INET: - if (connect_addr.addr.sin.sin_port == 0) - connect_addr.addr.sin.sin_port = htons(default_port); - break; - case AF_INET6: - if (connect_addr.addr.sin6.sin6_port == 0) - connect_addr.addr.sin6.sin6_port = htons(default_port); - /* Configure the socket as dual-stacked */ - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off), sizeof(off)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n", - coap_socket_strerror()); - break; - default: - coap_log(LOG_ALERT, "coap_socket_connect_tcp1: unsupported sa_family\n"); - break; - } - - if (local_if && local_if->addr.sa.sa_family) { - coap_address_copy(local_addr, local_if); - if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n", - coap_socket_strerror()); - if (bind(sock->fd, &local_if->addr.sa, local_if->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_tcp1: bind: %s\n", - coap_socket_strerror()); - goto error; - } - } else { - local_addr->addr.sa.sa_family = server->addr.sa.sa_family; - } - - if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) { -#ifdef _WIN32 - if (WSAGetLastError() == WSAEWOULDBLOCK) { -#else - if (errno == EINPROGRESS) { -#endif - /* - * COAP_SOCKET_CONNECTED needs to be set here as there will be reads/writes - * by underlying TLS libraries during connect() and we do not want to - * assert() in coap_read_session() or coap_write_session() when called by coap_read() - */ - sock->flags |= COAP_SOCKET_WANT_CONNECT | COAP_SOCKET_CONNECTED; - return 1; - } - coap_log(LOG_WARNING, "coap_socket_connect_tcp1: connect: %s\n", - coap_socket_strerror()); - goto error; - } - - if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_tcp1: getsockname: %s\n", - coap_socket_strerror()); - } - - if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_tcp1: getpeername: %s\n", - coap_socket_strerror()); - } - - sock->flags |= COAP_SOCKET_CONNECTED; - return 1; - -error: - coap_socket_close(sock); - return 0; -} - -int -coap_socket_connect_tcp2(coap_socket_t *sock, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - int error = 0; -#ifdef _WIN32 - int optlen = (int)sizeof( error ); -#else - socklen_t optlen = (socklen_t)sizeof( error ); -#endif - - sock->flags &= ~(COAP_SOCKET_WANT_CONNECT | COAP_SOCKET_CAN_CONNECT); - - if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, OPTVAL_GT(&error), - &optlen) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_finish_connect_tcp: getsockopt: %s\n", - coap_socket_strerror()); - } - - if (error) { - coap_log(LOG_WARNING, - "coap_socket_finish_connect_tcp: connect failed: %s\n", - coap_socket_format_errno(error)); - coap_socket_close(sock); - return 0; - } - - if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_tcp: getsockname: %s\n", - coap_socket_strerror()); - } - - if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_tcp: getpeername: %s\n", - coap_socket_strerror()); - } - - return 1; -} - -int -coap_socket_bind_tcp(coap_socket_t *sock, - const coap_address_t *listen_addr, - coap_address_t *bound_addr) { - int on = 1, off = 0; -#ifdef _WIN32 - u_long u_on = 1; -#endif - - sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_STREAM, 0); - - if (sock->fd == COAP_INVALID_SOCKET) { - coap_log(LOG_WARNING, "coap_socket_bind_tcp: socket: %s\n", - coap_socket_strerror()); - goto error; - } - -#ifdef _WIN32 - if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) { -#else - if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) { -#endif - coap_log(LOG_WARNING, "coap_socket_bind_tcp: ioctl FIONBIO: %s\n", - coap_socket_strerror()); - } - if (setsockopt (sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on), - sizeof (on)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n", - coap_socket_strerror()); - - if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), - sizeof(on)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n", - coap_socket_strerror()); - - switch (listen_addr->addr.sa.sa_family) { - case AF_INET: - break; - case AF_INET6: - /* Configure the socket as dual-stacked */ - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off), sizeof(off)) == COAP_SOCKET_ERROR) - coap_log(LOG_ALERT, - "coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n", - coap_socket_strerror()); - break; - default: - coap_log(LOG_ALERT, "coap_socket_bind_tcp: unsupported sa_family\n"); - } - - if (bind(sock->fd, &listen_addr->addr.sa, listen_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_ALERT, "coap_socket_bind_tcp: bind: %s\n", - coap_socket_strerror()); - goto error; - } - - bound_addr->size = (socklen_t)sizeof(*bound_addr); - if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) { - coap_log(LOG_WARNING, "coap_socket_bind_tcp: getsockname: %s\n", - coap_socket_strerror()); - goto error; - } - - if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) { - coap_log(LOG_ALERT, "coap_socket_bind_tcp: listen: %s\n", - coap_socket_strerror()); - goto error; - } - - return 1; - -error: - coap_socket_close(sock); - return 0; -} - -int -coap_socket_accept_tcp(coap_socket_t *server, - coap_socket_t *new_client, - coap_address_t *local_addr, - coap_address_t *remote_addr) { -#ifdef _WIN32 - u_long u_on = 1; -#else - int on = 1; -#endif - - server->flags &= ~COAP_SOCKET_CAN_ACCEPT; - - new_client->fd = accept(server->fd, &remote_addr->addr.sa, - &remote_addr->size); - if (new_client->fd == COAP_INVALID_SOCKET) { - coap_log(LOG_WARNING, "coap_socket_accept_tcp: accept: %s\n", - coap_socket_strerror()); - return 0; - } - - if (getsockname( new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0) - coap_log(LOG_WARNING, "coap_socket_accept_tcp: getsockname: %s\n", - coap_socket_strerror()); - - #ifdef _WIN32 - if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) { -#else - if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) { -#endif - coap_log(LOG_WARNING, "coap_socket_accept_tcp: ioctl FIONBIO: %s\n", - coap_socket_strerror()); - } - - return 1; -} - -int -coap_socket_connect_udp(coap_socket_t *sock, - const coap_address_t *local_if, - const coap_address_t *server, - int default_port, - coap_address_t *local_addr, - coap_address_t *remote_addr) { - int on = 1, off = 0; -#ifdef _WIN32 - u_long u_on = 1; -#endif - coap_address_t connect_addr; - int is_mcast = coap_is_mcast(server); - coap_address_copy(&connect_addr, server); - - sock->flags &= ~(COAP_SOCKET_CONNECTED | COAP_SOCKET_MULTICAST); - sock->fd = socket(connect_addr.addr.sa.sa_family, SOCK_DGRAM, 0); - - if (sock->fd == COAP_INVALID_SOCKET) { - coap_log(LOG_WARNING, "coap_socket_connect_udp: socket: %s\n", - coap_socket_strerror()); - goto error; - } - -#ifdef _WIN32 - if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) { -#else - if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) { -#endif - coap_log(LOG_WARNING, "coap_socket_connect_udp: ioctl FIONBIO: %s\n", - coap_socket_strerror()); - } - - switch (connect_addr.addr.sa.sa_family) { - case AF_INET: - if (connect_addr.addr.sin.sin_port == 0) - connect_addr.addr.sin.sin_port = htons(default_port); - break; - case AF_INET6: - if (connect_addr.addr.sin6.sin6_port == 0) - connect_addr.addr.sin6.sin6_port = htons(default_port); - /* Configure the socket as dual-stacked */ - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off), sizeof(off)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_connect_udp: setsockopt IPV6_V6ONLY: %s\n", - coap_socket_strerror()); - break; - default: - coap_log(LOG_ALERT, "coap_socket_connect_udp: unsupported sa_family\n"); - break; - } - - if (local_if && local_if->addr.sa.sa_family) { - if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) - coap_log(LOG_WARNING, - "coap_socket_connect_udp: setsockopt SO_REUSEADDR: %s\n", - coap_socket_strerror()); - if (bind(sock->fd, &local_if->addr.sa, local_if->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_udp: bind: %s\n", - coap_socket_strerror()); - goto error; - } - } - - /* special treatment for sockets that are used for multicast communication */ - if (is_mcast) { - if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, - "coap_socket_connect_udp: getsockname for multicast socket: %s\n", - coap_socket_strerror()); - } - coap_address_copy(remote_addr, &connect_addr); - sock->flags |= COAP_SOCKET_MULTICAST; - return 1; - } - - if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_udp: connect: %s\n", - coap_socket_strerror()); - goto error; - } - - if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_udp: getsockname: %s\n", - coap_socket_strerror()); - } - - if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) { - coap_log(LOG_WARNING, "coap_socket_connect_udp: getpeername: %s\n", - coap_socket_strerror()); - } - - sock->flags |= COAP_SOCKET_CONNECTED; - return 1; - -error: - coap_socket_close(sock); - return 0; -} - -void coap_socket_close(coap_socket_t *sock) { - if (sock->fd != COAP_INVALID_SOCKET) { - coap_closesocket(sock->fd); - sock->fd = COAP_INVALID_SOCKET; - } - sock->flags = COAP_SOCKET_EMPTY; -} - -ssize_t -coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) { - ssize_t r; - - sock->flags &= ~(COAP_SOCKET_WANT_WRITE | COAP_SOCKET_CAN_WRITE); -#ifdef _WIN32 - r = send(sock->fd, (const char *)data, (int)data_len, 0); -#else - r = send(sock->fd, data, data_len, 0); -#endif - if (r == COAP_SOCKET_ERROR) { -#ifdef _WIN32 - if (WSAGetLastError() == WSAEWOULDBLOCK) { -#elif EAGAIN != EWOULDBLOCK - if (errno==EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { -#else - if (errno==EAGAIN || errno == EINTR) { -#endif - sock->flags |= COAP_SOCKET_WANT_WRITE; - return 0; - } - coap_log(LOG_WARNING, "coap_socket_write: send: %s\n", - coap_socket_strerror()); - return -1; - } - if (r < (ssize_t)data_len) - sock->flags |= COAP_SOCKET_WANT_WRITE; - return r; -} - -ssize_t -coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { - ssize_t r; -#ifdef _WIN32 - int error; -#endif - -#ifdef _WIN32 - r = recv(sock->fd, (char *)data, (int)data_len, 0); -#else - r = recv(sock->fd, data, data_len, 0); -#endif - if (r == 0) { - /* graceful shutdown */ - sock->flags &= ~COAP_SOCKET_CAN_READ; - return -1; - } else if (r == COAP_SOCKET_ERROR) { - sock->flags &= ~COAP_SOCKET_CAN_READ; -#ifdef _WIN32 - error = WSAGetLastError(); - if (error == WSAEWOULDBLOCK) { -#elif EAGAIN != EWOULDBLOCK - if (errno==EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { -#else - if (errno==EAGAIN || errno == EINTR) { -#endif - return 0; - } -#ifdef _WIN32 - if (error != WSAECONNRESET) -#else - if (errno != ECONNRESET) -#endif - coap_log(LOG_WARNING, "coap_socket_read: recv: %s\n", - coap_socket_strerror()); - return -1; - } - if (r < (ssize_t)data_len) - sock->flags &= ~COAP_SOCKET_CAN_READ; - return r; -} - -#endif /* WITH_CONTIKI */ - -#if (!defined(WITH_CONTIKI)) != ( defined(HAVE_NETINET_IN_H) || defined(HAVE_WS2TCPIP_H) ) -/* define struct in6_pktinfo and struct in_pktinfo if not available - FIXME: check with configure -*/ -struct in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - unsigned int ipi6_ifindex; /* send/recv interface index */ -}; - -#endif - -#if !defined(WITH_CONTIKI) && !defined(SOL_IP) -/* Solaris expects level IPPROTO_IP for ancillary data. */ -#define SOL_IP IPPROTO_IP -#endif - -#ifdef __GNUC__ -#define UNUSED_PARAM __attribute__ ((unused)) -#else /* not a GCC */ -#define UNUSED_PARAM -#endif /* GCC */ - -#if defined(_WIN32) -#include -static __declspec(thread) LPFN_WSARECVMSG lpWSARecvMsg = NULL; -/* Map struct WSABUF fields to their posix counterpart */ -#define msghdr _WSAMSG -#define msg_name name -#define msg_namelen namelen -#define msg_iov lpBuffers -#define msg_iovlen dwBufferCount -#define msg_control Control.buf -#define msg_controllen Control.len -#define iovec _WSABUF -#define iov_base buf -#define iov_len len -#define iov_len_t u_long -#undef CMSG_DATA -#define CMSG_DATA WSA_CMSG_DATA -#define ipi_spec_dst ipi_addr -#else -#define iov_len_t size_t -#endif - -ssize_t -coap_network_send(coap_socket_t *sock, const coap_session_t *session, const uint8_t *data, size_t datalen) { - ssize_t bytes_written = 0; - - if (!coap_debug_send_packet()) { - bytes_written = (ssize_t)datalen; -#ifndef WITH_CONTIKI - } else if (sock->flags & COAP_SOCKET_CONNECTED) { -#ifdef _WIN32 - bytes_written = send(sock->fd, (const char *)data, (int)datalen, 0); -#else - bytes_written = send(sock->fd, data, datalen, 0); -#endif -#endif - } else { -#ifndef WITH_CONTIKI -#ifdef _WIN32 - DWORD dwNumberOfBytesSent = 0; - int r; -#endif -#ifndef COAP_BAD_RECVMSG - /* a buffer large enough to hold all packet info types, ipv6 is the largest */ - char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - struct msghdr mhdr; - struct iovec iov[1]; - const void *addr = &session->remote_addr.addr; - - assert(session); - - memcpy (&iov[0].iov_base, &data, sizeof (iov[0].iov_base)); - iov[0].iov_len = (iov_len_t)datalen; - - memset(buf, 0, sizeof (buf)); - - memset(&mhdr, 0, sizeof(struct msghdr)); - memcpy (&mhdr.msg_name, &addr, sizeof (mhdr.msg_name)); - mhdr.msg_namelen = session->remote_addr.size; - - mhdr.msg_iov = iov; - mhdr.msg_iovlen = 1; - - if (!coap_address_isany(&session->local_addr) && !coap_is_mcast(&session->local_addr)) switch (session->local_addr.addr.sa.sa_family) { - case AF_INET6: - { - struct cmsghdr *cmsg; - - if (IN6_IS_ADDR_V4MAPPED(&session->local_addr.addr.sin6.sin6_addr)) { -#if defined(IP_PKTINFO) - struct in_pktinfo *pktinfo; - mhdr.msg_control = buf; - mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mhdr); - cmsg->cmsg_level = SOL_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - - pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); - - pktinfo->ipi_ifindex = session->ifindex; - memcpy(&pktinfo->ipi_spec_dst, session->local_addr.addr.sin6.sin6_addr.s6_addr + 12, sizeof(pktinfo->ipi_spec_dst)); -#elif defined(IP_SENDSRCADDR) - mhdr.msg_control = buf; - mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); - - cmsg = CMSG_FIRSTHDR(&mhdr); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_SENDSRCADDR; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); - - memcpy(CMSG_DATA(cmsg), session->local_addr.addr.sin6.sin6_addr.s6_addr + 12, sizeof(struct in_addr)); -#endif /* IP_PKTINFO */ - } else { - struct in6_pktinfo *pktinfo; - mhdr.msg_control = buf; - mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mhdr); - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - - pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); - - pktinfo->ipi6_ifindex = session->ifindex; - memcpy(&pktinfo->ipi6_addr, &session->local_addr.addr.sin6.sin6_addr, sizeof(pktinfo->ipi6_addr)); - } - break; - } - case AF_INET: - { -#if defined(IP_PKTINFO) - struct cmsghdr *cmsg; - struct in_pktinfo *pktinfo; - - mhdr.msg_control = buf; - mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mhdr); - cmsg->cmsg_level = SOL_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - - pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); - - pktinfo->ipi_ifindex = session->ifindex; - memcpy(&pktinfo->ipi_spec_dst, &session->local_addr.addr.sin.sin_addr, sizeof(pktinfo->ipi_spec_dst)); -#elif defined(IP_SENDSRCADDR) - struct cmsghdr *cmsg; - mhdr.msg_control = buf; - mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); - - cmsg = CMSG_FIRSTHDR(&mhdr); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_SENDSRCADDR; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); - - memcpy(CMSG_DATA(cmsg), &session->local_addr.addr.sin.sin_addr, sizeof(struct in_addr)); -#endif /* IP_PKTINFO */ - break; - } - default: - /* error */ - coap_log(LOG_WARNING, "protocol not supported\n"); - bytes_written = -1; - } -#endif /* ! COAP_BAD_RECVMSG */ - -#ifdef _WIN32 - r = WSASendMsg(sock->fd, &mhdr, 0 /*dwFlags*/, &dwNumberOfBytesSent, NULL /*lpOverlapped*/, NULL /*lpCompletionRoutine*/); - if (r == 0) - bytes_written = (ssize_t)dwNumberOfBytesSent; - else - bytes_written = -1; -#else -#ifndef COAP_BAD_RECVMSG - bytes_written = sendmsg(sock->fd, &mhdr, 0); -#else /* COAP_BAD_RECVMSG */ - bytes_written = sendto(sock->fd, data, datalen, 0, &session->remote_addr.addr.sa, session->remote_addr.size); -#endif /* COAP_BAD_RECVMSG */ -#endif -#else /* WITH_CONTIKI */ - /* FIXME: untested */ - /* FIXME: is there a way to check if send was successful? */ - (void)datalen; - (void)data; - uip_udp_packet_sendto((struct uip_udp_conn *)sock->conn, data, datalen, - &session->remote_addr.addr, session->remote_addr.port); - bytes_written = datalen; -#endif /* WITH_CONTIKI */ - } - - if (bytes_written < 0) - coap_log(LOG_CRIT, "coap_network_send: %s\n", coap_socket_strerror()); - - return bytes_written; -} - -#define SIN6(A) ((struct sockaddr_in6 *)(A)) - -void -coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) { - *address = packet->payload; - *length = packet->length; -} - -void coap_packet_set_addr(coap_packet_t *packet, const coap_address_t *src, const coap_address_t *dst) { - coap_address_copy(&packet->src, src); - coap_address_copy(&packet->dst, dst); -} - -ssize_t -coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { - ssize_t len = -1; - - assert(sock); - assert(packet); - - if ((sock->flags & COAP_SOCKET_CAN_READ) == 0) { - return -1; - } else { - /* clear has-data flag */ - sock->flags &= ~COAP_SOCKET_CAN_READ; - } - -#ifndef WITH_CONTIKI - if (sock->flags & COAP_SOCKET_CONNECTED) { -#ifdef _WIN32 - len = recv(sock->fd, (char *)packet->payload, COAP_RXBUFFER_SIZE, 0); -#else - len = recv(sock->fd, packet->payload, COAP_RXBUFFER_SIZE, 0); -#endif - if (len < 0) { -#ifdef _WIN32 - if (WSAGetLastError() == WSAECONNRESET) { -#else - if (errno == ECONNREFUSED) { -#endif - /* client-side ICMP destination unreachable, ignore it */ - coap_log(LOG_WARNING, "coap_network_read: unreachable\n"); - return -2; - } - coap_log(LOG_WARNING, "coap_network_read: %s\n", coap_socket_strerror()); - goto error; - } else if (len > 0) { - packet->length = (size_t)len; - } - } else { -#endif /* WITH_CONTIKI */ -#if defined(_WIN32) - DWORD dwNumberOfBytesRecvd = 0; - int r; -#endif -#if !defined(WITH_CONTIKI) -#ifndef COAP_BAD_RECVMSG - /* a buffer large enough to hold all packet info types, ipv6 is the largest */ - char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - struct msghdr mhdr; - struct iovec iov[1]; - - iov[0].iov_base = packet->payload; - iov[0].iov_len = (iov_len_t)COAP_RXBUFFER_SIZE; - - memset(&mhdr, 0, sizeof(struct msghdr)); - - mhdr.msg_name = (struct sockaddr*)&packet->src.addr; - mhdr.msg_namelen = sizeof(packet->src.addr); - - mhdr.msg_iov = iov; - mhdr.msg_iovlen = 1; - - mhdr.msg_control = buf; - mhdr.msg_controllen = sizeof(buf); - -#if defined(_WIN32) - if (!lpWSARecvMsg) { - GUID wsaid = WSAID_WSARECVMSG; - DWORD cbBytesReturned = 0; - if (WSAIoctl(sock->fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &wsaid, sizeof(wsaid), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &cbBytesReturned, NULL, NULL) != 0) { - coap_log(LOG_WARNING, "coap_network_read: no WSARecvMsg\n"); - return -1; - } - } - r = lpWSARecvMsg(sock->fd, &mhdr, &dwNumberOfBytesRecvd, NULL /* LPWSAOVERLAPPED */, NULL /* LPWSAOVERLAPPED_COMPLETION_ROUTINE */); - if (r == 0) - len = (ssize_t)dwNumberOfBytesRecvd; -#else - len = recvmsg(sock->fd, &mhdr, 0); -#endif - -#else /* COAP_BAD_RECVMSG */ - packet->src.size = packet->src.size; - len = recvfrom(sock->fd, packet->payload, COAP_RXBUFFER_SIZE, 0, &packet->src.addr.sa, &packet->src.size); -#endif /* COAP_BAD_RECVMSG */ - - if (len < 0) { -#ifdef _WIN32 - if (WSAGetLastError() == WSAECONNRESET) { -#else - if (errno == ECONNREFUSED) { -#endif - /* server-side ICMP destination unreachable, ignore it. The destination address is in msg_name. */ - return 0; - } - coap_log(LOG_WARNING, "coap_network_read: %s\n", coap_socket_strerror()); - goto error; - } else { -#ifndef COAP_BAD_RECVMSG - struct cmsghdr *cmsg; - - packet->src.size = mhdr.msg_namelen; - packet->length = (size_t)len; - - /* Walk through ancillary data records until the local interface - * is found where the data was received. */ - for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) { - - /* get the local interface for IPv6 */ - if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { - union { - uint8_t *c; - struct in6_pktinfo *p; - } u; - u.c = CMSG_DATA(cmsg); - packet->ifindex = (int)(u.p->ipi6_ifindex); - memcpy(&packet->dst.addr.sin6.sin6_addr, &u.p->ipi6_addr, sizeof(struct in6_addr)); - break; - } - - /* local interface for IPv4 */ -#if defined(IP_PKTINFO) - if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { - union { - uint8_t *c; - struct in_pktinfo *p; - } u; - u.c = CMSG_DATA(cmsg); - packet->ifindex = u.p->ipi_ifindex; - if (packet->dst.addr.sa.sa_family == AF_INET6) { - memset(packet->dst.addr.sin6.sin6_addr.s6_addr, 0, 10); - packet->dst.addr.sin6.sin6_addr.s6_addr[10] = 0xff; - packet->dst.addr.sin6.sin6_addr.s6_addr[11] = 0xff; - memcpy(packet->dst.addr.sin6.sin6_addr.s6_addr + 12, &u.p->ipi_addr, sizeof(struct in_addr)); - } else { - memcpy(&packet->dst.addr.sin.sin_addr, &u.p->ipi_addr, sizeof(struct in_addr)); - } - break; - } -#elif defined(IP_RECVDSTADDR) - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { - packet->ifindex = 0; - memcpy(&packet->dst.addr.sin.sin_addr, CMSG_DATA(cmsg), sizeof(struct in_addr)); - break; - } -#endif /* IP_PKTINFO */ - } -#else /* COAP_BAD_RECVMSG */ - packet->length = (size_t)len; - packet->ifindex = 0; - if (getsockname(sock->fd, &packet->dst.addr.sa, &packet->dst.size) < 0) { - coap_log(LOG_DEBUG, "Cannot determine local port\n"); - goto error; - } -#endif /* COAP_BAD_RECVMSG */ - } -#endif /* !defined(WITH_CONTIKI) */ -#ifdef WITH_CONTIKI - /* FIXME: untested, make this work */ -#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) - - if (uip_newdata()) { - uip_ipaddr_copy(&packet->src.addr, &UIP_IP_BUF->srcipaddr); - packet->src.port = UIP_UDP_BUF->srcport; - uip_ipaddr_copy(&(packet)->dst.addr, &UIP_IP_BUF->destipaddr); - packet->dst.port = UIP_UDP_BUF->destport; - - len = uip_datalen(); - - if (len > COAP_RXBUFFER_SIZE) { - /* FIXME: we might want to send back a response */ - coap_log(LOG_WARNING, "discarded oversized packet\n"); - return -1; - } - - ((char *)uip_appdata)[len] = 0; -#ifndef NDEBUG - if (LOG_DEBUG <= coap_get_log_level()) { -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 40 -#endif - unsigned char addr_str[INET6_ADDRSTRLEN + 8]; - - if (coap_print_addr(&packet->src, addr_str, INET6_ADDRSTRLEN + 8)) { - coap_log(LOG_DEBUG, "received %zd bytes from %s\n", len, addr_str); - } - } -#endif /* NDEBUG */ - - packet->length = len; - memcpy(&packet->payload, uip_appdata, len); - } - -#undef UIP_IP_BUF -#undef UIP_UDP_BUF -#endif /* WITH_CONTIKI */ -#ifndef WITH_CONTIKI - } -#endif /* WITH_CONTIKI */ - - if (len >= 0) - return len; -#if !defined(WITH_CONTIKI) -error: -#endif - return -1; -} - -#if !defined(WITH_CONTIKI) - -unsigned int -coap_write(coap_context_t *ctx, - coap_socket_t *sockets[], - unsigned int max_sockets, - unsigned int *num_sockets, - coap_tick_t now) -{ - coap_queue_t *nextpdu; - coap_endpoint_t *ep; - coap_session_t *s; - coap_tick_t session_timeout; - coap_tick_t timeout = 0; - coap_session_t *tmp; - - *num_sockets = 0; - - /* Check to see if we need to send off any Observe requests */ - coap_check_notify(ctx); - - if (ctx->session_timeout > 0) - session_timeout = ctx->session_timeout * COAP_TICKS_PER_SECOND; - else - session_timeout = COAP_DEFAULT_SESSION_TIMEOUT * COAP_TICKS_PER_SECOND; - - LL_FOREACH(ctx->endpoint, ep) { - if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_ACCEPT)) { - if (*num_sockets < max_sockets) - sockets[(*num_sockets)++] = &ep->sock; - } - LL_FOREACH_SAFE(ep->sessions, s, tmp) { - if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 && - s->delayqueue == NULL && - (s->last_rx_tx + session_timeout <= now || - s->state == COAP_SESSION_STATE_NONE)) { - coap_session_free(s); - } else { - if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 && s->delayqueue == NULL) { - coap_tick_t s_timeout = (s->last_rx_tx + session_timeout) - now; - if (timeout == 0 || s_timeout < timeout) - timeout = s_timeout; - } - if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE)) { - if (*num_sockets < max_sockets) - sockets[(*num_sockets)++] = &s->sock; - } - } - } - } - LL_FOREACH_SAFE(ctx->sessions, s, tmp) { - if ( - s->type == COAP_SESSION_TYPE_CLIENT - && COAP_PROTO_RELIABLE(s->proto) - && s->state == COAP_SESSION_STATE_ESTABLISHED - && ctx->ping_timeout > 0 - ) { - coap_tick_t s_timeout; - if (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND <= now) { - if ((s->last_ping > 0 && s->last_pong < s->last_ping) - || coap_session_send_ping(s) == COAP_INVALID_TID) - { - /* Make sure the session object is not deleted in the callback */ - coap_session_reference(s); - coap_session_disconnected(s, COAP_NACK_NOT_DELIVERABLE); - coap_session_release(s); - continue; - } - s->last_rx_tx = now; - s->last_ping = now; - } - s_timeout = (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND) - now; - if (timeout == 0 || s_timeout < timeout) - timeout = s_timeout; - } - - if ( - s->type == COAP_SESSION_TYPE_CLIENT - && COAP_PROTO_RELIABLE(s->proto) - && s->state == COAP_SESSION_STATE_CSM - && ctx->csm_timeout > 0 - ) { - coap_tick_t s_timeout; - if (s->csm_tx == 0) { - s->csm_tx = now; - } else if (s->csm_tx + ctx->csm_timeout * COAP_TICKS_PER_SECOND <= now) { - /* Make sure the session object is not deleted in the callback */ - coap_session_reference(s); - coap_session_disconnected(s, COAP_NACK_NOT_DELIVERABLE); - coap_session_release(s); - continue; - } - s_timeout = (s->csm_tx + ctx->csm_timeout * COAP_TICKS_PER_SECOND) - now; - if (timeout == 0 || s_timeout < timeout) - timeout = s_timeout; - } - - if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) { - if (*num_sockets < max_sockets) - sockets[(*num_sockets)++] = &s->sock; - } - } - - nextpdu = coap_peek_next(ctx); - - while (nextpdu && now >= ctx->sendqueue_basetime && nextpdu->t <= now - ctx->sendqueue_basetime) { - coap_retransmit(ctx, coap_pop_next(ctx)); - nextpdu = coap_peek_next(ctx); - } - - if (nextpdu && (timeout == 0 || nextpdu->t - ( now - ctx->sendqueue_basetime ) < timeout)) - timeout = nextpdu->t - (now - ctx->sendqueue_basetime); - - if (ctx->dtls_context) { - if (coap_dtls_is_context_timeout()) { - coap_tick_t tls_timeout = coap_dtls_get_context_timeout(ctx->dtls_context); - if (tls_timeout > 0) { - if (tls_timeout < now + COAP_TICKS_PER_SECOND / 10) - tls_timeout = now + COAP_TICKS_PER_SECOND / 10; - coap_log(LOG_DEBUG, "** DTLS global timeout set to %dms\n", - (int)((tls_timeout - now) * 1000 / COAP_TICKS_PER_SECOND)); - if (timeout == 0 || tls_timeout - now < timeout) - timeout = tls_timeout - now; - } - } else { - LL_FOREACH(ctx->endpoint, ep) { - if (ep->proto == COAP_PROTO_DTLS) { - LL_FOREACH(ep->sessions, s) { - if (s->proto == COAP_PROTO_DTLS && s->tls) { - coap_tick_t tls_timeout = coap_dtls_get_timeout(s); - while (tls_timeout > 0 && tls_timeout <= now) { - coap_log(LOG_DEBUG, "** %s: DTLS retransmit timeout\n", - coap_session_str(s)); - coap_dtls_handle_timeout(s); - if (s->tls) - tls_timeout = coap_dtls_get_timeout(s); - else { - tls_timeout = 0; - timeout = 1; - } - } - if (tls_timeout > 0 && (timeout == 0 || tls_timeout - now < timeout)) - timeout = tls_timeout - now; - } - } - } - } - LL_FOREACH(ctx->sessions, s) { - if (s->proto == COAP_PROTO_DTLS && s->tls) { - coap_tick_t tls_timeout = coap_dtls_get_timeout(s); - while (tls_timeout > 0 && tls_timeout <= now) { - coap_log(LOG_DEBUG, "** %s: DTLS retransmit timeout\n", coap_session_str(s)); - coap_dtls_handle_timeout(s); - if (s->tls) - tls_timeout = coap_dtls_get_timeout(s); - else { - tls_timeout = 0; - timeout = 1; - } - } - if (tls_timeout > 0 && (timeout == 0 || tls_timeout - now < timeout)) - timeout = tls_timeout - now; - } - } - } - } - - return (unsigned int)((timeout * 1000 + COAP_TICKS_PER_SECOND - 1) / COAP_TICKS_PER_SECOND); -} - -int -coap_run_once(coap_context_t *ctx, unsigned timeout_ms) { - fd_set readfds, writefds, exceptfds; - coap_fd_t nfds = 0; - struct timeval tv; - coap_tick_t before, now; - int result; - coap_socket_t *sockets[64]; - unsigned int num_sockets = 0, i, timeout; - - coap_ticks(&before); - - timeout = coap_write(ctx, sockets, (unsigned int)(sizeof(sockets) / sizeof(sockets[0])), &num_sockets, before); - if (timeout == 0 || timeout_ms < timeout) - timeout = timeout_ms; - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptfds); - for (i = 0; i < num_sockets; i++) { - if (sockets[i]->fd + 1 > nfds) - nfds = sockets[i]->fd + 1; - if (sockets[i]->flags & COAP_SOCKET_WANT_READ) - FD_SET(sockets[i]->fd, &readfds); - if (sockets[i]->flags & COAP_SOCKET_WANT_WRITE) - FD_SET(sockets[i]->fd, &writefds); - if (sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) - FD_SET(sockets[i]->fd, &readfds); - if (sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) { - FD_SET(sockets[i]->fd, &writefds); - FD_SET(sockets[i]->fd, &exceptfds); - } - } - - if ( timeout > 0 ) { - tv.tv_usec = (timeout % 1000) * 1000; - tv.tv_sec = (long)(timeout / 1000); - } - - result = select(nfds, &readfds, &writefds, &exceptfds, timeout > 0 ? &tv : NULL); - - if (result < 0) { /* error */ -#ifdef _WIN32 - if (WSAGetLastError() != WSAEINVAL) { /* May happen because of ICMP */ -#else - if (errno != EINTR) { -#endif - coap_log(LOG_DEBUG, "%s", coap_socket_strerror()); - return -1; - } - } - - if (result > 0) { - for (i = 0; i < num_sockets; i++) { - if ((sockets[i]->flags & COAP_SOCKET_WANT_READ) && FD_ISSET(sockets[i]->fd, &readfds)) - sockets[i]->flags |= COAP_SOCKET_CAN_READ; - if ((sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) && FD_ISSET(sockets[i]->fd, &readfds)) - sockets[i]->flags |= COAP_SOCKET_CAN_ACCEPT; - if ((sockets[i]->flags & COAP_SOCKET_WANT_WRITE) && FD_ISSET(sockets[i]->fd, &writefds)) - sockets[i]->flags |= COAP_SOCKET_CAN_WRITE; - if ((sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) && (FD_ISSET(sockets[i]->fd, &writefds) || FD_ISSET(sockets[i]->fd, &exceptfds))) - sockets[i]->flags |= COAP_SOCKET_CAN_CONNECT; - } - } - - coap_ticks(&now); - coap_read(ctx, now); - - return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND); -} - -#else -int coap_run_once(coap_context_t *ctx, unsigned int timeout_ms) { - return -1; -} - -unsigned int -coap_write(coap_context_t *ctx, - coap_socket_t *sockets[], - unsigned int max_sockets, - unsigned int *num_sockets, - coap_tick_t now) -{ - *num_sockets = 0; - return 0; -} -#endif - -#ifdef _WIN32 -static const char *coap_socket_format_errno(int error) { - static char szError[256]; - if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)szError, (DWORD)sizeof(szError), NULL) == 0) - strcpy(szError, "Unknown error"); - return szError; -} - -const char *coap_socket_strerror(void) { - return coap_socket_format_errno(WSAGetLastError()); -} -#else -#ifndef WITH_CONTIKI -static const char *coap_socket_format_errno(int error) { - return strerror(error); -} -#endif /* WITH_CONTIKI */ - -const char *coap_socket_strerror(void) { - return strerror(errno); -} -#endif - -ssize_t -coap_socket_send(coap_socket_t *sock, coap_session_t *session, - const uint8_t *data, size_t data_len) { - return session->context->network_send(sock, session, data, data_len); -} - -#undef SIN6 diff --git a/components/coap/port/coap_mbedtls.c b/components/coap/port/coap_mbedtls.c new file mode 100644 index 00000000000..9f8c1fab98a --- /dev/null +++ b/components/coap/port/coap_mbedtls.c @@ -0,0 +1,1796 @@ +/* +* coap_mbedtls.c -- mbedTLS Datagram Transport Layer Support for libcoap +* +* Copyright (C) 2019 Jon Shallow +* 2019 Jitin George +* +* This file is part of the CoAP library libcoap. Please see README for terms +* of use. +*/ + +/* + * Naming used to prevent confusion between coap sessions, mbedtls sessions etc. + * when reading the code. + * + * c_context A coap_context_t * + * c_session A coap_session_t * + * m_context A coap_mbedtls_context_t * (held in c_context->dtls_context) + * m_env A coap_mbedtls_env_t * (held in c_session->tls) + */ + +#include "coap_config.h" + +#ifdef HAVE_MBEDTLS + +/* + * Once PS #335 has been merged in, then code following a rebase needs to be + * updated removing sections that are "#ifndef PSK2_PR", and then remove all + * references to PSK2_PR. + */ +#undef PSK2_PR + +#include "libcoap.h" +#include "coap_dtls.h" +#include "net.h" +#include "mem.h" +#include "coap_debug.h" +#include "prng.h" +#include "coap_mutex.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ESPIDF_VERSION) && defined(CONFIG_MBEDTLS_DEBUG) +#include +#endif /* ESPIDF_VERSION && CONFIG_MBEDTLS_DEBUG */ +#include +#include + +#define mbedtls_malloc(a) malloc(a) +#define mbedtls_realloc(a,b) realloc(a,b) +#define mbedtls_strdup(a) strdup(a) + +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else /* __GNUC__ */ +#define UNUSED +#endif /* __GNUC__ */ + +#define IS_PSK (1 << 0) +#define IS_PKI (1 << 1) +#define IS_CLIENT (1 << 6) +#define IS_SERVER (1 << 7) + +typedef struct coap_ssl_t { + const uint8_t *pdu; + unsigned pdu_len; + unsigned peekmode; + coap_tick_t timeout; +} coap_ssl_t; + +/* + * This structure encapsulates the mbedTLS session object. + * It handles both TLS and DTLS. + * c_session->tls points to this. + */ +typedef struct coap_mbedtls_env_t { + mbedtls_ssl_context ssl; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_config conf; + mbedtls_timing_delay_context timer; + mbedtls_x509_crt cacert; + mbedtls_x509_crt public_cert; + mbedtls_pk_context private_key; + mbedtls_ssl_cookie_ctx cookie_ctx; + /* If not set, need to do do_mbedtls_handshake */ + int established; + int seen_client_hello; + coap_ssl_t coap_ssl_data; +} coap_mbedtls_env_t; + +typedef struct pki_sni_entry { + char *sni; + coap_dtls_key_t pki_key; + mbedtls_x509_crt cacert; + mbedtls_x509_crt public_cert; + mbedtls_pk_context private_key; +} pki_sni_entry; + +#ifdef PSK2_PR +typedef struct psk_sni_entry { + coap_string_t sni; + coap_dtls_spsk_info_t psk_info; +} psk_sni_entry; +#endif /* PSK2_PR */ + +typedef struct coap_mbedtls_context_t { + coap_dtls_pki_t setup_data; + size_t pki_sni_count; + pki_sni_entry *pki_sni_entry_list; +#ifdef PSK2_PR + size_t psk_sni_count; + psk_sni_entry *psk_sni_entry_list; +#endif /* PSK2_PR */ + char *root_ca_file; + char *root_ca_path; + int psk_pki_enabled; +} coap_mbedtls_context_t; + +static int coap_dgram_read(void *ctx, unsigned char *out, size_t outl) +{ + ssize_t ret = 0; + coap_session_t *c_session = (struct coap_session_t *)ctx; + coap_ssl_t *data = &((coap_mbedtls_env_t *)c_session->tls)->coap_ssl_data; + + if (!c_session->tls) { + errno = EAGAIN; + return MBEDTLS_ERR_SSL_WANT_READ; + } + + if (out != NULL) { + if (data != NULL && data->pdu_len > 0) { + if (outl < data->pdu_len) { + memcpy(out, data->pdu, outl); + ret = outl; + data->pdu += outl; + data->pdu_len -= outl; + } + else { + memcpy(out, data->pdu, data->pdu_len); + ret = data->pdu_len; + if (!data->peekmode) { + data->pdu_len = 0; + data->pdu = NULL; + } + } + } + else { + ret = MBEDTLS_ERR_SSL_WANT_READ; + errno = EAGAIN; + return ret; + } + } + return ret; +} + +/* + * return +ve data amount + * 0 no more + * -1 error (error in errno) + */ +/* callback function given to mbedtls for sending data over socket */ +static int +coap_dgram_write(void *ctx, const unsigned char *send_buffer, + size_t send_buffer_length) +{ + ssize_t result = -1; + coap_session_t *c_session = (struct coap_session_t *)ctx; + + if (c_session) { + result = coap_session_send(c_session, send_buffer, send_buffer_length); + if (result != (int)send_buffer_length) { + coap_log(LOG_WARNING, "coap_network_send failed (%zd != %zd)\n", + result, send_buffer_length); + result = 0; + } + } else { + result = 0; + } + return result; +} + +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) +static char* +get_ip_addr(const struct coap_address_t *addr) +{ + const void *addrptr = NULL; + size_t buf_len; + + if (!addr) { + return NULL; + } + switch (addr->addr.sa.sa_family) { + case AF_INET: + addrptr = &addr->addr.sin.sin_addr; + buf_len = INET_ADDRSTRLEN; + break; + case AF_INET6: + addrptr = &addr->addr.sin6.sin6_addr; + buf_len = INET6_ADDRSTRLEN; + break; + default: + return NULL; + } + char *str = (char *)mbedtls_calloc(1, buf_len); + if (!str) { + coap_log(LOG_ERR, "Memory allocation failed\n"); + return NULL; + } + if (inet_ntop(addr->addr.sa.sa_family, addrptr, str, + buf_len) == 0) { + perror("coap_print_addr"); + return 0; + } + return str; +} +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + +#if !defined(ESPIDF_VERSION) || (defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) && defined(CONFIG_MBEDTLS_PSK_MODES)) +/* + * Server side PSK callback + */ +static int psk_server_callback(void *p_info, mbedtls_ssl_context *ssl, + const unsigned char *name, size_t name_len ) +{ + coap_session_t *c_session = + (coap_session_t *)p_info; + uint8_t buf[128]; + size_t psk_len; +#ifdef PSK2_PR + coap_dtls_spsk_t *setup_data; +#endif /* PSK2_PR */ + coap_mbedtls_env_t *m_env; + + coap_log(LOG_DEBUG, "got psk_identity: '%.*s'\n", + (int)name_len, name); + + if (c_session == NULL || c_session->context == NULL || + c_session->context->get_server_psk == NULL) { + return -1; + } + m_env = (coap_mbedtls_env_t *)c_session->tls; +#ifdef PSK2_PR + setup_data = &c_session->context->spsk_setup_data; + + if (setup_data->validate_id_call_back) { + coap_bin_const_t lidentity; + lidentity.length = name_len; + lidentity.s = (const uint8_t*)name; + const coap_bin_const_t *psk_key = + setup_data->validate_id_call_back(&lidentity, + c_session, + setup_data->id_call_back_arg); + + if (psk_key == NULL) + return -1; + mbedtls_ssl_set_hs_psk(ssl, psk_key->s, psk_key->length); + coap_session_refresh_psk_key(c_session, psk_key); + m_env->seen_client_hello = 1; + return 0; + } +#endif /* PSK2_PR */ + + psk_len = c_session->context->get_server_psk(c_session, + (const uint8_t*)name, + name_len, + (uint8_t*)buf, sizeof(buf)); + m_env->seen_client_hello = 1; + mbedtls_ssl_set_hs_psk(ssl, buf, psk_len); + return 0; +} +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + +static char* +get_san_or_cn_from_cert(mbedtls_x509_crt *crt) +{ + if (crt) { +#if COAP_CONSTRAINED_STACK + static coap_mutex_t a_static_mutex = COAP_MUTEX_INITIALIZER; + static char buf[1024]; +#else /* ! COAP_CONSTRAINED_STACK */ + char buf[1024]; +#endif /* ! COAP_CONSTRAINED_STACK */ + char *cn; + char *cp; + char *tcp; + int n; + +#if COAP_CONSTRAINED_STACK + coap_mutex_lock(&a_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + + mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt ); + + /* Look first to see if Subject Alt Name is defined */ + cp = strstr(buf, "subject alt name"); + if (cp) { + cp = strchr(cp, ':'); + if (cp) { + cp++; + while (*cp == ' ') cp++; + tcp = strchr(cp, '\n'); + if (tcp) + *tcp = '\000'; + /* Take only the first entry */ + tcp = strchr(cp, ','); + if (tcp) + *tcp = '\000'; + /* Return the Subject Alt Name */ +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&a_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + return mbedtls_strdup(cp); + } + } + + /* Pull CN= out of subject name */ + cp = strstr(buf, "subject name"); + if (cp) { + cp = strchr(cp, ':'); + if (cp) { + cp++; + while (*cp == ' ') cp++; + tcp = strchr(cp, '\n'); + if (tcp) + *tcp = '\000'; + + /* Need to emulate strcasestr() here. Looking for CN= */ + n = strlen(cp) - 3; + cn = cp; + while (n > 0) { + if (((cn[0] == 'C') || (cn[0] == 'c')) && + ((cn[1] == 'N') || (cn[1] == 'n')) && + (cn[2] == '=')) { + cn += 3; + break; + } + cn++; + n--; + } + if (n > 0) { + tcp = strchr(cn, ','); + if (tcp) + *tcp = '\000'; +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&a_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + return mbedtls_strdup(cn); + } + } + } +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&a_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + } + return NULL; +} + +/* + * return 0 All OK + * -ve Error Code + */ +static int +cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, + int depth, uint32_t *flags) +{ + coap_session_t *c_session = (coap_session_t*)data; + coap_mbedtls_context_t *m_context = + (coap_mbedtls_context_t *)c_session->context->dtls_context; + coap_dtls_pki_t *setup_data = &m_context->setup_data; + char *cn = NULL; + + if (*flags == 0) + return 0; + + if (!setup_data->verify_peer_cert) { + /* Nothing is being checked */ + *flags = 0; + return 0; + } + + cn = get_san_or_cn_from_cert(crt); + + if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) { + if (setup_data->allow_expired_certs) { + *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED; + coap_log(LOG_WARNING, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate has expired", cn ? cn : "?", depth); + } + } + if (*flags & MBEDTLS_X509_BADCERT_FUTURE) { + if (setup_data->allow_expired_certs) { + *flags &= ~MBEDTLS_X509_BADCERT_FUTURE; + coap_log(LOG_WARNING, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate has a future date", cn ? cn : "?", depth); + } + } + if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) { + if (setup_data->allow_bad_md_hash) { + *flags &= ~MBEDTLS_X509_BADCERT_BAD_MD; + coap_log(LOG_WARNING, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate has a bad MD hash", cn ? cn : "?", depth); + } + } + if (*flags & MBEDTLS_X509_BADCERT_BAD_KEY) { + if (setup_data->allow_short_rsa_length) { + *flags &= ~MBEDTLS_X509_BADCERT_BAD_KEY; + coap_log(LOG_WARNING, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate has a short RSA length", cn ? cn : "?", depth); + } + } + if (*flags & MBEDTLS_X509_BADCRL_EXPIRED) { + if (setup_data->check_cert_revocation && setup_data->allow_expired_crl) { + *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; + coap_log(LOG_WARNING, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate's CRL has expired", cn ? cn : "?", depth); + } + else if (!setup_data->check_cert_revocation) { + *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; + } + } + if (*flags & MBEDTLS_X509_BADCRL_FUTURE) { + if (setup_data->check_cert_revocation && setup_data->allow_expired_crl) { + *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; + coap_log(LOG_WARNING, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate's CRL has a future date", cn ? cn : "?", depth); + } + else if (!setup_data->check_cert_revocation) { + *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; + } + } + + if (*flags & MBEDTLS_X509_BADCERT_CN_MISMATCH) { + *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; + } + if (setup_data->validate_cn_call_back) { + if (!setup_data->validate_cn_call_back(cn, + crt->raw.p, + crt->raw.len, + c_session, + depth, + *flags == 0, + setup_data->cn_call_back_arg)) { + *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; + } + } + if (*flags != 0) { + char buf[128]; + char *tcp; + + mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); + tcp = strchr(buf, '\n'); + while (tcp) { + *tcp = '\000'; + coap_log(LOG_WARNING, + " %s: %s: issue 0x%x: '%s' depth %d\n", + coap_session_str(c_session), + buf, *flags, cn ? cn : "?", depth); + tcp = strchr(tcp+1, '\n'); + } + } + + if (cn) + mbedtls_free(cn); + + return 0; +} + +static int +setup_pki_credentials(mbedtls_x509_crt *cacert, + mbedtls_x509_crt *public_cert, + mbedtls_pk_context *private_key, + coap_mbedtls_env_t *m_env, + coap_mbedtls_context_t *m_context, + coap_session_t *c_session, + coap_dtls_pki_t *setup_data, + coap_dtls_role_t role) +{ + int ret; + + switch (setup_data->pki_key.key_type) { + case COAP_PKI_KEY_PEM: + if (setup_data->pki_key.key.pem.public_cert && + setup_data->pki_key.key.pem.public_cert[0] && + setup_data->pki_key.key.pem.private_key && + setup_data->pki_key.key.pem.private_key[0]) { + + mbedtls_x509_crt_init(public_cert); + mbedtls_pk_init(private_key); + + ret = mbedtls_x509_crt_parse_file(public_cert, + setup_data->pki_key.key.pem.public_cert); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse_file returned -0x%x\n\n", + -ret); + return ret; + } + + ret = mbedtls_pk_parse_keyfile(private_key, + setup_data->pki_key.key.pem.private_key, NULL); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret); + return ret; + } + + ret = mbedtls_ssl_conf_own_cert(&m_env->conf, public_cert, private_key); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret); + return ret; + } + } + else if (role == COAP_DTLS_ROLE_SERVER) { + coap_log(LOG_ERR, + "***setup_pki: (D)TLS: No %s Certificate + Private " + "Key defined\n", + role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); + return -1; + } + + if (setup_data->pki_key.key.pem.ca_file && + setup_data->pki_key.key.pem.ca_file[0]) { + mbedtls_x509_crt_init(cacert); + ret = mbedtls_x509_crt_parse_file(cacert, + setup_data->pki_key.key.pem.ca_file); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? + MBEDTLS_SSL_VERIFY_REQUIRED : + MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); + } + break; + case COAP_PKI_KEY_PEM_BUF: + if (setup_data->pki_key.key.pem_buf.public_cert && + setup_data->pki_key.key.pem_buf.public_cert_len && + setup_data->pki_key.key.pem_buf.private_key && + setup_data->pki_key.key.pem_buf.private_key_len > 0) { + mbedtls_x509_crt_init(public_cert); + mbedtls_pk_init(private_key); + ret = mbedtls_x509_crt_parse(public_cert, + (const unsigned char *)setup_data->pki_key.key.pem_buf.public_cert, + setup_data->pki_key.key.pem_buf.public_cert_len); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + + ret = mbedtls_pk_parse_key(private_key, + (const unsigned char *)setup_data->pki_key.key.pem_buf.private_key, + setup_data->pki_key.key.pem_buf.private_key_len, NULL, 0); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret); + return ret; + } + + ret = mbedtls_ssl_conf_own_cert(&m_env->conf, public_cert, private_key); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret); + return ret; + } + } else if (role == COAP_DTLS_ROLE_SERVER) { + coap_log(LOG_ERR, + "***setup_pki: (D)TLS: No %s Certificate + Private " + "Key defined\n", + role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); + return -1; + } + + if (setup_data->pki_key.key.pem_buf.ca_cert && + setup_data->pki_key.key.pem_buf.ca_cert_len > 0) { + mbedtls_x509_crt_init(cacert); + ret = mbedtls_x509_crt_parse(cacert, + (const unsigned char *)setup_data->pki_key.key.pem_buf.ca_cert, + setup_data->pki_key.key.pem_buf.ca_cert_len); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? + MBEDTLS_SSL_VERIFY_REQUIRED : + MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); + } + break; + case COAP_PKI_KEY_ASN1: + if (setup_data->pki_key.key.asn1.public_cert && + setup_data->pki_key.key.asn1.public_cert_len && + setup_data->pki_key.key.asn1.private_key && + setup_data->pki_key.key.asn1.private_key_len > 0) { + + mbedtls_x509_crt_init(public_cert); + mbedtls_pk_init(private_key); + ret = mbedtls_x509_crt_parse(public_cert, + (const unsigned char *)setup_data->pki_key.key.asn1.public_cert, + setup_data->pki_key.key.asn1.public_cert_len); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + + ret = mbedtls_pk_parse_key(private_key, + (const unsigned char *)setup_data->pki_key.key.asn1.private_key, + setup_data->pki_key.key.asn1.private_key_len, NULL, 0); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret); + return ret; + } + + ret = mbedtls_ssl_conf_own_cert(&m_env->conf, public_cert, private_key); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret); + return ret; + } + } else if (role == COAP_DTLS_ROLE_SERVER) { + coap_log(LOG_ERR, + "***setup_pki: (D)TLS: No %s Certificate + Private " + "Key defined\n", + role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); + return -1; + } + + if (setup_data->pki_key.key.asn1.ca_cert && + setup_data->pki_key.key.asn1.ca_cert_len > 0) { + mbedtls_x509_crt_init(cacert); + ret = mbedtls_x509_crt_parse(cacert, + (const unsigned char *)setup_data->pki_key.key.asn1.ca_cert, + setup_data->pki_key.key.asn1.ca_cert_len); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? + MBEDTLS_SSL_VERIFY_REQUIRED : + MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); + } + break; + default: + coap_log(LOG_ERR, + "***setup_pki: (D)TLS: Unknown key type %d\n", + setup_data->pki_key.key_type); + return -1; + } + + if (m_context->root_ca_file) { + ret = mbedtls_x509_crt_parse_file(cacert, m_context->root_ca_file); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); + } + if (m_context->root_ca_path) { + ret = mbedtls_x509_crt_parse_file(cacert, m_context->root_ca_path); + if (ret < 0) { + coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + return ret; + } + mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); + } + + /* + * Verify Peer. + * Need to do all checking, even if setup_data->verify_peer_cert is not set + */ + mbedtls_ssl_conf_verify(&m_env->conf, + cert_verify_callback_mbedtls, c_session); + + return 0; +} + +/* + * PKI SNI callback. + */ +static int +pki_sni_callback(void *p_info, mbedtls_ssl_context *ssl, + const unsigned char *uname, size_t name_len) +{ + unsigned int i; + coap_dtls_pki_t sni_setup_data; + coap_session_t *c_session = (coap_session_t *)p_info; + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + coap_mbedtls_context_t *m_context = + (coap_mbedtls_context_t *)c_session->context->dtls_context; + int ret = 0; + + /* Is this a cached entry? */ + for (i = 0; i < m_context->pki_sni_count; i++) { + if (name_len == strlen(m_context->pki_sni_entry_list[i].sni) && + memcmp(uname, m_context->pki_sni_entry_list[i].sni, name_len) == 0) { + break; + } + } + if (i == m_context->pki_sni_count) { + /* + * New PKI SNI request + */ + char *name; + coap_dtls_key_t *new_entry; + + name = mbedtls_malloc(name_len+1); + memcpy(name, uname, name_len); + name[name_len] = '\000'; + new_entry = + m_context->setup_data.validate_sni_call_back(name, + m_context->setup_data.sni_call_back_arg); + if (!new_entry) { + ret = -1; + mbedtls_free(name); + goto end; + } + + m_context->pki_sni_entry_list = + mbedtls_realloc(m_context->pki_sni_entry_list, + (i+1)*sizeof(pki_sni_entry)); + m_context->pki_sni_entry_list[i].sni = name; + m_context->pki_sni_entry_list[i].pki_key = *new_entry; + sni_setup_data = m_context->setup_data; + sni_setup_data.pki_key = *new_entry; + if ((ret = setup_pki_credentials(&m_context->pki_sni_entry_list[i].cacert, + &m_context->pki_sni_entry_list[i].public_cert, + &m_context->pki_sni_entry_list[i].private_key, + m_env, + m_context, + c_session, + &sni_setup_data, COAP_DTLS_ROLE_SERVER)) < 0) { + ret = -1; + mbedtls_free(name); + goto end; + } + m_context->pki_sni_count++; + } + +end: + if (ret != -1) { + mbedtls_ssl_set_hs_ca_chain(ssl, &m_context->pki_sni_entry_list[i].cacert, + NULL); + return mbedtls_ssl_set_hs_own_cert(ssl, + &m_context->pki_sni_entry_list[i].public_cert, + &m_context->pki_sni_entry_list[i].private_key); + } + return ret; +} + +#ifdef PSK2_PR +/* + * PSK SNI callback. + */ +static int +psk_sni_callback(void *p_info, mbedtls_ssl_context *ssl, + const unsigned char *uname, size_t name_len) +{ + unsigned int i; + coap_dtls_spsk_t sni_setup_data; + coap_session_t *c_session = (coap_session_t *)p_info; + coap_mbedtls_context_t *m_context = + (coap_mbedtls_context_t *)c_session->context->dtls_context; + int ret = 0; + + /* Is this a cached entry? */ + for (i = 0; i < m_context->psk_sni_count; i++) { + if (name_len == m_context->psk_sni_entry_list[i].sni.length && + memcmp(uname, m_context->psk_sni_entry_list[i].sni.s, name_len) == 0) { + break; + } + } + if (i == m_context->psk_sni_count) { + /* + * New PSK SNI request + */ + coap_str_const_t lsni; + uint8_t *name; + const coap_dtls_spsk_info_t *new_entry; + + name = mbedtls_malloc(name_len+1); + memcpy(name, uname, name_len); + name[name_len] = '\000'; + + lsni.s = name; + lsni.length = name_len; + new_entry = + c_session->context->spsk_setup_data.validate_sni_call_back(&lsni, + c_session, + c_session->context->spsk_setup_data.sni_call_back_arg); + if (!new_entry) { + ret = -1; + mbedtls_free(name); + goto end; + } + + m_context->psk_sni_entry_list = + mbedtls_realloc(m_context->psk_sni_entry_list, + (i+1)*sizeof(psk_sni_entry)); + + m_context->psk_sni_entry_list[i].sni.s = name; + m_context->psk_sni_entry_list[i].sni.length = name_len; + m_context->psk_sni_entry_list[i].psk_info = *new_entry; + sni_setup_data = c_session->context->spsk_setup_data; + sni_setup_data.psk_info = *new_entry; + m_context->psk_sni_count++; + } + +end: + if (ret != -1) { + coap_session_refresh_psk_hint(c_session, + &m_context->psk_sni_entry_list[i].psk_info.hint); + coap_session_refresh_psk_key(c_session, + &m_context->psk_sni_entry_list[i].psk_info.key); + return mbedtls_ssl_set_hs_psk(ssl, + m_context->psk_sni_entry_list[i].psk_info.key.s, + m_context->psk_sni_entry_list[i].psk_info.key.length); + } + return ret; +} +#endif /* PSK2_PR */ + +static int setup_server_ssl_session(coap_session_t *c_session, + coap_mbedtls_env_t *m_env) +{ + coap_mbedtls_context_t *m_context = + (coap_mbedtls_context_t *)c_session->context->dtls_context; + int ret = 0; + m_context->psk_pki_enabled |= IS_SERVER; + + mbedtls_ssl_cookie_init(&m_env->cookie_ctx); + if ((ret = mbedtls_ssl_config_defaults(&m_env->conf, + MBEDTLS_SSL_IS_SERVER, + c_session->proto == COAP_PROTO_DTLS ? + MBEDTLS_SSL_TRANSPORT_DATAGRAM : + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + coap_log(LOG_ERR, "mbedtls_ssl_config_defaults returned -0x%x\n", -ret); + goto fail; + } + + mbedtls_ssl_conf_rng(&m_env->conf, mbedtls_ctr_drbg_random, &m_env->ctr_drbg); + +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + mbedtls_ssl_conf_handshake_timeout(&m_env->conf, 1000, 60000); + + if (m_context->psk_pki_enabled & IS_PSK) { +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_PSK_MODES) + mbedtls_ssl_conf_psk_cb(&m_env->conf, psk_server_callback, c_session); +#ifdef PSK2_PR + if (c_session->context->spsk_setup_data.validate_sni_call_back) { + mbedtls_ssl_conf_sni(&m_env->conf, psk_sni_callback, c_session); + } +#endif /* PSK2_PR */ +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_PSK_MODES */ + } +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + + if (m_context->psk_pki_enabled & IS_PKI) { + ret = setup_pki_credentials(&m_env->cacert, &m_env->public_cert, + &m_env->private_key, m_env, m_context, + c_session, &m_context->setup_data, + COAP_DTLS_ROLE_SERVER); + if (ret < 0) { + coap_log(LOG_ERR, "PKI setup failed\n"); + return ret; + } + if (m_context->setup_data.validate_sni_call_back) { + mbedtls_ssl_conf_sni(&m_env->conf, pki_sni_callback, c_session); + } + } + + if ((ret = mbedtls_ssl_cookie_setup(&m_env->cookie_ctx, + mbedtls_ctr_drbg_random, + &m_env->ctr_drbg)) != 0) { + coap_log(LOG_ERR, "mbedtls_ssl_cookie_setup: returned -0x%x\n", -ret); + goto fail; + } + +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + mbedtls_ssl_conf_dtls_cookies(&m_env->conf, mbedtls_ssl_cookie_write, + mbedtls_ssl_cookie_check, + &m_env->cookie_ctx ); + mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ +fail: + return ret; +} + +#define MAX_CIPHERS 100 +static int psk_ciphers[MAX_CIPHERS]; +static int pki_ciphers[MAX_CIPHERS]; +static int processed_ciphers = 0; + +static void +set_ciphersuites(mbedtls_ssl_config *conf, int is_psk) +{ + if (!processed_ciphers) { + const int *list = mbedtls_ssl_list_ciphersuites(); + int *psk_list = psk_ciphers; + int *pki_list = pki_ciphers; + + while (*list) { + const mbedtls_ssl_ciphersuite_t *cur = + mbedtls_ssl_ciphersuite_from_id(*list); + + if (cur) { + if (mbedtls_ssl_ciphersuite_uses_psk(cur)) { + if (&psk_ciphers[MAX_CIPHERS] - psk_list > 1) { + *psk_list = *list; + psk_list++; + } + else { + static int done = 0; + + if (!done) { + done = 1; + coap_log(LOG_ERR, "psk_ciphers[MAX_CIPHERS] insufficient\n"); + } + } + } + else { + if (&pki_ciphers[MAX_CIPHERS] - pki_list > 1) { + *pki_list = *list; + pki_list++; + } + else { + static int done = 0; + + if (!done) { + done = 1; + coap_log(LOG_ERR, "pki_ciphers[MAX_CIPHERS] insufficient\n"); + } + } + } + } + list++; + } + /* zero terminate */ + *psk_list = 0; + *pki_list = 0; + processed_ciphers = 1; + } + mbedtls_ssl_conf_ciphersuites(conf, is_psk ? psk_ciphers : pki_ciphers); +} + +static int setup_client_ssl_session(coap_session_t *c_session, + coap_mbedtls_env_t *m_env) +{ + int ret; + + coap_mbedtls_context_t *m_context = + (coap_mbedtls_context_t *)c_session->context->dtls_context; + + m_context->psk_pki_enabled |= IS_CLIENT; + + if ((ret = mbedtls_ssl_config_defaults(&m_env->conf, + MBEDTLS_SSL_IS_CLIENT, + c_session->proto == COAP_PROTO_DTLS ? + MBEDTLS_SSL_TRANSPORT_DATAGRAM : + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + coap_log(LOG_ERR, "mbedtls_ssl_config_defaults returned -0x%x", -ret); + goto fail; + } + +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + mbedtls_ssl_conf_handshake_timeout(&m_env->conf, 1000, 60000); +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + + mbedtls_ssl_conf_authmode(&m_env->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_rng(&m_env->conf, mbedtls_ctr_drbg_random, &m_env->ctr_drbg); + + if (m_context->psk_pki_enabled & IS_PSK) { +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_PSK_MODES) + uint8_t identity[64]; + size_t identity_len; + uint8_t psk_key[64]; + size_t psk_len; + size_t max_identity_len = sizeof(identity); + + coap_log(LOG_INFO, "Setting PSK key\n"); + psk_len = c_session->context->get_client_psk(c_session, + NULL, + 0, + identity, + &identity_len, + max_identity_len, + psk_key, + sizeof(psk_key)); + assert(identity_len < sizeof(identity)); + mbedtls_ssl_conf_psk(&m_env->conf, (const unsigned char *)psk_key, + psk_len, (const unsigned char *)identity, + identity_len); +#ifdef PSK2_PR + if (c_session->cpsk_setup_data.client_sni) { + mbedtls_ssl_set_hostname(&m_env->ssl, + c_session->cpsk_setup_data.client_sni); + } +#if 0 +/* Identity Hint currently not supported in MbedTLS */ + if (c_session->cpsk_setup_data.validate_ih_call_back) { + coap_log(LOG_DEBUG, + "CoAP Client restricted to (D)TLS1.2 with Identity Hint callback\n"); + mbedtls_ssl_conf_max_version(&m_env->conf, MBEDTLS_SSL_MAJOR_VERSION_3, + MBEDTLS_SSL_MINOR_VERSION_3); + } +#endif +#endif /* PSK2_PR */ + set_ciphersuites(&m_env->conf, 1); +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_PSK_MODES */ + } + else if ((m_context->psk_pki_enabled & IS_PKI) || + (m_context->psk_pki_enabled & (IS_PSK | IS_PKI)) == 0) { + /* + * If neither PSK or PKI have been set up, use PKI basics. + * This works providing COAP_PKI_KEY_PEM has a value of 0. + */ + if ((m_context->psk_pki_enabled & (IS_PSK | IS_PKI)) == 0) { + mbedtls_ssl_conf_authmode(&m_env->conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + } + ret = setup_pki_credentials(&m_env->cacert, &m_env->public_cert, + &m_env->private_key, m_env, m_context, + c_session, &m_context->setup_data, + COAP_DTLS_ROLE_CLIENT); + if (ret < 0) { + coap_log(LOG_ERR, "PKI setup failed\n"); + return ret; + } + if (c_session->proto == COAP_PROTO_TLS) { + const char *alpn_list[2]; + + memset(alpn_list, 0, sizeof(alpn_list)); + alpn_list[0] = "coap"; + ret = mbedtls_ssl_conf_alpn_protocols(&m_env->conf, alpn_list); + if (ret != 0) { + coap_log(LOG_ERR, "ALPN setup failed %d)\n", ret); + } + } + if (m_context->setup_data.client_sni) { + mbedtls_ssl_set_hostname(&m_env->ssl, m_context->setup_data.client_sni); + } +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + set_ciphersuites(&m_env->conf, 0); + } + return 0; + +fail: + return ret; +} + +static void mbedtls_cleanup(coap_mbedtls_env_t *m_env) +{ + if (!m_env) { + return; + } + + mbedtls_x509_crt_free(&m_env->cacert); + mbedtls_x509_crt_free(&m_env->public_cert); + mbedtls_pk_free(&m_env->private_key); + mbedtls_entropy_free(&m_env->entropy); + mbedtls_ssl_config_free(&m_env->conf); + mbedtls_ctr_drbg_free(&m_env->ctr_drbg); + mbedtls_ssl_free(&m_env->ssl); + mbedtls_ssl_cookie_free(&m_env->cookie_ctx); +} + +static void +coap_dtls_free_mbedtls_env(coap_mbedtls_env_t *m_env) { + if (m_env) { + mbedtls_cleanup(m_env); + free(m_env); + } +} + +/* + * return -1 failure + * 0 not completed + * 1 established + */ +static int do_mbedtls_handshake(coap_session_t *c_session, + coap_mbedtls_env_t *m_env) { + int ret; + char buf[128]; + + ret = mbedtls_ssl_handshake(&m_env->ssl); + switch (ret) { + case 0: + m_env->established = 1; + coap_log(LOG_DEBUG, "* %s: MbedTLS established\n", + coap_session_str(c_session)); + ret = 1; + break; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + errno = EAGAIN; + ret = 0; + break; + case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED: + coap_log(LOG_INFO, "hello verification requested\n"); + ret = -1; + mbedtls_ssl_session_reset(&m_env->ssl); + break; + case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; + ret = -1; + break; + default: + mbedtls_strerror(ret, buf, sizeof(buf)); + coap_log(LOG_WARNING, + "do_mbedtls_handshake: session establish " + "returned -0x%x: '%s'\n", + -ret, buf); + ret = -1; + break; + } + return ret; +} + +static void +mbedtls_debug_out(void *ctx UNUSED, int level, + const char *file, int line, const char *str) { + int log_level; + + switch (level) { + case 4: + case 3: + case 2: + log_level = LOG_DEBUG; + break; + case 1: + log_level = LOG_ERR; + break; + case 0: + default: + log_level = 0; + break; + } + coap_log(log_level, "%s:%04d: %s", file, line, str); +} + +static coap_mbedtls_env_t *coap_dtls_new_mbedtls_env(coap_session_t *c_session, + coap_dtls_role_t role) +{ + int ret = 0; + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + + if (m_env) + return m_env; + + m_env = (coap_mbedtls_env_t *)calloc(1, sizeof(coap_mbedtls_env_t)); + if (!m_env) { + return NULL; + } + + mbedtls_ssl_init(&m_env->ssl); + mbedtls_ctr_drbg_init(&m_env->ctr_drbg); + mbedtls_ssl_config_init(&m_env->conf); + mbedtls_entropy_init(&m_env->entropy); + +#if defined(ESPIDF_VERSION) && defined(CONFIG_MBEDTLS_DEBUG) + mbedtls_esp_enable_debug_log(&m_env->conf, CONFIG_MBEDTLS_DEBUG_LEVEL); +#endif /* ESPIDF_VERSION && CONFIG_MBEDTLS_DEBUG */ + if ((ret = mbedtls_ctr_drbg_seed(&m_env->ctr_drbg, + mbedtls_entropy_func, &m_env->entropy, NULL, 0)) != 0) { + coap_log(LOG_ERR, "mbedtls_ctr_drbg_seed returned -0x%x", -ret); + goto fail; + } + + if (role == COAP_DTLS_ROLE_CLIENT) { + if (setup_client_ssl_session(c_session, m_env) != 0) { + goto fail; + } + } else if (role == COAP_DTLS_ROLE_SERVER) { + if (setup_server_ssl_session(c_session, m_env) != 0) { + goto fail; + } + } else { + goto fail; + } + + if ((ret = mbedtls_ssl_setup(&m_env->ssl, &m_env->conf)) != 0) { + goto fail; + } + mbedtls_ssl_set_bio(&m_env->ssl, c_session, coap_dgram_write, + coap_dgram_read, NULL); + mbedtls_ssl_set_timer_cb(&m_env->ssl, &m_env->timer, + mbedtls_timing_set_delay, + mbedtls_timing_get_delay); + + mbedtls_ssl_conf_dbg(&m_env->conf, mbedtls_debug_out, stdout); + return m_env; + +fail: + if (m_env) { + free(m_env); + } + return NULL; +} + +int coap_dtls_is_supported(void) { +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + return 1; +#else /* ESPIDF_VERSION && !CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + coap_log(LOG_EMERG, + "libcoap not compiled for DTLS with MbedTLS" + " - update MbedTLS to include DTLS\n"); + return 0; +#endif /* ESPIDF_VERSION && !CONFIG_MBEDTLS_SSL_PROTO_DTLS */ +} + +int coap_tls_is_supported(void) +{ + return 0; +} + +void *coap_dtls_new_context(struct coap_context_t *c_context) +{ + coap_mbedtls_context_t *m_context; + (void)c_context; + + m_context = (coap_mbedtls_context_t *)calloc(1, sizeof(coap_mbedtls_context_t)); + if (m_context) { + memset(m_context, 0, sizeof(coap_mbedtls_context_t)); + } + return m_context; +} + +#ifndef PSK2_PR +int coap_dtls_context_set_psk(struct coap_context_t *c_context, + const char *identity_hint UNUSED, + coap_dtls_role_t role UNUSED) +{ + coap_mbedtls_context_t *m_context = + ((coap_mbedtls_context_t *)c_context->dtls_context); + m_context->psk_pki_enabled |= IS_PSK; + return 1; +} +#else /* PSK2_PR */ +/* + * return 0 failed + * 1 passed + */ +int +coap_dtls_context_set_spsk(coap_context_t *c_context, + coap_dtls_spsk_t *setup_data +) { + coap_mbedtls_context_t *m_context = + ((coap_mbedtls_context_t *)c_context->dtls_context); + + if (!m_context || !setup_data) + return 0; + + m_context->psk_pki_enabled |= IS_PSK; + return 1; +} + +/* + * return 0 failed + * 1 passed + */ +int +coap_dtls_context_set_cpsk(coap_context_t *c_context, + coap_dtls_cpsk_t *setup_data +) { + coap_mbedtls_context_t *m_context = + ((coap_mbedtls_context_t *)c_context->dtls_context); + + if (!m_context || !setup_data) + return 0; + + if (setup_data->validate_ih_call_back) { + coap_log(LOG_WARNING, + "CoAP Client with MbedTLS does not support Identity Hint selection\n"); + } + m_context->psk_pki_enabled |= IS_PSK; + return 1; +} + +#endif /* PSK2_PR */ + +int coap_dtls_context_set_pki(struct coap_context_t *c_context, + coap_dtls_pki_t *setup_data, + coap_dtls_role_t role UNUSED) +{ + coap_mbedtls_context_t *m_context = + ((coap_mbedtls_context_t *)c_context->dtls_context); + + m_context->setup_data = *setup_data; + m_context->psk_pki_enabled |= IS_PKI; + return 1; +} + +int coap_dtls_context_set_pki_root_cas(struct coap_context_t *c_context, + const char *ca_file, + const char *ca_path) +{ + coap_mbedtls_context_t *m_context = + ((coap_mbedtls_context_t *)c_context->dtls_context); + if (!m_context) { + coap_log(LOG_WARNING, + "coap_context_set_pki_root_cas: (D)TLS environment " + "not set up\n"); + return 0; + } + + if (ca_file == NULL && ca_path == NULL) { + coap_log(LOG_WARNING, + "coap_context_set_pki_root_cas: ca_file and/or ca_path " + "not defined\n"); + return 0; + } + if (m_context->root_ca_file) { + free(m_context->root_ca_file); + m_context->root_ca_file = NULL; + } + + if (ca_file) { + m_context->root_ca_file = mbedtls_strdup(ca_file); + } + + if (m_context->root_ca_path) { + free(m_context->root_ca_path); + m_context->root_ca_path = NULL; + } + + if (ca_path) { + m_context->root_ca_path = mbedtls_strdup(ca_path); + } + return 1; +} + +int coap_dtls_context_check_keys_enabled(struct coap_context_t *c_context) +{ + coap_mbedtls_context_t *m_context = + ((coap_mbedtls_context_t *)c_context->dtls_context); + return m_context->psk_pki_enabled ? 1 : 0; +} + +void coap_dtls_free_context(void *dtls_context) +{ + coap_mbedtls_context_t *m_context = (coap_mbedtls_context_t *)dtls_context; + unsigned int i; + + for (i = 0; i < m_context->pki_sni_count; i++) { + mbedtls_free(m_context->pki_sni_entry_list[i].sni); + + mbedtls_x509_crt_free(&m_context->pki_sni_entry_list[i].public_cert); + + mbedtls_pk_free(&m_context->pki_sni_entry_list[i].private_key); + + mbedtls_x509_crt_free(&m_context->pki_sni_entry_list[i].cacert); + } +#ifdef PSK2_PR + for (i = 0; i < m_context->psk_sni_count; i++) { + mbedtls_free(m_context->psk_sni_entry_list[i].sni.s); + } + if (m_context->psk_sni_entry_list) + mbedtls_free(m_context->pki_sni_entry_list); + +#endif /* PSK2_PR */ + + free(m_context); +} + +void *coap_dtls_new_client_session(coap_session_t *c_session) +{ + coap_mbedtls_env_t *m_env = coap_dtls_new_mbedtls_env(c_session, + COAP_DTLS_ROLE_CLIENT); + int ret; + + if (m_env) { + ret = do_mbedtls_handshake(c_session, m_env); + if (ret == -1) { + coap_dtls_free_mbedtls_env(m_env); + return NULL; + } + } + return m_env; +} + +void *coap_dtls_new_server_session(coap_session_t *c_session) +{ + coap_mbedtls_env_t *m_env = + (coap_mbedtls_env_t *)c_session->tls; + if (m_env) { + m_env->seen_client_hello = 1; +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + } + return m_env; +} + +void coap_dtls_free_session(coap_session_t *c_session) +{ + if (c_session && c_session->context) { + coap_dtls_free_mbedtls_env(c_session->tls); + c_session->tls = NULL; + } + return; +} + +void coap_dtls_session_update_mtu(coap_session_t *c_session) +{ +#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + coap_mbedtls_env_t *m_env = + (coap_mbedtls_env_t *)c_session->tls; + if (m_env) { + mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); + } +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ +} + +int coap_dtls_send(coap_session_t *c_session, + const uint8_t *data, + size_t data_len) +{ + int ret; + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + char buf[128]; + + assert(m_env != NULL); + + if (!m_env) { + return -1; + } + c_session->dtls_event = -1; + if (m_env->established) { + ret = mbedtls_ssl_write(&m_env->ssl, (const unsigned char*) data, data_len); + if (ret <= 0) { + switch (ret) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + ret = 0; + break; + case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; + ret = -1; + break; + default: + mbedtls_strerror(ret, buf, sizeof(buf)); + coap_log(LOG_WARNING, + "coap_dtls_send: " + "returned -0x%x: '%s'\n", + -ret, buf); + ret = -1; + break; + } + if (ret == -1) { + coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n"); + } + } + } else { + ret = do_mbedtls_handshake(c_session, m_env); + if (ret == 1) { + /* Just connected, so send the data */ + return coap_dtls_send(c_session, data, data_len); + } + ret = -1; + } + + if (c_session->dtls_event >= 0) { + coap_handle_event(c_session->context, c_session->dtls_event, c_session); + if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR || + c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) { + coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); + ret = -1; + } + } + return ret; +} + +int coap_dtls_is_context_timeout(void) +{ + return 0; +} + +coap_tick_t coap_dtls_get_context_timeout(void *dtls_context UNUSED) +{ + return 0; +} + +coap_tick_t coap_dtls_get_timeout(coap_session_t *c_session, coap_tick_t now) +{ + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + int ret = mbedtls_timing_get_delay(&m_env->timer); + + switch (ret) { + case 0: + case 1: + /* int_ms has timed out, but not fin_ms */ + return now + 1; + case 2: + /* fin_ms has timed out - time for a retry */ + return now; + default: + break; + } + + return 0; +} + +void coap_dtls_handle_timeout(coap_session_t *c_session) +{ + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + + assert(m_env != NULL); + if (((c_session->state == COAP_SESSION_STATE_HANDSHAKE) && + (++c_session->dtls_timeout_count > c_session->max_retransmit)) || + (do_mbedtls_handshake(c_session, m_env) < 0)) { + /* Too many retries */ + coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); + } + return; +} + +int coap_dtls_receive(coap_session_t *c_session, + const uint8_t *data, + size_t data_len) +{ + int ret = 1; + + c_session->dtls_event = -1; + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + assert(m_env != NULL); + + coap_ssl_t *ssl_data = &m_env->coap_ssl_data; + if (ssl_data->pdu_len) { + coap_log(LOG_INFO, "** %s: Previous data not read %u bytes\n", + coap_session_str(c_session), ssl_data->pdu_len); + } + ssl_data->pdu = data; + ssl_data->pdu_len = (unsigned)data_len; + + if (m_env->established) { +#if COAP_CONSTRAINED_STACK + static coap_mutex_t b_static_mutex = COAP_MUTEX_INITIALIZER; + static uint8_t pdu[COAP_RXBUFFER_SIZE]; +#else /* ! COAP_CONSTRAINED_STACK */ + uint8_t pdu[COAP_RXBUFFER_SIZE]; +#endif /* ! COAP_CONSTRAINED_STACK */ + +#if COAP_CONSTRAINED_STACK + coap_mutex_lock(&b_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + + if (c_session->state == COAP_SESSION_STATE_HANDSHAKE) { + coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED, + c_session); + coap_session_connected(c_session); + } + + ret = mbedtls_ssl_read(&m_env->ssl, pdu, (int)sizeof(pdu)); + if (ret > 0) { + ret = coap_handle_dgram(c_session->context, c_session, pdu, (size_t)ret); +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&b_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + return ret; + } + else if (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; + } + else if (ret != MBEDTLS_ERR_SSL_WANT_READ) { + char buf[128]; + + mbedtls_strerror(ret, buf, sizeof(buf)); + coap_log(LOG_WARNING, + "coap_dtls_receive: " + "returned -0x%x: '%s' (length %zd)\n", + -ret, buf, data_len); + } +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&b_static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + ret = -1; + } + else { + ret = do_mbedtls_handshake(c_session, m_env); + if (ret == 1) { + /* Just connected, so send the data */ + coap_session_connected(c_session); + } else { + if (ssl_data->pdu_len) { + /* Do the handshake again incase of internal timeout */ + ret = do_mbedtls_handshake(c_session, m_env); + if (ret == 1) { + /* Just connected, so send the data */ + coap_session_connected(c_session); + } else { + ret = -1; + } + } + ret = -1; + } + } + if (c_session->dtls_event >= 0) { + coap_handle_event(c_session->context, c_session->dtls_event, c_session); + if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR || + c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) { + coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); + ret = -1; + } + } + return ret; +} + +int coap_dtls_hello(coap_session_t *c_session, + const uint8_t *data, + size_t data_len) +{ +#if defined(ESPIDF_VERSION) && !defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) + (void)c_session; + (void)data; + (void)data_len; + return -1; +#else /* !ESPIDF_VERSION) || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ + coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; + coap_ssl_t *ssl_data = m_env ? &m_env->coap_ssl_data : NULL; + int ret; + + if (m_env) { + char *str = get_ip_addr(&c_session->remote_addr); + if (!str) { + return -1; + } + if((ret = mbedtls_ssl_set_client_transport_id(&m_env->ssl, + (unsigned char *)str, strlen(str))) != 0) { + coap_log(LOG_ERR, + "mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n", + -ret); + free(str); + return -1; + } + free(str); + } + + if (!m_env) { + m_env = coap_dtls_new_mbedtls_env(c_session, COAP_DTLS_ROLE_SERVER); + if (m_env) { + c_session->tls = m_env; + ssl_data = &m_env->coap_ssl_data; + ssl_data->pdu = data; + ssl_data->pdu_len = (unsigned)data_len; + char *str = get_ip_addr(&c_session->remote_addr); + if (!str) { + return -1; + } + if((ret = mbedtls_ssl_set_client_transport_id(&m_env->ssl, + (unsigned char *)str, strlen(str)) ) != 0) { + coap_log(LOG_ERR, + "mbedtls_ssl_set_client_transport_id() returned -0x%x\n", + -ret); + free(str); + return -1; + } + ret = do_mbedtls_handshake(c_session, m_env); + if (ret == 0 || m_env->seen_client_hello) { + m_env->seen_client_hello = 0; + free(str); + return 1; + } + free(str); + } + return 0; + } + + ssl_data->pdu = data; + ssl_data->pdu_len = (unsigned)data_len; + ret = do_mbedtls_handshake(c_session, m_env); + if (ret == 0 || m_env->seen_client_hello) { + /* The test for seen_client_hello gives the ability to setup a new + c_session to continue the do_mbedtls_handshake past the client hello + and safely allow updating of the m_env and separately + letting a new session cleanly start up. + */ + m_env->seen_client_hello = 0; + return 1; + } + return 0; +#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ +} + +unsigned int coap_dtls_get_overhead(coap_session_t *c_session UNUSED) +{ + return 13 + 8 + 8; +} + +void *coap_tls_new_client_session(coap_session_t *c_session UNUSED, int *connected UNUSED) +{ + return NULL; +} + +void *coap_tls_new_server_session(coap_session_t *c_session UNUSED, int *connected UNUSED) +{ + return NULL; +} + +void coap_tls_free_session( coap_session_t *c_session UNUSED) +{ + return; +} + +ssize_t coap_tls_write(coap_session_t *c_session UNUSED, + const uint8_t *data UNUSED, + size_t data_len UNUSED + ) +{ + return 0; +} + +ssize_t coap_tls_read(coap_session_t *c_session UNUSED, + uint8_t *data UNUSED, + size_t data_len UNUSED + ) +{ + return 0; +} + +void coap_dtls_startup(void) +{ + return; +} + +static int keep_log_level = 0; + +void coap_dtls_set_log_level(int level) +{ +#if !defined(ESPIDF_VERSION) + int use_level; + /* + * MbedTLS debug levels filter + * 0 No debug + * 1 Error + * 2 State change + * 3 Informational + * 4 Verbose + */ + + if (level <= LOG_ERR) { + use_level = 1; + } + else { + use_level = (level >= LOG_DEBUG) ? level - LOG_DEBUG + 2 : 0; + } + mbedtls_debug_set_threshold(use_level); +#endif /* !ESPIDF_VERSION) */ + keep_log_level = level; + return; +} + +int coap_dtls_get_log_level(void) +{ + return keep_log_level; +} + +coap_tls_version_t * coap_get_tls_library_version(void) +{ + static coap_tls_version_t version; + version.version = mbedtls_version_get_number(); + version.built_version = MBEDTLS_VERSION_NUMBER; + version.type = COAP_TLS_LIBRARY_MBEDTLS; + return &version; +} + +#else /* !HAVE_MBEDTLS */ + +#ifdef __clang__ +/* Make compilers happy that do not like empty modules. As this function is + * never used, we ignore -Wunused-function at the end of compiling this file + */ +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +static inline void dummy(void) { +} + +#endif /* HAVE_MBEDTLS */ diff --git a/components/coap/port/include/coap/coap_dtls.h b/components/coap/port/include/coap/coap_dtls.h new file mode 100644 index 00000000000..2dd0e88d2e5 --- /dev/null +++ b/components/coap/port/include/coap/coap_dtls.h @@ -0,0 +1,631 @@ +/* + * coap_dtls.h -- (Datagram) Transport Layer Support for libcoap + * + * Copyright (C) 2016 Olaf Bergmann + * Copyright (C) 2017 Jean-Claude Michelou + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#ifndef COAP_DTLS_H_ +#define COAP_DTLS_H_ + +#include "coap_time.h" +#include "str.h" + +struct coap_context_t; +struct coap_session_t; +struct coap_dtls_pki_t; + +/** + * @defgroup dtls DTLS Support + * API functions for interfacing with DTLS libraries. + * @{ + */ + +/** + * Check whether DTLS is available. + * + * @return @c 1 if support for DTLS is enabled, or @c 0 otherwise. + */ +int coap_dtls_is_supported(void); + +/** + * Check whether TLS is available. + * + * @return @c 1 if support for TLS is enabled, or @c 0 otherwise. + */ +int coap_tls_is_supported(void); + +typedef enum coap_tls_library_t { + COAP_TLS_LIBRARY_NOTLS = 0, /**< No DTLS library */ + COAP_TLS_LIBRARY_TINYDTLS, /**< Using TinyDTLS library */ + COAP_TLS_LIBRARY_OPENSSL, /**< Using OpenSSL library */ + COAP_TLS_LIBRARY_GNUTLS, /**< Using GnuTLS library */ + COAP_TLS_LIBRARY_MBEDTLS, /**< Using MbedTLS library */ +} coap_tls_library_t; + +/** + * The structure used for returning the underlying (D)TLS library + * information. + */ +typedef struct coap_tls_version_t { + uint64_t version; /**< (D)TLS runtime Library Version */ + coap_tls_library_t type; /**< Library type. One of COAP_TLS_LIBRARY_* */ + uint64_t built_version; /**< (D)TLS Built against Library Version */ +} coap_tls_version_t; + +/** + * Determine the type and version of the underlying (D)TLS library. + * + * @return The version and type of library libcoap was compiled against. + */ +coap_tls_version_t *coap_get_tls_library_version(void); + +/** + * Additional Security setup handler that can be set up by + * coap_context_set_pki(). + * Invoked when libcoap has done the validation checks at the TLS level, + * but the application needs to do some additional checks/changes/updates. + * + * @param tls_session The security session definition - e.g. SSL * for OpenSSL. + * NULL if server call-back. + * This will be dependent on the underlying TLS library - + * see coap_get_tls_library_version() + * @param setup_data A structure containing setup data originally passed into + * coap_context_set_pki() or coap_new_client_session_pki(). + * + * @return @c 1 if successful, else @c 0. + */ +typedef int (*coap_dtls_security_setup_t)(void* tls_session, + struct coap_dtls_pki_t *setup_data); + +/** + * CN Validation call-back that can be set up by coap_context_set_pki(). + * Invoked when libcoap has done the validation checks at the TLS level, + * but the application needs to check that the CN is allowed. + * CN is the SubjectAltName in the cert, if not present, then the leftmost + * Common Name (CN) component of the subject name. + * + * @param cn The determined CN from the certificate + * @param asn1_public_cert The ASN.1 DER encoded X.509 certificate + * @param asn1_length The ASN.1 length + * @param coap_session The CoAP session associated with the certificate update + * @param depth Depth in cert chain. If 0, then client cert, else a CA + * @param validated TLS layer can find no issues if 1 + * @param arg The same as was passed into coap_context_set_pki() + * in setup_data->cn_call_back_arg + * + * @return @c 1 if accepted, else @c 0 if to be rejected. + */ +typedef int (*coap_dtls_cn_callback_t)(const char *cn, + const uint8_t *asn1_public_cert, + size_t asn1_length, + struct coap_session_t *coap_session, + unsigned depth, + int validated, + void *arg); + +/** + * The enum used for determining the provided PKI ASN.1 (DER) Private Key + * formats. + */ +typedef enum coap_asn1_privatekey_type_t { + COAP_ASN1_PKEY_NONE, /**< NONE */ + COAP_ASN1_PKEY_RSA, /**< RSA type */ + COAP_ASN1_PKEY_RSA2, /**< RSA2 type */ + COAP_ASN1_PKEY_DSA, /**< DSA type */ + COAP_ASN1_PKEY_DSA1, /**< DSA1 type */ + COAP_ASN1_PKEY_DSA2, /**< DSA2 type */ + COAP_ASN1_PKEY_DSA3, /**< DSA3 type */ + COAP_ASN1_PKEY_DSA4, /**< DSA4 type */ + COAP_ASN1_PKEY_DH, /**< DH type */ + COAP_ASN1_PKEY_DHX, /**< DHX type */ + COAP_ASN1_PKEY_EC, /**< EC type */ + COAP_ASN1_PKEY_HMAC, /**< HMAC type */ + COAP_ASN1_PKEY_CMAC, /**< CMAC type */ + COAP_ASN1_PKEY_TLS1_PRF, /**< TLS1_PRF type */ + COAP_ASN1_PKEY_HKDF /**< HKDF type */ +} coap_asn1_privatekey_type_t; + +/** + * The enum used for determining the PKI key formats. + */ +typedef enum coap_pki_key_t { + COAP_PKI_KEY_PEM = 0, /**< The PKI key type is PEM file */ + COAP_PKI_KEY_ASN1, /**< The PKI key type is ASN.1 (DER) */ + COAP_PKI_KEY_PEM_BUF, /**< The PKI key type is PEM buffer */ +} coap_pki_key_t; + +/** + * The structure that holds the PKI PEM definitions. + */ +typedef struct coap_pki_key_pem_t { + const char *ca_file; /**< File location of Common CA in PEM format */ + const char *public_cert; /**< File location of Public Cert in PEM format */ + const char *private_key; /**< File location of Private Key in PEM format */ +} coap_pki_key_pem_t; + +/** + * The structure that holds the PKI PEM buffer definitions. + */ +typedef struct coap_pki_key_pem_buf_t { + const uint8_t *ca_cert; /**< PEM buffer Common CA Cert */ + const uint8_t *public_cert; /**< PEM buffer Public Cert */ + const uint8_t *private_key; /**< PEM buffer Private Key */ + size_t ca_cert_len; /**< PEM buffer CA Cert length */ + size_t public_cert_len; /**< PEM buffer Public Cert length */ + size_t private_key_len; /**< PEM buffer Private Key length */ +} coap_pki_key_pem_buf_t; + +/** + * The structure that holds the PKI ASN.1 (DER) definitions. + */ +typedef struct coap_pki_key_asn1_t { + const uint8_t *ca_cert; /**< ASN1 (DER) Common CA Cert */ + const uint8_t *public_cert; /**< ASN1 (DER) Public Cert */ + const uint8_t *private_key; /**< ASN1 (DER) Private Key */ + size_t ca_cert_len; /**< ASN1 CA Cert length */ + size_t public_cert_len; /**< ASN1 Public Cert length */ + size_t private_key_len; /**< ASN1 Private Key length */ + coap_asn1_privatekey_type_t private_key_type; /**< Private Key Type */ +} coap_pki_key_asn1_t; + +/** + * The structure that holds the PKI key information. + */ +typedef struct coap_dtls_key_t { + coap_pki_key_t key_type; /**< key format type */ + union { + coap_pki_key_pem_t pem; /**< for PEM file keys */ + coap_pki_key_pem_buf_t pem_buf; /**< for PEM memory keys */ + coap_pki_key_asn1_t asn1; /**< for ASN.1 (DER) file keys */ + } key; +} coap_dtls_key_t; + +/** + * Server Name Indication (SNI) Validation call-back that can be set up by + * coap_context_set_pki(). + * Invoked if the SNI is not previously seen and prior to sending a certificate + * set back to the client so that the appropriate certificate set can be used + * based on the requesting SNI. + * + * @param sni The requested SNI + * @param arg The same as was passed into coap_context_set_pki() + * in setup_data->sni_call_back_arg + * + * @return New set of certificates to use, or @c NULL if SNI is to be rejected. + */ +typedef coap_dtls_key_t *(*coap_dtls_sni_callback_t)(const char *sni, + void* arg); + + +#define COAP_DTLS_PKI_SETUP_VERSION 1 /**< Latest PKI setup version */ + +/** + * The structure used for defining the PKI setup data to be used. + */ +typedef struct coap_dtls_pki_t { + uint8_t version; /** Set to 1 to support this version of the struct */ + + /* Options to enable different TLS functionality in libcoap */ + uint8_t verify_peer_cert; /**< 1 if peer cert is to be verified */ + uint8_t require_peer_cert; /**< 1 if peer cert is required */ + uint8_t allow_self_signed; /**< 1 if self signed certs are allowed */ + uint8_t allow_expired_certs; /**< 1 if expired certs are allowed */ + uint8_t cert_chain_validation; /**< 1 if to check cert_chain_verify_depth */ + uint8_t cert_chain_verify_depth; /**< recommended depth is 3 */ + uint8_t check_cert_revocation; /**< 1 if revocation checks wanted */ + uint8_t allow_no_crl; /**< 1 ignore if CRL not there */ + uint8_t allow_expired_crl; /**< 1 if expired crl is allowed */ + uint8_t allow_bad_md_hash; /**< 1 if expired certs are allowed */ + uint8_t allow_short_rsa_length; /**< 1 if expired certs are allowed */ + uint8_t reserved[4]; /**< Reserved - must be set to 0 for + future compatibility */ + /* Size of 4 chosen to align to next + * parameter, so if newly defined option + * it can use one of the reserverd slot so + * no need to change + * COAP_DTLS_PKI_SETUP_VERSION and just + * decrement the reserved[] count. + */ + + /** CN check call-back function. + * If not NULL, is called when the TLS connection has passed the configured + * TLS options above for the application to verify if the CN is valid. + */ + coap_dtls_cn_callback_t validate_cn_call_back; + void *cn_call_back_arg; /**< Passed in to the CN call-back function */ + + /** SNI check call-back function. + * If not @p NULL, called if the SNI is not previously seen and prior to + * sending a certificate set back to the client so that the appropriate + * certificate set can be used based on the requesting SNI. + */ + coap_dtls_sni_callback_t validate_sni_call_back; + void *sni_call_back_arg; /**< Passed in to the sni call-back function */ + + /** Additional Security call-back handler that is invoked when libcoap has + * done the standerd, defined validation checks at the TLS level, + * If not @p NULL, called from within the TLS Client Hello connection + * setup. + */ + coap_dtls_security_setup_t additional_tls_setup_call_back; + + char* client_sni; /**< If not NULL, SNI to use in client TLS setup. + Owned by the client app and must remain valid + during the call to coap_new_client_session_pki() */ + + coap_dtls_key_t pki_key; /**< PKI key definition */ +} coap_dtls_pki_t; + +/** @} */ + +/** + * @defgroup dtls_internal DTLS Support (Internal) + * Internal API functions for interfacing with DTLS libraries. + * @{ + */ + +/** + * Creates a new DTLS context for the given @p coap_context. This function + * returns a pointer to a new DTLS context object or @c NULL on error. + * + * Internal function. + * + * @param coap_context The CoAP context where the DTLS object shall be used. + * + * @return A DTLS context object or @c NULL on error. + */ +void * +coap_dtls_new_context(struct coap_context_t *coap_context); + +typedef enum coap_dtls_role_t { + COAP_DTLS_ROLE_CLIENT, /**< Internal function invoked for client */ + COAP_DTLS_ROLE_SERVER /**< Internal function invoked for server */ +} coap_dtls_role_t; + +/** + * Set the DTLS context's default PSK information. + * This does the PSK specifics following coap_dtls_new_context(). + * If @p COAP_DTLS_ROLE_SERVER, then identity hint will also get set. + * If @p COAP_DTLS_ROLE_SERVER, then the information will get put into the + * TLS library's context (from which sessions are derived). + * If @p COAP_DTLS_ROLE_CLIENT, then the information will get put into the + * TLS library's session. + * + * Internal function. + * + * @param coap_context The CoAP context. + * @param identity_hint The default PSK server identity hint sent to a client. + * Required parameter. If @p NULL, will be set to "". + * Empty string is a valid hint. + * This parameter is ignored if COAP_DTLS_ROLE_CLIENT + * @param role One of @p COAP_DTLS_ROLE_CLIENT or @p COAP_DTLS_ROLE_SERVER + * + * @return @c 1 if successful, else @c 0. + */ + +int +coap_dtls_context_set_psk(struct coap_context_t *coap_context, + const char *identity_hint, + coap_dtls_role_t role); + +/** + * Set the DTLS context's default server PKI information. + * This does the PKI specifics following coap_dtls_new_context(). + * If @p COAP_DTLS_ROLE_SERVER, then the information will get put into the + * TLS library's context (from which sessions are derived). + * If @p COAP_DTLS_ROLE_CLIENT, then the information will get put into the + * TLS library's session. + * + * Internal function. + * + * @param coap_context The CoAP context. + * @param setup_data Setup information defining how PKI is to be setup. + * Required parameter. If @p NULL, PKI will not be + * set up. + * @param role One of @p COAP_DTLS_ROLE_CLIENT or @p COAP_DTLS_ROLE_SERVER + * + * @return @c 1 if successful, else @c 0. + */ + +int +coap_dtls_context_set_pki(struct coap_context_t *coap_context, + coap_dtls_pki_t *setup_data, + coap_dtls_role_t role); + +/** + * Set the dtls context's default Root CA information for a client or server. + * + * Internal function. + * + * @param coap_context The current coap_context_t object. + * @param ca_file If not @p NULL, is the full path name of a PEM encoded + * file containing all the Root CAs to be used. + * @param ca_dir If not @p NULL, points to a directory containing PEM + * encoded files containing all the Root CAs to be used. + * + * @return @c 1 if successful, else @c 0. + */ + +int +coap_dtls_context_set_pki_root_cas(struct coap_context_t *coap_context, + const char *ca_file, + const char *ca_dir); + +/** + * Check whether one of the coap_dtls_context_set_{psk|pki}() functions have + * been called. + * + * Internal function. + * + * @param coap_context The current coap_context_t object. + * + * @return @c 1 if coap_dtls_context_set_{psk|pki}() called, else @c 0. + */ + +int coap_dtls_context_check_keys_enabled(struct coap_context_t *coap_context); + +/** + * Releases the storage allocated for @p dtls_context. + * + * Internal function. + * + * @param dtls_context The DTLS context as returned by coap_dtls_new_context(). + */ +void coap_dtls_free_context(void *dtls_context); + +/** + * Create a new client-side session. This should send a HELLO to the server. + * + * Internal function. + * + * @param coap_session The CoAP session. + * + * @return Opaque handle to underlying TLS library object containing security + * parameters for the session. +*/ +void *coap_dtls_new_client_session(struct coap_session_t *coap_session); + +/** + * Create a new DTLS server-side session. + * Called after coap_dtls_hello() has returned @c 1, signalling that a validated + * HELLO was received from a client. + * This should send a HELLO to the server. + * + * Internal function. + * + * @param coap_session The CoAP session. + * + * @return Opaque handle to underlying TLS library object containing security + * parameters for the DTLS session. + */ +void *coap_dtls_new_server_session(struct coap_session_t *coap_session); + +/** + * Terminates the DTLS session (may send an ALERT if necessary) then frees the + * underlying TLS library object containing security parameters for the session. + * + * Internal function. + * + * @param coap_session The CoAP session. + */ +void coap_dtls_free_session(struct coap_session_t *coap_session); + +/** + * Notify of a change in the CoAP session's MTU, for example after + * a PMTU update. + * + * Internal function. + * + * @param coap_session The CoAP session. + */ +void coap_dtls_session_update_mtu(struct coap_session_t *coap_session); + +/** + * Send data to a DTLS peer. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param data pointer to data. + * @param data_len Number of bytes to send. + * + * @return @c 0 if this would be blocking, @c -1 if there is an error or the + * number of cleartext bytes sent. + */ +int coap_dtls_send(struct coap_session_t *coap_session, + const uint8_t *data, + size_t data_len); + +/** + * Check if timeout is handled per CoAP session or per CoAP context. + * + * Internal function. + * + * @return @c 1 of timeout and retransmit is per context, @c 0 if it is + * per session. + */ +int coap_dtls_is_context_timeout(void); + +/** + * Do all pending retransmits and get next timeout + * + * Internal function. + * + * @param dtls_context The DTLS context. + * + * @return @c 0 if no event is pending or date of the next retransmit. + */ +coap_tick_t coap_dtls_get_context_timeout(void *dtls_context); + +/** + * Get next timeout for this session. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param now The current time in ticks. + * + * @return @c 0 If no event is pending or ticks time of the next retransmit. + */ +coap_tick_t coap_dtls_get_timeout(struct coap_session_t *coap_session, + coap_tick_t now); + +/** + * Handle a DTLS timeout expiration. + * + * Internal function. + * + * @param coap_session The CoAP session. + */ +void coap_dtls_handle_timeout(struct coap_session_t *coap_session); + +/** + * Handling incoming data from a DTLS peer. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param data Encrypted datagram. + * @param data_len Encrypted datagram size. + * + * @return Result of coap_handle_dgram on the decrypted CoAP PDU + * or @c -1 for error. + */ +int coap_dtls_receive(struct coap_session_t *coap_session, + const uint8_t *data, + size_t data_len); + +/** + * Handling client HELLO messages from a new candiate peer. + * Note that session->tls is empty. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param data Encrypted datagram. + * @param data_len Encrypted datagram size. + * + * @return @c 0 if a cookie verification message has been sent, @c 1 if the + * HELLO contains a valid cookie and a server session should be created, + * @c -1 if the message is invalid. + */ +int coap_dtls_hello(struct coap_session_t *coap_session, + const uint8_t *data, + size_t data_len); + +/** + * Get DTLS overhead over cleartext PDUs. + * + * Internal function. + * + * @param coap_session The CoAP session. + * + * @return Maximum number of bytes added by DTLS layer. + */ +unsigned int coap_dtls_get_overhead(struct coap_session_t *coap_session); + +/** + * Create a new TLS client-side session. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param connected Updated with whether the connection is connected yet or not. + * @c 0 is not connected, @c 1 is connected. + * + * @return Opaque handle to underlying TLS library object containing security + * parameters for the session. +*/ +void *coap_tls_new_client_session(struct coap_session_t *coap_session, int *connected); + +/** + * Create a TLS new server-side session. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param connected Updated with whether the connection is connected yet or not. + * @c 0 is not connected, @c 1 is connected. + * + * @return Opaque handle to underlying TLS library object containing security + * parameters for the session. + */ +void *coap_tls_new_server_session(struct coap_session_t *coap_session, int *connected); + +/** + * Terminates the TLS session (may send an ALERT if necessary) then frees the + * underlying TLS library object containing security parameters for the session. + * + * Internal function. + * + * @param coap_session The CoAP session. + */ +void coap_tls_free_session( struct coap_session_t *coap_session ); + +/** + * Send data to a TLS peer, with implicit flush. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param data Pointer to data. + * @param data_len Number of bytes to send. + * + * @return @c 0 if this should be retried, @c -1 if there is an error + * or the number of cleartext bytes sent. + */ +ssize_t coap_tls_write(struct coap_session_t *coap_session, + const uint8_t *data, + size_t data_len + ); + +/** + * Read some data from a TLS peer. + * + * Internal function. + * + * @param coap_session The CoAP session. + * @param data Pointer to data. + * @param data_len Maximum number of bytes to read. + * + * @return @c 0 if this should be retried, @c -1 if there is an error + * or the number of cleartext bytes read. + */ +ssize_t coap_tls_read(struct coap_session_t *coap_session, + uint8_t *data, + size_t data_len + ); + +/** + * Initialize the underlying (D)TLS Library layer. + * + * Internal function. + * + */ +void coap_dtls_startup(void); + +/** @} */ + +/** + * @ingroup logging + * Sets the (D)TLS logging level to the specified @p level. + * Note: coap_log_level() will influence output if at a specified level. + * + * @param level The logging level to use - LOG_* + */ +void coap_dtls_set_log_level(int level); + +/** + * @ingroup logging + * Get the current (D)TLS logging. + * + * @return The current log level (one of LOG_*). + */ +int coap_dtls_get_log_level(void); + + +#endif /* COAP_DTLS_H */ diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h index 8f5a5cfb101..dc1166090d3 100644 --- a/components/coap/port/include/coap_config_posix.h +++ b/components/coap/port/include/coap_config_posix.h @@ -21,18 +21,27 @@ #ifdef WITH_POSIX #include +#include #define HAVE_SYS_SOCKET_H #define HAVE_MALLOC #define HAVE_ARPA_INET_H #define HAVE_TIME_H +#define HAVE_NETDB_H +#define HAVE_NETINET_IN_H #define IPV6_PKTINFO IPV6_V6ONLY #define PACKAGE_NAME "libcoap-posix" #define PACKAGE_VERSION "?" -#define COAP_BAD_RECVMSG +#define HAVE_MBEDTLS +#define COAP_CONSTRAINED_STACK 1 +#define ESPIDF_VERSION + +#define _POSIX_TIMERS 1 + +#define gai_strerror(x) "gai_strerror() not supported" #endif /* WITH_POSIX */ #endif /* COAP_CONFIG_POSIX_H_ */ diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 5cfac4d9ba1..b37269f02b8 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -25,6 +25,7 @@ target_sources(mbedtls PRIVATE "${COMPONENT_DIR}/port/esp_bignum.c" "${COMPONENT_DIR}/port/esp_sha1.c" "${COMPONENT_DIR}/port/esp_sha256.c" "${COMPONENT_DIR}/port/esp_sha512.c" + "${COMPONENT_DIR}/port/esp_timing.c" "${COMPONENT_DIR}/port/mbedtls_debug.c" "${COMPONENT_DIR}/port/net_sockets.c" "${COMPONENT_DIR}/port/esp32/aes.c" @@ -36,4 +37,4 @@ foreach(target ${mbedtls_targets}) endforeach() # Link mbedtls libraries to component library -target_link_libraries(${COMPONENT_LIB} INTERFACE ${mbedtls_targets}) \ No newline at end of file +target_link_libraries(${COMPONENT_LIB} INTERFACE ${mbedtls_targets}) diff --git a/components/mbedtls/port/esp_timing.c b/components/mbedtls/port/esp_timing.c new file mode 100644 index 00000000000..93a34f5fa8d --- /dev/null +++ b/components/mbedtls/port/esp_timing.c @@ -0,0 +1,102 @@ +/* + * Portable interface to the CPU cycle counter + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * mbedtls_timing_get_timer()m mbedtls_timing_set_delay() and + * mbedtls_timing_set_delay only abstracted from mbedtls/library/timing.c + * as that does not build on ESP-IDF but these 2 functions are needed for + * DTLS (in particular mbedtls_ssl_set_timer_cb() must be called for DTLS + * which requires these 2 delay functions). + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if !defined(MBEDTLS_ESP_TIMING_C) + +#include +#include "mbedtls/timing.h" + +struct _hr_time +{ + struct timeval start; +}; + +unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) +{ + struct _hr_time *t = (struct _hr_time *) val; + + if( reset ) + { + gettimeofday( &t->start, NULL ); + return( 0 ); + } + else + { + unsigned long delta; + struct timeval now; + gettimeofday( &now, NULL ); + delta = ( now.tv_sec - t->start.tv_sec ) * 1000ul + + ( now.tv_usec - t->start.tv_usec ) / 1000; + return( delta ); + } +} + +/* + * Set delays to watch + */ +void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms ) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + + ctx->int_ms = int_ms; + ctx->fin_ms = fin_ms; + + if( fin_ms != 0 ) + (void) mbedtls_timing_get_timer( &ctx->timer, 1 ); +} + +/* + * Get number of delays expired + */ +int mbedtls_timing_get_delay( void *data ) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + unsigned long elapsed_ms; + + if( ctx->fin_ms == 0 ) + return( -1 ); + + elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 ); + + if( elapsed_ms >= ctx->fin_ms ) + return( 2 ); + + if( elapsed_ms >= ctx->int_ms ) + return( 1 ); + + return( 0 ); +} + +#endif /* MBEDTLS_ESP_TIMING_C */ diff --git a/examples/protocols/coap_client/README.md b/examples/protocols/coap_client/README.md index d058e3c3114..d25b8d30cac 100644 --- a/examples/protocols/coap_client/README.md +++ b/examples/protocols/coap_client/README.md @@ -2,14 +2,27 @@ # CoAP client example (See the README.md file in the upper level 'examples' directory for more information about examples.) -this CoAP client example is adaptation of one of the [libcoap](https://github.com/obgm/libcoap) example. +This CoAP client example is very simplified adaptation of one of the +[libcoap](https://github.com/obgm/libcoap) examples. -CoAP client example would connect your ESP32 device to any CoAP server, fetch data from CoAP server and upstream data to CoAP server. +CoAP client example will connect your ESP32 device to a CoAP server, send off a GET request and +fetch the response data from CoAP server. The client can be extended to PUT / POST / DELETE requests, +as well as supporting the Observer extensions [RFC7641](https://tools.ietf.org/html/rfc7641). -The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things. -The protocol is designed for machine-to-machine (M2M) applications such as smart energy and building automation. +If the URI is prefixed with coaps:// instead of coap://, then the CoAP client will attempt to use +the DTLS protocol using the defined Pre-Shared Keys(PSK) or Public Key Infrastructure (PKI) which the +CoAP server needs to know about. -please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. +If the URI is prefixed with coap+tcp://, then the CoAP will try to use TCP for the communication. + +NOTE: coaps+tcp:// is not currently supported, even though both libcoap and MbedTLS support it. + +The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with +constrained nodes and constrained networks in the Internet of Things. +The protocol is designed for machine-to-machine (M2M) applications such as smart energy and +building automation. + +Please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. ## How to use example @@ -19,16 +32,29 @@ please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) make menuconfig ``` -* Set serial port under Serial Flasher config -* Set Target Uri under Example Configuration -* Set WiFi SSID under Example Configuration -* Set WiFi Password under Example Configuration +Example Connection Configuration ---> + * Set WiFi SSID under Example Configuration + * Set WiFi Password under Example Configuration +Example CoAP Client Configuration ---> + * Set CoAP Target Uri + * Set encryption method definitions (None, PSK or PKI) + * If PSK Set CoAP Preshared Key to use in connection to the server + * If PSK Set CoAP PSK Client identity (username) + Enable CoAP debugging if required +Component config ---> + mbedTLS ---> + [*] Enable mbedtls certificate expiry check + TLS Key Exchange Methods ---> + [*] Enable pre-shared-key ciphersuites + [*] Enable PSK based ciphersuite modes + [*] Support DTLS protocol (all versions) ### Build and Flash Build the project and flash it to the board, then run monitor tool to view serial output: ``` +make make -j4 flash monitor ``` @@ -72,9 +98,11 @@ published under EPL+EDL: http://www.eclipse.org/californium/ This can be found at https://libcoap.net/doc/reference/4.2.0/ ## Troubleshooting -* Please make sure Target Url includes valid `host`, optional `port`, optional `path`, and begins -with `coap://` or `coap+tcp://` for a coap server that supports TCP +* Please make sure Target Url includes valid `host`, optional `port`, +optional `path`, and begins with `coap://`, `coaps://` or `coap+tcp://` +for a coap server that supports TCP (not all do including coap+tcp://californium.eclipse.org). -* libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0` -to `#define COAP_LOGGING_LEVEL 9` +* CoAP logging can be enabled by running 'make menuconfig' and enable debugging + +* Encryption (MbedTLS) can be enabled by running 'make menuconfig' and enable debugging diff --git a/examples/protocols/coap_client/main/CMakeLists.txt b/examples/protocols/coap_client/main/CMakeLists.txt index 90a88c9d3b6..eb0d270668b 100644 --- a/examples/protocols/coap_client/main/CMakeLists.txt +++ b/examples/protocols/coap_client/main/CMakeLists.txt @@ -1,2 +1,4 @@ +# Embed CA, certificate & key directly into binary idf_component_register(SRCS "coap_client_example_main.c" - INCLUDE_DIRS ".") \ No newline at end of file + INCLUDE_DIRS "." + EMBED_TXTFILES coap_ca.pem coap_client.crt coap_client.key) diff --git a/examples/protocols/coap_client/main/Kconfig.projbuild b/examples/protocols/coap_client/main/Kconfig.projbuild index 929b8cf4fa9..f96ac41e4a7 100644 --- a/examples/protocols/coap_client/main/Kconfig.projbuild +++ b/examples/protocols/coap_client/main/Kconfig.projbuild @@ -1,9 +1,96 @@ -menu "Example Configuration" +menu "Example CoAP Client Configuration" config TARGET_DOMAIN_URI string "Target Uri" default "coap://californium.eclipse.org" help - Target uri for the example to use. + Target uri for the example to use. Use coaps:// prefix for encrypted traffic + using Pre-Shared Key (PSK) or Public Key Infrastructure (PKI). + + choice MBEDTLS_COAP_ENCRYPTION_MODE + prompt "CoAP Encryption method" + default MBEDTLS_COAP_PKI_NONE + help + If the CoAP information is to be encrypted, the encryption environment + can be set up in one of three ways + + - None defined (will use PKI if coaps:// used) + - Encrypt using defined Pre-Shared Keys (PSK) + - Encrypt using defined Public Key Infrastructure (PKI) + + config MBEDTLS_COAP_NONE + bool "None defined" + + config MBEDTLS_COAP_PSK + bool "Pre-Shared Keys" + + config MBEDTLS_COAP_PKI + bool "PKI Certificates" + + endchoice #MBEDTLS_COAP_ENCRYPTION_MODE + + config COAP_PSK_KEY + string "Preshared Key (PSK) to used in the connection to the CoAP server" + depends on MBEDTLS_COAP_PSK + default "secret-key" + help + The Preshared Key to use to encrypt the communicatons. The same key must be + used at both ends of the CoAP connection, and the CoaP client must request + an URI prefixed with coaps:// instead of coap:// for DTLS to be used. + + config COAP_PSK_IDENTITY + string "PSK Client identity (username)" + depends on MBEDTLS_COAP_PSK + default "coap-client" + help + The identity (or username) to use to identify to the CoAP server which + PSK key to use. + + config MBEDTLS_COAP_DEBUG + bool "Enable CoAP debugging" + default n + help + Enable CoAP debugging functions at compile time for the example code. + + If this option is enabled, call coap_set_log_level() + at runtime in order to enable CoAP debug output via the ESP + log mechanism. + + choice MBEDTLS_COAP_DEBUG_LEVEL + bool "Set CoAP debugging level" + depends on MBEDTLS_COAP_DEBUG + default COAP_LOG_WARNING + help + Set CoAP debugging level + + config COAP_LOG_EMERG + bool "Emergency" + config COAP_LOG_ALERT + bool "Alert" + config COAP_LOG_CRIT + bool "Critical" + config COAP_LOG_ERROR + bool "Error" + config COAP_LOG_WARNING + bool "Warning" + config COAP_LOG_NOTICE + bool "Notice" + config COAP_LOG_INFO + bool "Info" + config COAP_LOG_DEBUG + bool "Debug" + endchoice + + config COAP_LOG_DEFAULT_LEVEL + int + default 0 if !MBEDTLS_COAP_DEBUG + default 0 if COAP_LOG_EMERG + default 1 if COAP_LOG_ALERT + default 2 if COAP_LOG_CRIT + default 3 if COAP_LOG_ERROR + default 4 if COAP_LOG_WARNING + default 5 if COAP_LOG_NOTICE + default 6 if COAP_LOG_INFO + default 7 if COAP_LOG_DEBUG endmenu diff --git a/examples/protocols/coap_client/main/coap_ca.pem b/examples/protocols/coap_client/main/coap_ca.pem new file mode 100644 index 00000000000..1bdf23d94bb --- /dev/null +++ b/examples/protocols/coap_client/main/coap_ca.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD +VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT +BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs +ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w +DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh +bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG +A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66 +1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe +vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p +AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG +A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG +A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl +LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ +5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93 +d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA +euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW +S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt +PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc= +-----END CERTIFICATE----- diff --git a/examples/protocols/coap_client/main/coap_client.crt b/examples/protocols/coap_client/main/coap_client.crt new file mode 100644 index 00000000000..0e03a1b79bf --- /dev/null +++ b/examples/protocols/coap_client/main/coap_client.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 48 (0x30) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Jun 7 08:06:49 2017 GMT + Not After : Jun 5 08:06:49 2027 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=user@example.com/emailAddress=user@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d2:f6:be:72:a5:ab:2e:56:0c:dd:f2:3b:2c:7c: + e0:5d:05:40:af:0c:8c:f3:82:0c:d0:18:34:b4:e3: + 7d:5f:8d:0a:3e:aa:79:02:f9:96:ad:10:00:ec:51: + e9:dc:3f:fb:ea:b0:57:eb:48:c7:ca:ef:e8:05:ab: + ee:3f:66:ba:5c:9e:7f:40:85:9f:25:a0:e0:e3:7c: + cf:b6:e6:31:f5:fd:24:03:c8:f4:fb:d8:a4:f3:92: + 29:05:aa:55:43:80:f7:3e:13:10:43:3a:89:24:be: + d8:01:86:d1:69:73:44:7d:f8:b9:46:2b:6b:51:d0: + 11:31:4b:06:ae:9f:45:fa:12:17:0c:ef:6a:fa:d0: + f7:36:46:eb:2e:db:4e:20:46:01:33:ac:b1:f7:4a: + e6:18:3d:53:22:dc:e8:4a:12:78:11:2f:e4:3b:92: + bd:d7:07:5a:c9:81:5d:48:58:c8:0f:9b:e9:a4:0f: + bb:89:b1:ad:38:07:6f:93:d0:a6:12:56:f9:07:48: + d2:23:2f:a3:a9:93:b0:11:0a:27:4c:48:0a:8d:70: + 41:68:76:7a:dd:bc:54:c3:42:33:b0:7b:f6:ae:1f: + e7:95:5e:11:ca:f2:b4:4b:5c:ba:47:64:f0:f3:d7: + 87:95:7f:93:06:a1:72:c9:81:12:a5:b7:8f:9d:7e: + d1:ef + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 2d:02:bc:7b:88:b8:5c:e1:07:b8:bb:ba:b2:f3:98:14:8f:cb: + b0:21:13:b5:e5:6f:05:4f:92:fa:ac:c0:53:a7:b0:cd:7e:ba: + 87:36:85:25:d7:41:c5:29:84:22:74:af:bf:3e:34:36:d5:24: + 7a:81:e2:1b:54:52:85:6f:76:de:dc:63:98:45:fc:2c:31:fa: + 22:a4:72:3a:8d:d4:6a:2e:de:33:10:41:eb:94:1d:e3:59:cd: + b2:be:ab:f0:b6:20:86:9c:b8:46:ee:c5:64:ba:b6:6c:cc:53: + 44:7a:80:12:77:7c:e7:51:67:91:32:2f:88:9d:93:a8:ef:d6: + cd:de +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBMDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw +ODA2NDlaFw0yNzA2MDUwODA2NDlaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANL2vnKlqy5WDN3yOyx84F0FQK8MjPOC +DNAYNLTjfV+NCj6qeQL5lq0QAOxR6dw/++qwV+tIx8rv6AWr7j9mulyef0CFnyWg +4ON8z7bmMfX9JAPI9PvYpPOSKQWqVUOA9z4TEEM6iSS+2AGG0WlzRH34uUYra1HQ +ETFLBq6fRfoSFwzvavrQ9zZG6y7bTiBGATOssfdK5hg9UyLc6EoSeBEv5DuSvdcH +WsmBXUhYyA+b6aQPu4mxrTgHb5PQphJW+QdI0iMvo6mTsBEKJ0xICo1wQWh2et28 +VMNCM7B79q4f55VeEcrytEtcukdk8PPXh5V/kwahcsmBEqW3j51+0e8CAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQAtArx7iLhc4Qe4u7qy85gUj8uwIRO15W8FT5L6rMBTp7DNfrqHNoUl10HFKYQi +dK+/PjQ21SR6geIbVFKFb3be3GOYRfwsMfoipHI6jdRqLt4zEEHrlB3jWc2yvqvw +tiCGnLhG7sVkurZszFNEeoASd3znUWeRMi+InZOo79bN3g== +-----END CERTIFICATE----- diff --git a/examples/protocols/coap_client/main/coap_client.key b/examples/protocols/coap_client/main/coap_client.key new file mode 100644 index 00000000000..99936e25b7a --- /dev/null +++ b/examples/protocols/coap_client/main/coap_client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0va+cqWrLlYM3fI7LHzgXQVArwyM84IM0Bg0tON9X40KPqp5 +AvmWrRAA7FHp3D/76rBX60jHyu/oBavuP2a6XJ5/QIWfJaDg43zPtuYx9f0kA8j0 ++9ik85IpBapVQ4D3PhMQQzqJJL7YAYbRaXNEffi5RitrUdARMUsGrp9F+hIXDO9q ++tD3NkbrLttOIEYBM6yx90rmGD1TItzoShJ4ES/kO5K91wdayYFdSFjID5vppA+7 +ibGtOAdvk9CmElb5B0jSIy+jqZOwEQonTEgKjXBBaHZ63bxUw0IzsHv2rh/nlV4R +yvK0S1y6R2Tw89eHlX+TBqFyyYESpbePnX7R7wIDAQABAoIBAQC5PncO3tBIeMEF +pu007FZq9/DLhP7D2B9+HrMxX0y4uXUUf8aQyS74ukPFP0xV3U1M0BnzfU4KscyQ +Jl+nBoKAT6C3vF15wiGXQAJ4vPuD4Ate03fjKWH2ixJAakhCZR01QbIXBnBkdrvf +401BBjlPUDcIGZo8FbLzEMlGTo84vE9v3Qmkbi+PzPCh2YC+NDmsOcIW1zpmwyYC +ZYCpoWgl4++kqXXn0NGhuaOgB0JLsJOBpx/hOOjBU/wXCKaXZ1vchYqfbvvx2gf2 +WX4P0CiTH1z7MEAHanaZkcnNyxV/oF1EIMY5p0vDDzgrKtppvPOqspjydje03+CE +t0wKGPi5AoGBAPAG2Y4efgwLcoWdPjKZtsHLhDhLJnvxkqnNkzdPnLZojNi8pKkV +/Yu++pPemJZZa4YAp+OnqyEfhcha+HYqKMwRC8t3YrEVOlRQTfW/OoSrp059JIRV +jTvq/u7DdYGJRRgMLUJiEI+7xj1WbTc2EceJAgn0qfKvbvBtVJP0LH1TAoGBAOEA +xZB7SwyX+zDGRTugqMYg+sYobbQHJ7utLyoX+ckeG+sPEjEYLpQQfshET/gwF8ZK +4aILkACx/tna799xCjQdmyyc338NO9WULlY1xF+65WfeaxrtTAsqVikX3p19McRI +ijX8k7Msy3gYWJXev3MCtPT2+g68IgbL/W2wY+l1AoGAT7xGy0Jv5vpqid5pig+s +OYatHrJAT445hXUIQaiNy77Bg0JvhMgMWT8RKMwabl+4K2TOYP8TB0bcf2lQ/pgU +w22qOGYpf+AoZ1fh/hAPlYEcbCOAXQG6kDwJgjGmOGjsbgelhVbkX4smWLv8PgoV +L+7goYQIbNlAhlgbb6b+nIcCgYBB7Zr2Cdpkt0en9ACnRx0M6O7yDziNzqbqzAUM +3XeYYZUmnATlk8NaKTcs8S9JdrYQqTJR6/dm7MDTDt7IZvPpb19fhBvMu5DztPaa +1ihTMI01kStq+WsVvnL+mXrmRJ/HdsXgqcCReKep6eBTEbChP4LMYG3G0YNa4HzC +njO4XQKBgQDRnbqqg2CNTnS94BN2D3uzzELtwsIG6aVCtl09ZsLnGaBKVVDtP6BI +j2hGD7xw4g5JeSPIJU5J03nALTY3hz1JyI7AJCX7+JRtUTX2A8C4mlbeul7ilGaU +A7MFT8GqhjYYa84GzNcA1mK8ynlixpL8+yzTT/8lWInWRBa69SkktA== +-----END RSA PRIVATE KEY----- diff --git a/examples/protocols/coap_client/main/coap_client_example_main.c b/examples/protocols/coap_client/main/coap_client_example_main.c index 921541aef2e..067364a7368 100644 --- a/examples/protocols/coap_client/main/coap_client_example_main.c +++ b/examples/protocols/coap_client/main/coap_client_example_main.c @@ -7,6 +7,13 @@ CONDITIONS OF ANY KIND, either express or implied. */ +/* + * WARNING + * libcoap is not multi-thread safe, so only this thread must make any coap_*() + * calls. Any external (to this thread) data transmitted in/out via libcoap + * therefore has to be passed in/out by xQueue*() via this thread. + */ + #include #include #include @@ -23,12 +30,36 @@ #include "protocol_examples_common.h" +#if 1 +/* Needed until coap_dtls.h becomes a part of libcoap proper */ +#include "libcoap.h" +#include "coap_dtls.h" +#endif #include "coap.h" #define COAP_DEFAULT_TIME_SEC 5 -/* Set this to 9 to get verbose logging from within libcoap */ -#define COAP_LOGGING_LEVEL 0 +/* The examples use simple Pre-Shared-Key configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_COAP_PSK_KEY "some-agreed-preshared-key" + + Note: PSK will only be used if the URI is prefixed with coaps:// + instead of coap:// and the PSK must be one that the server supports + (potentially associated with the IDENTITY) +*/ +#define EXAMPLE_COAP_PSK_KEY CONFIG_COAP_PSK_KEY +#define EXAMPLE_COAP_PSK_IDENTITY CONFIG_COAP_PSK_IDENTITY + +/* The examples use uri Logging Level that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entry to a value + that is between 0 and 7 with + the config you want - ie #define EXAMPLE_COAP_LOG_DEFAULT_LEVEL 7 +*/ +#define EXAMPLE_COAP_LOG_DEFAULT_LEVEL CONFIG_COAP_LOG_DEFAULT_LEVEL /* The examples use uri "coap://californium.eclipse.org" that you can set via the project configuration (idf.py menuconfig) @@ -124,17 +155,44 @@ static void message_handler(coap_context_t *ctx, coap_session_t *session, resp_wait = 0; } -static void coap_example_task(void *p) +#ifdef CONFIG_MBEDTLS_COAP_PKI + +#ifdef __GNUC__ +#define UNUSED_PARAM __attribute__ ((unused)) +#else /* not a GCC */ +#define UNUSED_PARAM +#endif /* GCC */ + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +static int +verify_cn_callback(const char *cn, + const uint8_t *asn1_public_cert UNUSED_PARAM, + size_t asn1_length UNUSED_PARAM, + coap_session_t *session UNUSED_PARAM, + unsigned depth, + int validated UNUSED_PARAM, + void *arg UNUSED_PARAM +) { + coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n", + cn, depth ? "CA" : "Certificate"); + return 1; +} +#endif /* CONFIG_MBEDTLS_COAP_PKI */ + +static void coap_example_client(void *p) { struct hostent *hp; - struct ip4_addr *ip4_addr; - coap_address_t dst_addr, src_addr; + coap_address_t dst_addr; static coap_uri_t uri; const char* server_uri = COAP_DEFAULT_DEMO_URI; char* phostname = NULL; - coap_set_log_level(COAP_LOGGING_LEVEL); + coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL); + while (1) { #define BUFSIZE 40 unsigned char _buf[BUFSIZE]; @@ -153,7 +211,7 @@ static void coap_example_task(void *p) if ((uri.scheme==COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) || (uri.scheme==COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported())) { - ESP_LOGE(TAG, "CoAP server uri scheme error"); + ESP_LOGE(TAG, "CoAP server uri scheme is not supported"); break; } @@ -172,19 +230,31 @@ static void coap_example_task(void *p) ESP_LOGE(TAG, "DNS lookup failed"); vTaskDelay(1000 / portTICK_PERIOD_MS); free(phostname); - continue; + goto clean_up; + } + { + char tmpbuf[INET6_ADDRSTRLEN]; + coap_address_init(&dst_addr); + switch (hp->h_addrtype) { + case AF_INET: + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(uri.port); + memcpy(&dst_addr.addr.sin.sin_addr, hp->h_addr, sizeof(dst_addr.addr.sin.sin_addr)); + inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf)); + ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf); + break; + case AF_INET6: + dst_addr.addr.sin6.sin6_family = AF_INET6; + dst_addr.addr.sin6.sin6_port = htons(uri.port); + memcpy(&dst_addr.addr.sin6.sin6_addr, hp->h_addr, sizeof(dst_addr.addr.sin6.sin6_addr)); + inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf)); + ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf); + break; + default: + ESP_LOGE(TAG, "DNS lookup response failed"); + goto clean_up; + } } - - /* Code to print the resolved IP. - - Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ - ip4_addr = (struct ip4_addr *)hp->h_addr; - ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr)); - - coap_address_init(&src_addr); - src_addr.addr.sin.sin_family = AF_INET; - src_addr.addr.sin.sin_port = htons(0); - src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; if (uri.path.length) { buflen = BUFSIZE; @@ -222,15 +292,102 @@ static void coap_example_task(void *p) goto clean_up; } - coap_address_init(&dst_addr); - dst_addr.addr.sin.sin_family = AF_INET; - dst_addr.addr.sin.sin_port = htons(uri.port); - dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; - - session = coap_new_client_session(ctx, &src_addr, &dst_addr, - uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP : - uri.scheme==COAP_URI_SCHEME_COAPS_TCP ? COAP_PROTO_TLS : - uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_UDP); + /* + * Note that if the URI starts with just coap:// (not coaps://) the + * session will still be plain text. + * + * coaps+tcp:// is NOT supported by the libcoap->mbedtls interface + * so COAP_URI_SCHEME_COAPS_TCP will have failed in a test above, + * but the code is left in for completeness. + */ + if (uri.scheme==COAP_URI_SCHEME_COAPS || uri.scheme==COAP_URI_SCHEME_COAPS_TCP) { +#ifdef CONFIG_MBEDTLS_COAP_PSK + session = coap_new_client_session_psk(ctx, NULL, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS, + EXAMPLE_COAP_PSK_IDENTITY, + (const uint8_t*)EXAMPLE_COAP_PSK_KEY, + sizeof(EXAMPLE_COAP_PSK_KEY)-1); +#endif /* CONFIG_MBEDTLS_COAP_PSK */ + +#ifdef CONFIG_MBEDTLS_COAP_PKI +/* CA cert, taken from coap_ca.pem + Client cert, taken from coap_client.crt + Client key, taken from coap_client.key + + The PEM, CRT and KEY file are examples taken from the wpa2 enterprise + example. + + To embed it in the app binary, the PEM, CRT and KEY file is named + in the component.mk COMPONENT_EMBED_TXTFILES variable. +*/ +extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start"); +extern uint8_t ca_pem_end[] asm("_binary_coap_ca_pem_end"); +extern uint8_t client_crt_start[] asm("_binary_coap_client_crt_start"); +extern uint8_t client_crt_end[] asm("_binary_coap_client_crt_end"); +extern uint8_t client_key_start[] asm("_binary_coap_client_key_start"); +extern uint8_t client_key_end[] asm("_binary_coap_client_key_end"); + unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start; + unsigned int client_crt_bytes = client_crt_end - client_crt_start; + unsigned int client_key_bytes = client_key_end - client_key_start; + coap_dtls_pki_t dtls_pki; + static char client_sni[256]; + + memset (&dtls_pki, 0, sizeof(dtls_pki)); + dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION; + if (ca_pem_bytes) { + /* + * Add in additional certificate checking. + * This list of enabled can be tuned for the specific + * requirements - see 'man coap_encryption'. + * + * Note: A list of root ca file can be setup separately using + * coap_context_set_pki_root_cas(), but the below is used to + * define what checking actually takes place. + */ + dtls_pki.verify_peer_cert = 1; + dtls_pki.require_peer_cert = 1; + dtls_pki.allow_self_signed = 1; + dtls_pki.allow_expired_certs = 1; + dtls_pki.cert_chain_validation = 1; + dtls_pki.cert_chain_verify_depth = 2; + dtls_pki.check_cert_revocation = 1; + dtls_pki.allow_no_crl = 1; + dtls_pki.allow_expired_crl = 1; + dtls_pki.allow_bad_md_hash = 1; + dtls_pki.allow_short_rsa_length = 1; + dtls_pki.validate_cn_call_back = verify_cn_callback; + dtls_pki.cn_call_back_arg = NULL; + dtls_pki.validate_sni_call_back = NULL; + dtls_pki.sni_call_back_arg = NULL; + memset(client_sni, 0, sizeof(client_sni)); + if (uri.host.length) + memcpy(client_sni, uri.host.s, min(uri.host.length, sizeof(client_sni))); + else + memcpy(client_sni, "localhost", 9); + dtls_pki.client_sni = client_sni; + } + dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF; + dtls_pki.pki_key.key.pem_buf.public_cert = client_crt_start; + dtls_pki.pki_key.key.pem_buf.public_cert_len = client_crt_bytes; + dtls_pki.pki_key.key.pem_buf.private_key = client_key_start; + dtls_pki.pki_key.key.pem_buf.private_key_len = client_key_bytes; + dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start; + dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes; + + session = coap_new_client_session_pki(ctx, NULL, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS, + &dtls_pki); +#endif /* CONFIG_MBEDTLS_COAP_PKI */ + +#ifdef CONFIG_MBEDTLS_COAP_NONE + session = coap_new_client_session(ctx, NULL, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS); +#endif /* CONFIG_MBEDTLS_COAP_NONE */ + } else { + session = coap_new_client_session(ctx, NULL, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP : + COAP_PROTO_UDP); + } if (!session) { ESP_LOGE(TAG, "coap_new_client_session() failed"); goto clean_up; @@ -272,7 +429,10 @@ static void coap_example_task(void *p) if (session) coap_session_release(session); if (ctx) coap_free_context(ctx); coap_cleanup(); - /* Only send the request off once */ + /* + * change the following line to something like sleep(2) + * if you want the request to continually be sent + */ break; } @@ -285,11 +445,19 @@ void app_main(void) tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_create_default()); +#if 0 +/* See https://github.com/Ebiroll/qemu_esp32 for further information */ +#include "emul_ip.h" + if (is_running_qemu()) { + xTaskCreate(task_lwip_init, "task_lwip_init", 2*4096, NULL, 20, NULL); + } + else +#endif /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - xTaskCreate(coap_example_task, "coap", 5 * 1024, NULL, 5, NULL); + xTaskCreate(coap_example_client, "coap", 8 * 1024, NULL, 5, NULL); } diff --git a/examples/protocols/coap_client/main/component.mk b/examples/protocols/coap_client/main/component.mk index 0b9d7585e76..50306ea2f91 100644 --- a/examples/protocols/coap_client/main/component.mk +++ b/examples/protocols/coap_client/main/component.mk @@ -3,3 +3,8 @@ # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := coap_ca.pem +COMPONENT_EMBED_TXTFILES += coap_client.crt +COMPONENT_EMBED_TXTFILES += coap_client.key diff --git a/examples/protocols/coap_server/README.md b/examples/protocols/coap_server/README.md index 44366936d06..77362cf0ef5 100644 --- a/examples/protocols/coap_server/README.md +++ b/examples/protocols/coap_server/README.md @@ -2,14 +2,26 @@ # CoAP server example (See the README.md file in the upper level 'examples' directory for more information about examples.) -This CoAP server example is adaptation of one of the [libcoap](https://github.com/obgm/libcoap) example. +This CoAP server example is very simplified adaptation of one of the +[libcoap](https://github.com/obgm/libcoap) examples. -CoAP server example would startup a daemon task, receive data from CoAP client and transmit data to CoAP client. +CoAP server example will startup a daemon task, receive requests / data from CoAP client and transmit +data to CoAP client. -The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things. -The protocol is designed for machine-to-machine (M2M) applications such as smart energy and building automation. +If the incoming request requests the use of DTLS (connecting to port 5684), then the CoAP server will +try to establish a DTLS session using the previously defined Pre-Shared Key (PSK) - which +must be the same as the one that the CoAP client is using, or Public Key Infrastructure (PKI) where +the PKI information must match as requested. -please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. +NOTE: Client sessions trying to use coaps+tcp:// are not currently supported, even though both +libcoap and MbedTLS support it. + +The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with +constrained nodes and constrained networks in the Internet of Things. +The protocol is designed for machine-to-machine (M2M) applications such as smart energy and +building automation. + +Please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. ## How to use example @@ -19,15 +31,29 @@ please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) make menuconfig ``` -* Set default serial port under Serial Flasher config -* Set WiFi SSID under Example Configuration -* Set WiFi Password under Example Configuration +Example Connection Configuration ---> + * Set WiFi SSID under Example Configuration + * Set WiFi Password under Example Configuration +Example CoAP Client Configuration ---> + * Set CoAP Target Uri + * Set encryption method definitions (None, PSK or PKI) + * If PSK Set CoAP Preshared Key to use in connection to the server + * If PSK Set CoAP PSK Client identity (username) + Enable CoAP debugging if required +Component config ---> + mbedTLS ---> + [*] Enable mbedtls certificate expiry check + TLS Key Exchange Methods ---> + [*] Enable pre-shared-key ciphersuites + [*] Enable PSK based ciphersuite modes + [*] Support DTLS protocol (all versions) ### Build and Flash Build the project and flash it to the board, then run monitor tool to view serial output: ``` +make make -j4 flash monitor ``` @@ -54,8 +80,8 @@ I (2622) CoAP_server: Connected to AP ... ``` -if a CoAP client query `/Espressif` resource, CoAP server would return `"no data"` -until a CoAP client does a PUT with some data. +If a CoAP client queries the `/Espressif` resource, CoAP server will return `"Hello World!"` +until a CoAP client does a PUT with different data. ## libcoap Documentation This can be found at https://libcoap.net/doc/reference/4.2.0/ @@ -64,5 +90,7 @@ This can be found at https://libcoap.net/doc/reference/4.2.0/ * Please make sure CoAP client fetchs or puts data under path: `/Espressif` or fetches `/.well-known/core` -* libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0` -to `#define COAP_LOGGING_LEVEL 9` +* CoAP logging can be enabled by running 'make menuconfig' and enable debugging + +* Encryption (MbedTLS) can be enabled by running 'make menuconfig' and enable debugging + diff --git a/examples/protocols/coap_server/main/CMakeLists.txt b/examples/protocols/coap_server/main/CMakeLists.txt index c28a7be75ee..d4738f69187 100644 --- a/examples/protocols/coap_server/main/CMakeLists.txt +++ b/examples/protocols/coap_server/main/CMakeLists.txt @@ -1,2 +1,3 @@ idf_component_register(SRCS "coap_server_example_main.c" - INCLUDE_DIRS ".") \ No newline at end of file + INCLUDE_DIRS "." + EMBED_TXTFILES coap_ca.pem coap_server.crt coap_server.key) diff --git a/examples/protocols/coap_server/main/Kconfig.projbuild b/examples/protocols/coap_server/main/Kconfig.projbuild new file mode 100644 index 00000000000..bdf3b123052 --- /dev/null +++ b/examples/protocols/coap_server/main/Kconfig.projbuild @@ -0,0 +1,74 @@ +menu "Example CoAP Server Configuration" + + config MBEDTLS_COAP_PSK + bool "Pre-Shared Keys (PSK)" + default n + help + Use Pre-Shared Keys to encrypt the communications between the + CoAP Server and CoAP Client. Both ends need the same + Pre-Shared Key. + + config COAP_PSK_KEY + string "Preshared Key (PSK) to used in the connection from the CoAP client" + depends on MBEDTLS_COAP_PSK + default "secret-key" + help + The Preshared Key to use to encrypt the communicatons. The same key must be + used at both ends of the CoAP connection, and the CoaP client must request + an URI prefixed with coaps:// instead of coap:// for DTLS to be used. + + config MBEDTLS_COAP_PKI + bool "Public Key Infrastructure (PKI)" + default n + help + Use PKI Certificates and Private Keys to encrypt the communications + between the CoAP Server and CoAP Client. + + config MBEDTLS_COAP_DEBUG + bool "Enable CoAP debugging" + default n + help + Enable CoAP debugging functions at compile time for the example code. + + If this option is enabled, call coap_set_log_level() + at runtime in order to enable CoAP debug output via the ESP + log mechanism. + + choice MBEDTLS_COAP_DEBUG_LEVEL + bool "Set CoAP debugging level" + depends on MBEDTLS_COAP_DEBUG + default COAP_LOG_WARNING + help + Set CoAP debugging level + + config COAP_LOG_EMERG + bool "Emergency" + config COAP_LOG_ALERT + bool "Alert" + config COAP_LOG_CRIT + bool "Critical" + config COAP_LOG_ERROR + bool "Error" + config COAP_LOG_WARNING + bool "Warning" + config COAP_LOG_NOTICE + bool "Notice" + config COAP_LOG_INFO + bool "Info" + config COAP_LOG_DEBUG + bool "Debug" + endchoice + + config COAP_LOG_DEFAULT_LEVEL + int + default 0 if !MBEDTLS_COAP_DEBUG + default 0 if COAP_LOG_EMERG + default 1 if COAP_LOG_ALERT + default 2 if COAP_LOG_CRIT + default 3 if COAP_LOG_ERROR + default 4 if COAP_LOG_WARNING + default 5 if COAP_LOG_NOTICE + default 6 if COAP_LOG_INFO + default 7 if COAP_LOG_DEBUG + +endmenu diff --git a/examples/protocols/coap_server/main/coap_ca.pem b/examples/protocols/coap_server/main/coap_ca.pem new file mode 100644 index 00000000000..1bdf23d94bb --- /dev/null +++ b/examples/protocols/coap_server/main/coap_ca.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD +VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT +BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs +ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w +DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh +bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG +A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66 +1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe +vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p +AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG +A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG +A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl +LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ +5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93 +d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA +euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW +S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt +PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc= +-----END CERTIFICATE----- diff --git a/examples/protocols/coap_server/main/coap_server.crt b/examples/protocols/coap_server/main/coap_server.crt new file mode 100644 index 00000000000..8f1dfcda6cc --- /dev/null +++ b/examples/protocols/coap_server/main/coap_server.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 47 (0x2f) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Jun 7 08:06:49 2017 GMT + Not After : Jun 5 08:06:49 2027 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c9:d8:e2:e0:75:91:83:87:d8:c8:80:c6:20:4d: + e9:14:24:30:98:33:53:fa:56:0e:ec:9a:43:7f:87: + a9:22:94:26:06:c7:ac:b5:d9:ec:55:06:81:b7:0d: + c9:24:51:49:fa:47:fb:4b:4e:fc:ed:75:8a:e1:28: + 32:bc:c5:e0:4c:45:c4:58:60:15:67:1e:6b:40:19: + 3f:f0:ab:92:61:92:2d:71:10:2e:f2:eb:bc:81:2f: + 5a:3b:74:ca:5f:fd:e0:ee:d1:d9:07:6a:6c:20:c0: + 07:88:b4:8b:0f:ad:1e:c9:4f:7c:11:98:37:89:15: + de:24:b1:11:1a:7c:97:4a:cf:f3:c8:cb:79:9e:9c: + c3:71:da:a6:94:97:f5:95:fd:61:06:44:e2:3f:12: + 43:0b:1d:33:48:91:d2:ce:4f:97:a1:ed:6a:30:c7: + 5d:98:b5:6e:0a:b7:4f:d9:03:ec:80:76:09:b0:40: + a1:a1:af:ab:2a:59:c4:0f:56:22:bc:be:14:be:18: + df:10:7d:5d:22:bf:e5:04:77:7a:75:6b:3e:eb:6d: + 20:a1:a7:60:d4:f1:87:9d:9f:60:b9:d3:db:2c:25: + f4:91:4a:f1:d2:40:e5:a1:10:88:a0:41:5a:98:40: + ca:15:d7:e3:e6:3e:c0:6a:d5:46:b2:b4:90:b4:ae: + 3b:e3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + a4:25:21:51:0b:22:6c:63:8d:a9:c1:4f:04:33:69:79:34:f0: + 36:dd:8f:6a:27:5f:07:a2:1d:ef:8b:f0:96:e6:e7:a3:b8:3b: + 85:5e:3f:26:43:8a:8e:95:58:9c:a6:db:9c:51:bf:ea:53:16: + 3e:c1:a8:11:1a:c6:cf:0e:a1:17:18:64:d2:05:f1:c0:9c:a6: + 2b:16:c4:29:54:03:d2:17:bd:15:74:d6:ad:8a:8f:2d:cc:27: + 3b:88:88:f2:ea:d0:a2:cb:e9:42:57:df:26:9f:8a:a2:02:2f: + 35:b6:19:1d:26:43:44:af:12:4b:bc:b9:84:50:02:fd:1d:fa: + 50:e8 +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw +ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG +IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy +vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL +D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS +zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6 +dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ +tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4 +O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0 +1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo +-----END CERTIFICATE----- diff --git a/examples/protocols/coap_server/main/coap_server.key b/examples/protocols/coap_server/main/coap_server.key new file mode 100644 index 00000000000..9b3433273ea --- /dev/null +++ b/examples/protocols/coap_server/main/coap_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAydji4HWRg4fYyIDGIE3pFCQwmDNT+lYO7JpDf4epIpQmBses +tdnsVQaBtw3JJFFJ+kf7S0787XWK4SgyvMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu +8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSLD60eyU98EZg3iRXeJLERGnyXSs/zyMt5 +npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHSzk+Xoe1qMMddmLVuCrdP2QPsgHYJsECh +oa+rKlnED1YivL4UvhjfEH1dIr/lBHd6dWs+620goadg1PGHnZ9gudPbLCX0kUrx +0kDloRCIoEFamEDKFdfj5j7AatVGsrSQtK474wIDAQABAoIBAQC2kGDEPBJdMSW2 +VCLfXRiPixwYzXQLXIMrJWwfkQg9qlmqkDd6U50aWkRA2UswegW7RhfYSZ0i+cmf +VMhvTVpOIlwwwtcY6b5/v1bBy60eaySGuuh79xQMlFO8qynQIMStvUfbGTqrdIRb +9VBB4YeS9T12fILejtTZwv2BQ2dj1Y1SCay6Ri85UzJqSClRKgHISybvVdLNjPvP +0TRFBr57zyjL6WE8teKiKchzQko2u86No5uBCdKGsrAkrsdcR0YqlM/pZxd3VKNm ++eny0k+dZZlvcPxzkzP4hEp9+Rw5rP9/s3s/cCwvuuC5JO32ATBWKCbTvPv/XPDb +MdSJtOshAoGBAPzk0eswkcbFYtpnpBNmBAr1dtAdW1lfjUI2ucMMwt7Wns0P/tt+ +gq6Hi1wTaGP0l/dIECgeHwjtWj31ZJjQtFJ1y/kafxo4o9cA8vCydpdvSZaldAfg +sbLlDTDYzEpelaDIbNQBBXFoC5U9JlBhBsIFCL5Z8ZuIeFPsb7t5wwuHAoGBAMxT +jyWfNm1uNxp1xgCnrRsLPQPVnURrSFAqcHrECqRu3F7sozTN7q/cZViemxPvVDGQ +p9c+9bHwaYvW4trO5qDHJ++gGwm5L52bMAY1VUfeTt67fqrey43XpdmzcTX1V9Uj +QWawPUCSDzFjL1MjfCIejtyYf5ash53vj+T8r/vFAoGAA/OPVB1uKazr3n3AEo2F +gqZTNO1AgCT+EArK3EFWyiSQVqPpV4SihheYFdg3yVgJB9QYbIgL9BfBUTaEW97m +8mLkzP+c/Mvlw3ZAVYJ0V+llPPVY2saoACOUES9SAdd4fwqiqK1baGo3xB0wfBEI +CgAKIu9E1ylKuAT5ufQtGAECgYEAtP/kU5h5N3El4QupTdU7VDSdZTMqsHw0v8cI +gsf9AXKvRmtrnBA8u46KPHmruHoO5CVXeSZtsaXdaaH+rYQQ6yXg67WxnehtFLlv +TmCaXiLBTS9cYvMf8FOyuGnsBLeEietEOTov2G5KhR5uwsAxa2wUc7endor5S9/2 +YQuyvV0CgYALbiFpILd5l1ip65eE6JdA3hfttUbV2j2NSW12ej69vqbeOfaSgNse +uYCcXFsBbQPhNPwA+4d1oCe8SyXZg1f7gE812z2Tyr/3vdVnNZlitoxhsHmGiyS7 +gZdaTYCb78l9z0EBdaCVvA16owEle4SR6f9eCwzSI0WPOUra+x/hrA== +-----END RSA PRIVATE KEY----- diff --git a/examples/protocols/coap_server/main/coap_server_example_main.c b/examples/protocols/coap_server/main/coap_server_example_main.c index eb5e131baaf..b2f78bb0327 100644 --- a/examples/protocols/coap_server/main/coap_server_example_main.c +++ b/examples/protocols/coap_server/main/coap_server_example_main.c @@ -7,6 +7,13 @@ CONDITIONS OF ANY KIND, either express or implied. */ +/* + * WARNING + * libcoap is not multi-thread safe, so only this thread must make any coap_*() + * calls. Any external (to this thread) data transmitted in/out via libcoap + * therefore has to be passed in/out by xQueue*() via this thread. + */ + #include #include @@ -22,14 +29,39 @@ #include "protocol_examples_common.h" +#if 1 +/* Needed until coap_dtls.h becomes a part of libcoap proper */ +#include "libcoap.h" +#include "coap_dtls.h" +#endif #include "coap.h" -/* Set this to 9 to get verbose logging from within libcoap */ -#define COAP_LOGGING_LEVEL 0 +/* The examples use simple Pre-Shared-Key configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_COAP_PSK_KEY "some-agreed-preshared-key" + + Note: PSK will only be used if the URI is prefixed with coaps:// + instead of coap:// and the PSK must be one that the server supports + (potentially associated with the IDENTITY) +*/ +#define EXAMPLE_COAP_PSK_KEY CONFIG_COAP_PSK_KEY + +/* The examples use CoAP Logging Level that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entry to a value + that is between 0 and 7 with + the config you want - ie #define EXAMPLE_COAP_LOG_DEFAULT_LEVEL 7 +*/ +#define EXAMPLE_COAP_LOG_DEFAULT_LEVEL CONFIG_COAP_LOG_DEFAULT_LEVEL static char espressif_data[100]; static int espressif_data_len = 0; +#define INITIAL_DATA "Hello World!" + /* * The resource handler */ @@ -59,7 +91,7 @@ hnd_espressif_put(coap_context_t *ctx, coap_resource_notify_observers(resource, NULL); - if (strcmp (espressif_data, "no data") == 0) { + if (strcmp (espressif_data, INITIAL_DATA) == 0) { response->code = COAP_RESPONSE_CODE(201); } else { @@ -70,7 +102,7 @@ hnd_espressif_put(coap_context_t *ctx, (void)coap_get_data(request, &size, &data); if (size == 0) { /* re-init */ - snprintf(espressif_data, sizeof(espressif_data), "no data"); + snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA); espressif_data_len = strlen(espressif_data); } else { espressif_data_len = size > sizeof (espressif_data) ? sizeof (espressif_data) : size; @@ -88,23 +120,50 @@ hnd_espressif_delete(coap_context_t *ctx, coap_pdu_t *response) { coap_resource_notify_observers(resource, NULL); - snprintf(espressif_data, sizeof(espressif_data), "no data"); + snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA); espressif_data_len = strlen(espressif_data); response->code = COAP_RESPONSE_CODE(202); } -static void coap_example_thread(void *p) +#ifdef CONFIG_MBEDTLS_COAP_PKI + +#ifdef __GNUC__ +#define UNUSED_PARAM __attribute__ ((unused)) +#else /* not a GCC */ +#define UNUSED_PARAM +#endif /* GCC */ + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +static int +verify_cn_callback(const char *cn, + const uint8_t *asn1_public_cert UNUSED_PARAM, + size_t asn1_length UNUSED_PARAM, + coap_session_t *session UNUSED_PARAM, + unsigned depth, + int validated UNUSED_PARAM, + void *arg UNUSED_PARAM +) { + coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n", + cn, depth ? "CA" : "Certificate"); + return 1; +} +#endif /* CONFIG_MBEDTLS_COAP_PKI */ + +static void coap_example_server(void *p) { coap_context_t *ctx = NULL; - coap_address_t serv_addr; + coap_address_t serv_addr; coap_resource_t *resource = NULL; - snprintf(espressif_data, sizeof(espressif_data), "no data"); + snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA); espressif_data_len = strlen(espressif_data); - coap_set_log_level(COAP_LOGGING_LEVEL); + coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL); + while (1) { - coap_endpoint_t *ep_udp = NULL; - coap_endpoint_t *ep_tcp = NULL; + coap_endpoint_t *ep = NULL; unsigned wait_ms; /* Prepare the CoAP server socket */ @@ -117,14 +176,91 @@ static void coap_example_thread(void *p) if (!ctx) { continue; } - ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); - if (!ep_udp) { +#ifdef CONFIG_MBEDTLS_COAP_PSK + /* Need PSK setup before we set up endpoints */ + coap_context_set_psk(ctx, "CoAP", + (const uint8_t*)EXAMPLE_COAP_PSK_KEY, + sizeof(EXAMPLE_COAP_PSK_KEY)-1); +#endif /* CONFIG_MBEDTLS_COAP_PSK */ + +#ifdef CONFIG_MBEDTLS_COAP_PKI +/* CA cert, taken from coap_ca.pem + Server cert, taken from coap_server.crt + Server key, taken from coap_server.key + + The PEM, CRT and KEY file are examples taken from the wpa2 enterprise + example. + + To embed it in the app binary, the PEM, CRT and KEY file is named + in the component.mk COMPONENT_EMBED_TXTFILES variable. +*/ +extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start"); +extern uint8_t ca_pem_end[] asm("_binary_coap_ca_pem_end"); +extern uint8_t server_crt_start[] asm("_binary_coap_server_crt_start"); +extern uint8_t server_crt_end[] asm("_binary_coap_server_crt_end"); +extern uint8_t server_key_start[] asm("_binary_coap_server_key_start"); +extern uint8_t server_key_end[] asm("_binary_coap_server_key_end"); + unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start; + unsigned int server_crt_bytes = server_crt_end - server_crt_start; + unsigned int server_key_bytes = server_key_end - server_key_start; + coap_dtls_pki_t dtls_pki; + + memset (&dtls_pki, 0, sizeof(dtls_pki)); + dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION; + if (ca_pem_bytes) { + /* + * Add in additional certificate checking. + * This list of enabled can be tuned for the specific + * requirements - see 'man coap_encryption'. + * + * Note: A list of root ca file can be setup separately using + * coap_context_set_pki_root_cas(), but the below is used to + * define what checking actually takes place. + */ + dtls_pki.verify_peer_cert = 1; + dtls_pki.require_peer_cert = 1; + dtls_pki.allow_self_signed = 1; + dtls_pki.allow_expired_certs = 1; + dtls_pki.cert_chain_validation = 1; + dtls_pki.cert_chain_verify_depth = 2; + dtls_pki.check_cert_revocation = 1; + dtls_pki.allow_no_crl = 1; + dtls_pki.allow_expired_crl = 1; + dtls_pki.allow_bad_md_hash = 1; + dtls_pki.allow_short_rsa_length = 1; + dtls_pki.validate_cn_call_back = verify_cn_callback; + dtls_pki.cn_call_back_arg = NULL; + dtls_pki.validate_sni_call_back = NULL; + dtls_pki.sni_call_back_arg = NULL; + } + dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF; + dtls_pki.pki_key.key.pem_buf.public_cert = server_crt_start; + dtls_pki.pki_key.key.pem_buf.public_cert_len = server_crt_bytes; + dtls_pki.pki_key.key.pem_buf.private_key = server_key_start; + dtls_pki.pki_key.key.pem_buf.private_key_len = server_key_bytes; + dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start; + dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes; + + coap_context_set_pki(ctx, &dtls_pki); +#endif /* CONFIG_MBEDTLS_COAP_PKI */ + + ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); + if (!ep) { goto clean_up; } - ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); - if (!ep_tcp) { + ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); + if (!ep) { goto clean_up; } +#if defined(CONFIG_MBEDTLS_COAP_PSK) || defined(CONFIG_MBEDTLS_COAP_PKI) + if (coap_dtls_is_supported()) { + serv_addr.addr.sin.sin_port = htons(COAPS_DEFAULT_PORT); + ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_DTLS); + if (!ep) { + goto clean_up; + } + } +#endif /* CONFIG_MBEDTLS_COAP_PSK CONFIG_MBEDTLS_COAP_PKI */ resource = coap_resource_init(coap_make_str_const("Espressif"), 0); if (!resource) { goto clean_up; @@ -165,11 +301,19 @@ void app_main(void) tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_create_default()); +#if 0 +/* See https://github.com/Ebiroll/qemu_esp32 for further information */ +#include "emul_ip.h" + if (is_running_qemu()) { + xTaskCreate(task_lwip_init, "task_lwip_init", 2*4096, NULL, 20, NULL); + } + else +#endif /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - xTaskCreate(coap_example_thread, "coap", 1024 * 5, NULL, 5, NULL); + xTaskCreate(coap_example_server, "coap", 8 * 1024, NULL, 5, NULL); } diff --git a/examples/protocols/coap_server/main/component.mk b/examples/protocols/coap_server/main/component.mk index 0b9d7585e76..bccdac23fd6 100644 --- a/examples/protocols/coap_server/main/component.mk +++ b/examples/protocols/coap_server/main/component.mk @@ -3,3 +3,8 @@ # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := coap_ca.pem +COMPONENT_EMBED_TXTFILES += coap_server.crt +COMPONENT_EMBED_TXTFILES += coap_server.key