"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"
"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)
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.
#
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
-Subproject commit cfec0d072c5b99ed3e54828ca50ea2f6b91e1f50
+Subproject commit 98954eb30a2e728e172a6cd29430ae5bc999b585
--- /dev/null
+/* debug.c -- debug utilities
+ *
+ * Copyright (C) 2010--2012,2014--2019 Olaf Bergmann <bergmann@tzi.org> 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 <assert.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#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;
+}
+++ /dev/null
-/* coap_io.c -- Default network I/O functions for libcoap
- *
- * Copyright (C) 2012,2014,2016-2019 Olaf Bergmann <bergmann@tzi.org> 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 <stdio.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-# include <sys/select.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-# define OPTVAL_T(t) (t)
-# define OPTVAL_GT(t) (t)
-#endif
-#ifdef HAVE_SYS_IOCTL_H
- #include <sys/ioctl.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#ifdef HAVE_WS2TCPIP_H
-#include <ws2tcpip.h>
-# 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 <sys/uio.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <errno.h>
-
-#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(void) {
- 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 <mswsock.h>
-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
--- /dev/null
+/*
+* coap_mbedtls.c -- mbedTLS Datagram Transport Layer Support for libcoap
+*
+* Copyright (C) 2019 Jon Shallow <supjps-libcoap@jpshallow.com>
+* 2019 Jitin George <jitin@espressif.com>
+*
+* 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 <mbedtls/version.h>
+#include <mbedtls/platform.h>
+#include <mbedtls/net_sockets.h>
+#include <mbedtls/ssl.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/error.h>
+#include <mbedtls/certs.h>
+#include <mbedtls/timing.h>
+#include <mbedtls/ssl_cookie.h>
+#include <mbedtls/debug.h>
+#if defined(ESPIDF_VERSION) && defined(CONFIG_MBEDTLS_DEBUG)
+#include <mbedtls/esp_debug.h>
+#endif /* ESPIDF_VERSION && CONFIG_MBEDTLS_DEBUG */
+#include <errno.h>
+#include <arpa/inet.h>
+
+#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 */
--- /dev/null
+/*
+ * coap_dtls.h -- (Datagram) Transport Layer Support for libcoap
+ *
+ * Copyright (C) 2016 Olaf Bergmann <bergmann@tzi.org>
+ * Copyright (C) 2017 Jean-Claude Michelou <jcm@spinetix.com>
+ *
+ * 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 */
#ifdef WITH_POSIX
#include <sys/socket.h>
+#include <net/if.h>
#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_ */
"${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"
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})
--- /dev/null
+/*
+ * 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 <sys/time.h>
+#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 */
# 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
idf.py 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:
```
+idf.py build
idf.py -p PORT flash monitor
```
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
+# 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)
-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
--- /dev/null
+-----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-----
--- /dev/null
+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-----
--- /dev/null
+-----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-----
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 <string.h>
#include <sys/socket.h>
#include <netdb.h>
#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)
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];
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;
}
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;
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;
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;
}
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);
}
#
# (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
# 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
idf.py 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:
```
+idf.py build
idf.py -p PORT flash monitor
```
...
```
-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/
* 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
+
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)
--- /dev/null
+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
--- /dev/null
+-----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-----
--- /dev/null
+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-----
--- /dev/null
+-----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-----
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 <string.h>
#include <sys/socket.h>
#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
*/
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 {
(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;
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 */
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;
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);
}
#
# (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