]> granicus.if.org Git - esp-idf/commitdiff
Add DTLS support to libcoap using MbedTLS
authorJon Shallow <supjps-libcoap@jpshallow.com>
Sat, 13 Apr 2019 11:15:28 +0000 (12:15 +0100)
committerMahavir Jain <mahavir@espressif.com>
Tue, 6 Aug 2019 05:07:40 +0000 (10:37 +0530)
This update supports DTLS, TLS is a future TODO

components/coap/CMakeLists.txt:
components/coap/component.mk:

Add in the new files that have to be built
Replace libcoap/src/coap_notls.c with libcoap/src/coap_mbedtls.c

components/coap/libcoap:

Update the version to include the current version for supporting MbedTLS

components/coap/port/coap_debug.c:
components/coap/port/coap_mbedtls.c:
components/coap/port/include/coap/coap_dtls.h:

New port files for DTLS

components/coap/port/include/coap_config_posix.h:

Include building with MbedTLS

examples/protocols/coap_client/README.md:
examples/protocols/coap_client/main/CMakeLists.txt:
examples/protocols/coap_client/main/Kconfig.projbuild:
examples/protocols/coap_client/main/coap_client_example_main.c:
examples/protocols/coap_client/main/component.mk:

Update CoAP client to support DTLS

examples/protocols/coap_client/main/coap_ca.pem
examples/protocols/coap_client/main/coap_client.crt
examples/protocols/coap_client/main/coap_client.key

New PKI Certs for CoAP client (copied from wpa2_enterprise example)

examples/protocols/coap_server/README.md:
examples/protocols/coap_server/main/CMakeLists.txt:
examples/protocols/coap_server/main/Kconfig.projbuild:
examples/protocols/coap_server/main/coap_server_example_main.c:
examples/protocols/coap_server/main/component.mk:

Update CoAP server to support DTLS
Change "no data" to "Hello World!" to prevent confusion

examples/protocols/coap_server/main/coap_ca.pem
examples/protocols/coap_server/main/coap_server.crt
examples/protocols/coap_server/main/coap_server.key

New PKI Certs for CoAP server (copied from wpa2_enterprise example)

Closes https://github.com/espressif/esp-idf/pull/3345
Closes https://github.com/espressif/esp-idf/issues/1379

26 files changed:
components/coap/CMakeLists.txt
components/coap/component.mk
components/coap/libcoap
components/coap/port/coap_debug.c [new file with mode: 0644]
components/coap/port/coap_io.c [deleted file]
components/coap/port/coap_mbedtls.c [new file with mode: 0644]
components/coap/port/include/coap/coap_dtls.h [new file with mode: 0644]
components/coap/port/include/coap_config_posix.h
components/mbedtls/CMakeLists.txt
components/mbedtls/port/esp_timing.c [new file with mode: 0644]
examples/protocols/coap_client/README.md
examples/protocols/coap_client/main/CMakeLists.txt
examples/protocols/coap_client/main/Kconfig.projbuild
examples/protocols/coap_client/main/coap_ca.pem [new file with mode: 0644]
examples/protocols/coap_client/main/coap_client.crt [new file with mode: 0644]
examples/protocols/coap_client/main/coap_client.key [new file with mode: 0644]
examples/protocols/coap_client/main/coap_client_example_main.c
examples/protocols/coap_client/main/component.mk
examples/protocols/coap_server/README.md
examples/protocols/coap_server/main/CMakeLists.txt
examples/protocols/coap_server/main/Kconfig.projbuild [new file with mode: 0644]
examples/protocols/coap_server/main/coap_ca.pem [new file with mode: 0644]
examples/protocols/coap_server/main/coap_server.crt [new file with mode: 0644]
examples/protocols/coap_server/main/coap_server.key [new file with mode: 0644]
examples/protocols/coap_server/main/coap_server_example_main.c
examples/protocols/coap_server/main/component.mk

index aef9d3168260935b4b40148ad9d1e83b9c65b379..6d7dbb451745abdab5019b6fe9adc4f1d483ef7a 100644 (file)
@@ -8,7 +8,7 @@ set(srcs
     "libcoap/src/coap_hashkey.c"
     "libcoap/src/coap_session.c"
     "libcoap/src/coap_time.c"
-    "libcoap/src/coap_debug.c"
+    "port/coap_debug.c"
     "libcoap/src/encode.c"
     "libcoap/src/mem.c"
     "libcoap/src/net.c"
@@ -18,8 +18,8 @@ set(srcs
     "libcoap/src/str.c"
     "libcoap/src/subscribe.c"
     "libcoap/src/uri.c"
-    "libcoap/src/coap_notls.c"
-    "port/coap_io.c")
+    "libcoap/src/coap_io.c"
+    "port/coap_mbedtls.c")
 
 set(COMPONENT_REQUIRES lwip)
 
@@ -28,7 +28,7 @@ idf_component_register(SRCS "${srcs}"
                     REQUIRES lwip)
 
 # Silence format truncation warning, until it is fixed upstream
-set_source_files_properties(libcoap/src/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation)
+set_source_files_properties(port/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation)
 
 # Needed for coap headers in public builds, also.
 #
index 2eb07afd2c95d22e029d9361aab8f96260d951aa..46f703987062a72ba9897ec5159fb862b95b75de 100644 (file)
@@ -4,11 +4,11 @@
 
 COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap2
 
-COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o libcoap/src/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o libcoap/src/coap_notls.o port/coap_io.o
+COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o port/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_mbedtls.o libcoap/src/coap_io.o
 
 COMPONENT_SRCDIRS := libcoap/src libcoap port
 
 COMPONENT_SUBMODULES += libcoap
 
 # Silence format truncation warning, until it is fixed upstream
-libcoap/src/coap_debug.o: CFLAGS += -Wno-format-truncation
+port/coap_debug.o: CFLAGS += -Wno-format-truncation
index cfec0d072c5b99ed3e54828ca50ea2f6b91e1f50..98954eb30a2e728e172a6cd29430ae5bc999b585 160000 (submodule)
@@ -1 +1 @@
-Subproject commit cfec0d072c5b99ed3e54828ca50ea2f6b91e1f50
+Subproject commit 98954eb30a2e728e172a6cd29430ae5bc999b585
diff --git a/components/coap/port/coap_debug.c b/components/coap/port/coap_debug.c
new file mode 100644 (file)
index 0000000..64d6a01
--- /dev/null
@@ -0,0 +1,888 @@
+/* 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;
+}
diff --git a/components/coap/port/coap_io.c b/components/coap/port/coap_io.c
deleted file mode 100644 (file)
index 00c0ad1..0000000
+++ /dev/null
@@ -1,1417 +0,0 @@
-/* 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
diff --git a/components/coap/port/coap_mbedtls.c b/components/coap/port/coap_mbedtls.c
new file mode 100644 (file)
index 0000000..9f8c1fa
--- /dev/null
@@ -0,0 +1,1796 @@
+/*
+* 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 */
diff --git a/components/coap/port/include/coap/coap_dtls.h b/components/coap/port/include/coap/coap_dtls.h
new file mode 100644 (file)
index 0000000..2dd0e88
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * 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 */
index 8f5a5cfb10117ce2ddd57c5874a159720184f3ab..dc1166090d3e2b984fa13df4dcfbd35d1e9af372 100644 (file)
 #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_ */
index 5cfac4d9ba1a41bc3d9390ba5012e2b47c9eb897..b37269f02b8bf1be0a55e4577083b68ef8ed3634 100644 (file)
@@ -25,6 +25,7 @@ target_sources(mbedtls PRIVATE "${COMPONENT_DIR}/port/esp_bignum.c"
                                 "${COMPONENT_DIR}/port/esp_sha1.c"
                                 "${COMPONENT_DIR}/port/esp_sha256.c"
                                 "${COMPONENT_DIR}/port/esp_sha512.c"
+                                "${COMPONENT_DIR}/port/esp_timing.c"
                                 "${COMPONENT_DIR}/port/mbedtls_debug.c"
                                 "${COMPONENT_DIR}/port/net_sockets.c"
                                 "${COMPONENT_DIR}/port/esp32/aes.c"
@@ -36,4 +37,4 @@ foreach(target ${mbedtls_targets})
 endforeach()
 
 # Link mbedtls libraries to component library
-target_link_libraries(${COMPONENT_LIB} INTERFACE ${mbedtls_targets})
\ No newline at end of file
+target_link_libraries(${COMPONENT_LIB} INTERFACE ${mbedtls_targets})
diff --git a/components/mbedtls/port/esp_timing.c b/components/mbedtls/port/esp_timing.c
new file mode 100644 (file)
index 0000000..93a34f5
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *  Portable interface to the CPU cycle counter
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/*
+ * mbedtls_timing_get_timer()m mbedtls_timing_set_delay() and
+ * mbedtls_timing_set_delay only abstracted from mbedtls/library/timing.c
+ * as that does not build on ESP-IDF but these 2 functions are needed for
+ * DTLS (in particular mbedtls_ssl_set_timer_cb() must be called for DTLS
+ * which requires these 2 delay functions).
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if !defined(MBEDTLS_ESP_TIMING_C)
+
+#include <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 */
index bc3159dbb7acd478afa530727f1f586823839e2c..7c317dddda3bdf205d46a58f188a78750d68f47f 100644 (file)
@@ -2,14 +2,27 @@
 # CoAP client example
 
 (See the README.md file in the upper level 'examples' directory for more information about examples.)
-this CoAP client example is adaptation of one of the [libcoap](https://github.com/obgm/libcoap) example.
+This CoAP client example is very simplified adaptation of one of the
+[libcoap](https://github.com/obgm/libcoap) examples.
 
-CoAP client example would connect your ESP32 device to any CoAP server, fetch data from CoAP server and upstream data to CoAP server.
+CoAP client example will connect your ESP32 device to a CoAP server, send off a GET request and
+fetch the response data from CoAP server.  The client can be extended to PUT / POST / DELETE requests,
+as well as supporting the Observer extensions [RFC7641](https://tools.ietf.org/html/rfc7641).
 
-The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things.   
-The protocol is designed for machine-to-machine (M2M) applications such as smart energy and building automation.
+If the URI is prefixed with coaps:// instead of coap://, then the CoAP client will attempt to use
+the DTLS protocol using the defined Pre-Shared Keys(PSK) or Public Key Infrastructure (PKI) which the
+CoAP server needs to know about.
 
-please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details.
+If the URI is prefixed with coap+tcp://, then the CoAP will try to use TCP for the communication.
+
+NOTE: coaps+tcp:// is not currently supported, even though both libcoap and MbedTLS support it.
+
+The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with
+constrained nodes and constrained networks in the Internet of Things.   
+The protocol is designed for machine-to-machine (M2M) applications such as smart energy and
+building automation.
+
+Please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details.
 
 ## How to use example
 
@@ -19,16 +32,29 @@ please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf)
 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
 ```
 
@@ -72,9 +98,11 @@ published under EPL+EDL: http://www.eclipse.org/californium/
 This can be found at https://libcoap.net/doc/reference/4.2.0/
 
 ## Troubleshooting
-* Please make sure Target Url includes valid `host`, optional `port`, optional `path`, and begins
-with `coap://` or `coap+tcp://` for a coap server that supports TCP
+* Please make sure Target Url includes valid `host`, optional `port`,
+optional `path`, and begins with `coap://`, `coaps://` or `coap+tcp://`
+for a coap server that supports TCP
 (not all do including coap+tcp://californium.eclipse.org).
 
-* libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0`
-to `#define COAP_LOGGING_LEVEL 9`
+* CoAP logging can be enabled by running 'make menuconfig' and enable debugging
+
+* Encryption (MbedTLS) can be enabled by running 'make menuconfig' and enable debugging
index 90a88c9d3b6c98dc6fc7cff93f70bb7e0a0ea476..eb0d270668bfbf39f1fe9cada258b2bba87ab344 100644 (file)
@@ -1,2 +1,4 @@
+# Embed CA, certificate & key directly into binary
 idf_component_register(SRCS "coap_client_example_main.c"
-                    INCLUDE_DIRS ".")
\ No newline at end of file
+                    INCLUDE_DIRS "."
+                    EMBED_TXTFILES coap_ca.pem coap_client.crt coap_client.key)
index 929b8cf4fa9f0c1aaa2ede9773e1fa2947a2a1d0..f96ac41e4a7bf7afea111286026b7797eacac457 100644 (file)
@@ -1,9 +1,96 @@
-menu "Example Configuration"
+menu "Example CoAP Client Configuration"
 
     config TARGET_DOMAIN_URI
         string "Target Uri"
         default "coap://californium.eclipse.org"
         help
-            Target uri for the example to use.
+            Target uri for the example to use. Use coaps:// prefix for encrypted traffic
+            using Pre-Shared Key (PSK) or Public Key Infrastructure (PKI).
+
+    choice MBEDTLS_COAP_ENCRYPTION_MODE
+        prompt "CoAP Encryption method"
+        default MBEDTLS_COAP_PKI_NONE
+        help
+            If the CoAP information is to be encrypted, the encryption environment
+            can be set up in one of three ways
+
+            - None defined (will use PKI if coaps:// used)
+            - Encrypt using defined Pre-Shared Keys (PSK)
+            - Encrypt using defined Public Key Infrastructure (PKI)
+
+        config MBEDTLS_COAP_NONE
+            bool "None defined"
+
+        config MBEDTLS_COAP_PSK
+            bool "Pre-Shared Keys"
+
+        config MBEDTLS_COAP_PKI
+            bool "PKI Certificates"
+
+    endchoice #MBEDTLS_COAP_ENCRYPTION_MODE
+
+    config COAP_PSK_KEY
+        string "Preshared Key (PSK) to used in the connection to the CoAP server"
+        depends on MBEDTLS_COAP_PSK
+        default "secret-key"
+        help
+            The Preshared Key to use to encrypt the communicatons. The same key must be 
+            used at both ends of the CoAP connection, and the CoaP client must request 
+            an URI prefixed with coaps:// instead of coap:// for DTLS to be used.
+
+    config COAP_PSK_IDENTITY
+        string "PSK Client identity (username)"
+        depends on MBEDTLS_COAP_PSK
+        default "coap-client"
+        help
+            The identity (or username) to use to identify to the CoAP server which
+            PSK key to use.
+
+    config MBEDTLS_COAP_DEBUG
+        bool "Enable CoAP debugging"
+        default n
+        help
+            Enable CoAP debugging functions at compile time for the example code.
+
+            If this option is enabled, call coap_set_log_level()
+            at runtime in order to enable CoAP debug output via the ESP
+            log mechanism.
+
+    choice MBEDTLS_COAP_DEBUG_LEVEL
+        bool "Set CoAP debugging level"
+        depends on MBEDTLS_COAP_DEBUG
+        default COAP_LOG_WARNING
+        help
+            Set CoAP debugging level
+
+        config COAP_LOG_EMERG
+            bool "Emergency"
+        config COAP_LOG_ALERT
+            bool "Alert"
+        config COAP_LOG_CRIT
+            bool "Critical"
+        config COAP_LOG_ERROR
+            bool "Error"
+        config COAP_LOG_WARNING
+            bool "Warning"
+        config COAP_LOG_NOTICE
+            bool "Notice"
+        config COAP_LOG_INFO
+            bool "Info"
+        config COAP_LOG_DEBUG
+            bool "Debug"
+    endchoice
+
+    config COAP_LOG_DEFAULT_LEVEL
+        int
+        default 0 if !MBEDTLS_COAP_DEBUG
+        default 0 if COAP_LOG_EMERG
+        default 1 if COAP_LOG_ALERT
+        default 2 if COAP_LOG_CRIT
+        default 3 if COAP_LOG_ERROR
+        default 4 if COAP_LOG_WARNING
+        default 5 if COAP_LOG_NOTICE
+        default 6 if COAP_LOG_INFO
+        default 7 if COAP_LOG_DEBUG
 
 endmenu
diff --git a/examples/protocols/coap_client/main/coap_ca.pem b/examples/protocols/coap_client/main/coap_ca.pem
new file mode 100644 (file)
index 0000000..1bdf23d
--- /dev/null
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
+VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT
+BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs
+ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w
+DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh
+bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG
+A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66
+1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe
+vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p
+AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG
+A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV
+BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG
+A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl
+LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ
+5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93
+d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA
+euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW
+S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt
+PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc=
+-----END CERTIFICATE-----
diff --git a/examples/protocols/coap_client/main/coap_client.crt b/examples/protocols/coap_client/main/coap_client.crt
new file mode 100644 (file)
index 0000000..0e03a1b
--- /dev/null
@@ -0,0 +1,70 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 48 (0x30)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority
+        Validity
+            Not Before: Jun  7 08:06:49 2017 GMT
+            Not After : Jun  5 08:06:49 2027 GMT
+        Subject: C=FR, ST=Radius, O=Example Inc., CN=user@example.com/emailAddress=user@example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:d2:f6:be:72:a5:ab:2e:56:0c:dd:f2:3b:2c:7c:
+                    e0:5d:05:40:af:0c:8c:f3:82:0c:d0:18:34:b4:e3:
+                    7d:5f:8d:0a:3e:aa:79:02:f9:96:ad:10:00:ec:51:
+                    e9:dc:3f:fb:ea:b0:57:eb:48:c7:ca:ef:e8:05:ab:
+                    ee:3f:66:ba:5c:9e:7f:40:85:9f:25:a0:e0:e3:7c:
+                    cf:b6:e6:31:f5:fd:24:03:c8:f4:fb:d8:a4:f3:92:
+                    29:05:aa:55:43:80:f7:3e:13:10:43:3a:89:24:be:
+                    d8:01:86:d1:69:73:44:7d:f8:b9:46:2b:6b:51:d0:
+                    11:31:4b:06:ae:9f:45:fa:12:17:0c:ef:6a:fa:d0:
+                    f7:36:46:eb:2e:db:4e:20:46:01:33:ac:b1:f7:4a:
+                    e6:18:3d:53:22:dc:e8:4a:12:78:11:2f:e4:3b:92:
+                    bd:d7:07:5a:c9:81:5d:48:58:c8:0f:9b:e9:a4:0f:
+                    bb:89:b1:ad:38:07:6f:93:d0:a6:12:56:f9:07:48:
+                    d2:23:2f:a3:a9:93:b0:11:0a:27:4c:48:0a:8d:70:
+                    41:68:76:7a:dd:bc:54:c3:42:33:b0:7b:f6:ae:1f:
+                    e7:95:5e:11:ca:f2:b4:4b:5c:ba:47:64:f0:f3:d7:
+                    87:95:7f:93:06:a1:72:c9:81:12:a5:b7:8f:9d:7e:
+                    d1:ef
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://www.example.com/example_ca.crl
+
+    Signature Algorithm: sha1WithRSAEncryption
+         2d:02:bc:7b:88:b8:5c:e1:07:b8:bb:ba:b2:f3:98:14:8f:cb:
+         b0:21:13:b5:e5:6f:05:4f:92:fa:ac:c0:53:a7:b0:cd:7e:ba:
+         87:36:85:25:d7:41:c5:29:84:22:74:af:bf:3e:34:36:d5:24:
+         7a:81:e2:1b:54:52:85:6f:76:de:dc:63:98:45:fc:2c:31:fa:
+         22:a4:72:3a:8d:d4:6a:2e:de:33:10:41:eb:94:1d:e3:59:cd:
+         b2:be:ab:f0:b6:20:86:9c:b8:46:ee:c5:64:ba:b6:6c:cc:53:
+         44:7a:80:12:77:7c:e7:51:67:91:32:2f:88:9d:93:a8:ef:d6:
+         cd:de
+-----BEGIN CERTIFICATE-----
+MIIDTjCCAregAwIBAgIBMDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx
+DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
+eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw
+JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw
+ODA2NDlaFw0yNzA2MDUwODA2NDlaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS
+YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt
+cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBANL2vnKlqy5WDN3yOyx84F0FQK8MjPOC
+DNAYNLTjfV+NCj6qeQL5lq0QAOxR6dw/++qwV+tIx8rv6AWr7j9mulyef0CFnyWg
+4ON8z7bmMfX9JAPI9PvYpPOSKQWqVUOA9z4TEEM6iSS+2AGG0WlzRH34uUYra1HQ
+ETFLBq6fRfoSFwzvavrQ9zZG6y7bTiBGATOssfdK5hg9UyLc6EoSeBEv5DuSvdcH
+WsmBXUhYyA+b6aQPu4mxrTgHb5PQphJW+QdI0iMvo6mTsBEKJ0xICo1wQWh2et28
+VMNCM7B79q4f55VeEcrytEtcukdk8PPXh5V/kwahcsmBEqW3j51+0e8CAwEAAaNP
+ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov
+L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB
+gQAtArx7iLhc4Qe4u7qy85gUj8uwIRO15W8FT5L6rMBTp7DNfrqHNoUl10HFKYQi
+dK+/PjQ21SR6geIbVFKFb3be3GOYRfwsMfoipHI6jdRqLt4zEEHrlB3jWc2yvqvw
+tiCGnLhG7sVkurZszFNEeoASd3znUWeRMi+InZOo79bN3g==
+-----END CERTIFICATE-----
diff --git a/examples/protocols/coap_client/main/coap_client.key b/examples/protocols/coap_client/main/coap_client.key
new file mode 100644 (file)
index 0000000..99936e2
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0va+cqWrLlYM3fI7LHzgXQVArwyM84IM0Bg0tON9X40KPqp5
+AvmWrRAA7FHp3D/76rBX60jHyu/oBavuP2a6XJ5/QIWfJaDg43zPtuYx9f0kA8j0
++9ik85IpBapVQ4D3PhMQQzqJJL7YAYbRaXNEffi5RitrUdARMUsGrp9F+hIXDO9q
++tD3NkbrLttOIEYBM6yx90rmGD1TItzoShJ4ES/kO5K91wdayYFdSFjID5vppA+7
+ibGtOAdvk9CmElb5B0jSIy+jqZOwEQonTEgKjXBBaHZ63bxUw0IzsHv2rh/nlV4R
+yvK0S1y6R2Tw89eHlX+TBqFyyYESpbePnX7R7wIDAQABAoIBAQC5PncO3tBIeMEF
+pu007FZq9/DLhP7D2B9+HrMxX0y4uXUUf8aQyS74ukPFP0xV3U1M0BnzfU4KscyQ
+Jl+nBoKAT6C3vF15wiGXQAJ4vPuD4Ate03fjKWH2ixJAakhCZR01QbIXBnBkdrvf
+401BBjlPUDcIGZo8FbLzEMlGTo84vE9v3Qmkbi+PzPCh2YC+NDmsOcIW1zpmwyYC
+ZYCpoWgl4++kqXXn0NGhuaOgB0JLsJOBpx/hOOjBU/wXCKaXZ1vchYqfbvvx2gf2
+WX4P0CiTH1z7MEAHanaZkcnNyxV/oF1EIMY5p0vDDzgrKtppvPOqspjydje03+CE
+t0wKGPi5AoGBAPAG2Y4efgwLcoWdPjKZtsHLhDhLJnvxkqnNkzdPnLZojNi8pKkV
+/Yu++pPemJZZa4YAp+OnqyEfhcha+HYqKMwRC8t3YrEVOlRQTfW/OoSrp059JIRV
+jTvq/u7DdYGJRRgMLUJiEI+7xj1WbTc2EceJAgn0qfKvbvBtVJP0LH1TAoGBAOEA
+xZB7SwyX+zDGRTugqMYg+sYobbQHJ7utLyoX+ckeG+sPEjEYLpQQfshET/gwF8ZK
+4aILkACx/tna799xCjQdmyyc338NO9WULlY1xF+65WfeaxrtTAsqVikX3p19McRI
+ijX8k7Msy3gYWJXev3MCtPT2+g68IgbL/W2wY+l1AoGAT7xGy0Jv5vpqid5pig+s
+OYatHrJAT445hXUIQaiNy77Bg0JvhMgMWT8RKMwabl+4K2TOYP8TB0bcf2lQ/pgU
+w22qOGYpf+AoZ1fh/hAPlYEcbCOAXQG6kDwJgjGmOGjsbgelhVbkX4smWLv8PgoV
+L+7goYQIbNlAhlgbb6b+nIcCgYBB7Zr2Cdpkt0en9ACnRx0M6O7yDziNzqbqzAUM
+3XeYYZUmnATlk8NaKTcs8S9JdrYQqTJR6/dm7MDTDt7IZvPpb19fhBvMu5DztPaa
+1ihTMI01kStq+WsVvnL+mXrmRJ/HdsXgqcCReKep6eBTEbChP4LMYG3G0YNa4HzC
+njO4XQKBgQDRnbqqg2CNTnS94BN2D3uzzELtwsIG6aVCtl09ZsLnGaBKVVDtP6BI
+j2hGD7xw4g5JeSPIJU5J03nALTY3hz1JyI7AJCX7+JRtUTX2A8C4mlbeul7ilGaU
+A7MFT8GqhjYYa84GzNcA1mK8ynlixpL8+yzTT/8lWInWRBa69SkktA==
+-----END RSA PRIVATE KEY-----
index 921541aef2e170479582d359663a52ecae0d834d..067364a73688544aa63b0543d28ed42fc4d977f0 100644 (file)
@@ -7,6 +7,13 @@
    CONDITIONS OF ANY KIND, either express or implied.
 */
 
+/*
+ * WARNING
+ * libcoap is not multi-thread safe, so only this thread must make any coap_*()
+ * calls.  Any external (to this thread) data transmitted in/out via libcoap
+ * therefore has to be passed in/out by xQueue*() via this thread.
+ */
+
 #include <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)
@@ -124,17 +155,44 @@ clean_up:
     resp_wait = 0;
 }
 
-static void coap_example_task(void *p)
+#ifdef CONFIG_MBEDTLS_COAP_PKI
+
+#ifdef __GNUC__
+#define UNUSED_PARAM __attribute__ ((unused))
+#else /* not a GCC */
+#define UNUSED_PARAM
+#endif /* GCC */
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+static int
+verify_cn_callback(const char *cn,
+                   const uint8_t *asn1_public_cert UNUSED_PARAM,
+                   size_t asn1_length UNUSED_PARAM,
+                   coap_session_t *session UNUSED_PARAM,
+                   unsigned depth,
+                   int validated UNUSED_PARAM,
+                   void *arg UNUSED_PARAM
+) {
+  coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n",
+           cn, depth ? "CA" : "Certificate");
+  return 1;
+}
+#endif /* CONFIG_MBEDTLS_COAP_PKI */
+
+static void coap_example_client(void *p)
 {
     struct hostent *hp;
-    struct ip4_addr *ip4_addr;
 
-    coap_address_t    dst_addr, src_addr;
+    coap_address_t    dst_addr;
     static coap_uri_t uri;
     const char*       server_uri = COAP_DEFAULT_DEMO_URI;
     char* phostname = NULL;
 
-    coap_set_log_level(COAP_LOGGING_LEVEL);
+    coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
+
     while (1) {
 #define BUFSIZE 40
         unsigned char _buf[BUFSIZE];
@@ -153,7 +211,7 @@ static void coap_example_task(void *p)
 
         if ((uri.scheme==COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) ||
             (uri.scheme==COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported())) {
-            ESP_LOGE(TAG, "CoAP server uri scheme error");
+            ESP_LOGE(TAG, "CoAP server uri scheme is not supported");
             break;
         }
 
@@ -172,19 +230,31 @@ static void coap_example_task(void *p)
             ESP_LOGE(TAG, "DNS lookup failed");
             vTaskDelay(1000 / portTICK_PERIOD_MS);
             free(phostname);
-            continue;
+            goto clean_up;
+        }
+        {
+        char tmpbuf[INET6_ADDRSTRLEN];
+            coap_address_init(&dst_addr);
+            switch (hp->h_addrtype) {
+            case AF_INET:
+                dst_addr.addr.sin.sin_family      = AF_INET;
+                dst_addr.addr.sin.sin_port        = htons(uri.port);
+                memcpy(&dst_addr.addr.sin.sin_addr, hp->h_addr, sizeof(dst_addr.addr.sin.sin_addr));
+                inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf));
+                ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
+                break;
+            case AF_INET6:
+                dst_addr.addr.sin6.sin6_family      = AF_INET6;
+                dst_addr.addr.sin6.sin6_port        = htons(uri.port);
+                memcpy(&dst_addr.addr.sin6.sin6_addr, hp->h_addr, sizeof(dst_addr.addr.sin6.sin6_addr));
+                inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf));
+                ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
+                break;
+            default:
+                ESP_LOGE(TAG, "DNS lookup response failed");
+                goto clean_up;
+            }
         }
-
-        /* Code to print the resolved IP.
-
-           Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
-        ip4_addr = (struct ip4_addr *)hp->h_addr;
-        ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr));
-
-        coap_address_init(&src_addr);
-        src_addr.addr.sin.sin_family      = AF_INET;
-        src_addr.addr.sin.sin_port        = htons(0);
-        src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
 
         if (uri.path.length) {
             buflen = BUFSIZE;
@@ -222,15 +292,102 @@ static void coap_example_task(void *p)
            goto clean_up;
         }
 
-        coap_address_init(&dst_addr);
-        dst_addr.addr.sin.sin_family      = AF_INET;
-        dst_addr.addr.sin.sin_port        = htons(uri.port);
-        dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr;
-
-        session = coap_new_client_session(ctx, &src_addr, &dst_addr,
-           uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
-           uri.scheme==COAP_URI_SCHEME_COAPS_TCP ? COAP_PROTO_TLS :
-           uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_UDP);
+        /*
+         * Note that if the URI starts with just coap:// (not coaps://) the
+         * session will still be plain text.
+         *
+         * coaps+tcp:// is NOT supported by the libcoap->mbedtls interface
+         * so COAP_URI_SCHEME_COAPS_TCP will have failed in a test above,
+         * but the code is left in for completeness.
+         */
+        if (uri.scheme==COAP_URI_SCHEME_COAPS || uri.scheme==COAP_URI_SCHEME_COAPS_TCP) {
+#ifdef CONFIG_MBEDTLS_COAP_PSK
+            session = coap_new_client_session_psk(ctx, NULL, &dst_addr,
+               uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
+               EXAMPLE_COAP_PSK_IDENTITY,
+               (const uint8_t*)EXAMPLE_COAP_PSK_KEY,
+               sizeof(EXAMPLE_COAP_PSK_KEY)-1);
+#endif /* CONFIG_MBEDTLS_COAP_PSK */
+
+#ifdef CONFIG_MBEDTLS_COAP_PKI
+/* CA cert, taken from coap_ca.pem
+   Client cert, taken from coap_client.crt
+   Client key, taken from coap_client.key
+
+   The PEM, CRT and KEY file are examples taken from the wpa2 enterprise
+   example.
+
+   To embed it in the app binary, the PEM, CRT and KEY file is named
+   in the component.mk COMPONENT_EMBED_TXTFILES variable.
+*/
+extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start");
+extern uint8_t ca_pem_end[]   asm("_binary_coap_ca_pem_end");
+extern uint8_t client_crt_start[] asm("_binary_coap_client_crt_start");
+extern uint8_t client_crt_end[]   asm("_binary_coap_client_crt_end");
+extern uint8_t client_key_start[] asm("_binary_coap_client_key_start");
+extern uint8_t client_key_end[]   asm("_binary_coap_client_key_end");
+            unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
+            unsigned int client_crt_bytes = client_crt_end - client_crt_start;
+            unsigned int client_key_bytes = client_key_end - client_key_start;
+            coap_dtls_pki_t dtls_pki;
+            static char client_sni[256];
+
+            memset (&dtls_pki, 0, sizeof(dtls_pki));
+            dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
+            if (ca_pem_bytes) {
+                /*
+                 * Add in additional certificate checking.
+                 * This list of enabled can be tuned for the specific
+                 * requirements - see 'man coap_encryption'.
+                 *
+                 * Note: A list of root ca file can be setup separately using
+                 * coap_context_set_pki_root_cas(), but the below is used to
+                 * define what checking actually takes place.
+                 */
+                dtls_pki.verify_peer_cert        = 1;
+                dtls_pki.require_peer_cert       = 1;
+                dtls_pki.allow_self_signed       = 1;
+                dtls_pki.allow_expired_certs     = 1;
+                dtls_pki.cert_chain_validation   = 1;
+                dtls_pki.cert_chain_verify_depth = 2;
+                dtls_pki.check_cert_revocation   = 1;
+                dtls_pki.allow_no_crl            = 1;
+                dtls_pki.allow_expired_crl       = 1;
+                dtls_pki.allow_bad_md_hash       = 1;
+                dtls_pki.allow_short_rsa_length  = 1;
+                dtls_pki.validate_cn_call_back   = verify_cn_callback;
+                dtls_pki.cn_call_back_arg        = NULL;
+                dtls_pki.validate_sni_call_back  = NULL;
+                dtls_pki.sni_call_back_arg       = NULL;
+                memset(client_sni, 0, sizeof(client_sni));
+                if (uri.host.length)
+                    memcpy(client_sni, uri.host.s, min(uri.host.length, sizeof(client_sni)));
+                else
+                    memcpy(client_sni, "localhost", 9);
+                dtls_pki.client_sni = client_sni;
+            }
+            dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
+            dtls_pki.pki_key.key.pem_buf.public_cert = client_crt_start;
+            dtls_pki.pki_key.key.pem_buf.public_cert_len = client_crt_bytes;
+            dtls_pki.pki_key.key.pem_buf.private_key = client_key_start;
+            dtls_pki.pki_key.key.pem_buf.private_key_len = client_key_bytes;
+            dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
+            dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
+
+            session = coap_new_client_session_pki(ctx, NULL, &dst_addr,
+               uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
+               &dtls_pki);
+#endif /* CONFIG_MBEDTLS_COAP_PKI */
+
+#ifdef CONFIG_MBEDTLS_COAP_NONE
+            session = coap_new_client_session(ctx, NULL, &dst_addr,
+               uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS);
+#endif /* CONFIG_MBEDTLS_COAP_NONE */
+        } else {
+            session = coap_new_client_session(ctx, NULL, &dst_addr,
+               uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
+               COAP_PROTO_UDP);
+        }
         if (!session) {
            ESP_LOGE(TAG, "coap_new_client_session() failed");
            goto clean_up;
@@ -272,7 +429,10 @@ 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;
     }
 
@@ -285,11 +445,19 @@ void app_main(void)
     tcpip_adapter_init();
     ESP_ERROR_CHECK(esp_event_loop_create_default());
 
+#if 0
+/* See https://github.com/Ebiroll/qemu_esp32 for further information */
+#include "emul_ip.h"
+    if (is_running_qemu()) {
+        xTaskCreate(task_lwip_init, "task_lwip_init", 2*4096, NULL, 20, NULL); 
+    }
+    else
+#endif
     /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
      * Read "Establishing Wi-Fi or Ethernet Connection" section in
      * examples/protocols/README.md for more information about this function.
      */
     ESP_ERROR_CHECK(example_connect());
 
-    xTaskCreate(coap_example_task, "coap", 5 * 1024, NULL, 5, NULL);
+    xTaskCreate(coap_example_client, "coap", 8 * 1024, NULL, 5, NULL);
 }
index 0b9d7585e76877a6b3e3a3c81197a0ad8147b3df..50306ea2f91d2ae303834480fbcb47ceacfd7115 100644 (file)
@@ -3,3 +3,8 @@
 #
 # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
 
+# embed files from the "certs" directory as binary data symbols
+# in the app
+COMPONENT_EMBED_TXTFILES := coap_ca.pem
+COMPONENT_EMBED_TXTFILES += coap_client.crt
+COMPONENT_EMBED_TXTFILES += coap_client.key
index cf00279a4914625833135640728e01d15ea23f1e..60941855f1e4edbfc67fc943e26c2460b6919e9a 100644 (file)
@@ -2,14 +2,26 @@
 # CoAP server example
 
 (See the README.md file in the upper level 'examples' directory for more information about examples.)  
-This CoAP server example is adaptation of one of the [libcoap](https://github.com/obgm/libcoap) example.
+This CoAP server example is very simplified adaptation of one of the
+[libcoap](https://github.com/obgm/libcoap) examples.
 
-CoAP server example would startup a daemon task, receive data from CoAP client and transmit data to CoAP client.
+CoAP server example will startup a daemon task, receive requests / data from CoAP client and transmit
+data to CoAP client.
 
-The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things.   
-The protocol is designed for machine-to-machine (M2M) applications such as smart energy and building automation.
+If the incoming request requests the use of DTLS (connecting to port 5684), then the CoAP server will
+try to establish a DTLS session using the previously defined Pre-Shared Key (PSK) - which
+must be the same as the one that the CoAP client is using, or Public Key Infrastructure (PKI) where
+the PKI information must match as requested.
 
-please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details.
+NOTE: Client sessions trying to use coaps+tcp:// are not currently supported, even though both
+libcoap and MbedTLS support it.
+
+The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with
+constrained nodes and constrained networks in the Internet of Things.   
+The protocol is designed for machine-to-machine (M2M) applications such as smart energy and
+building automation.
+
+Please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details.
 
 ## How to use example
 
@@ -19,15 +31,29 @@ please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf)
 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
 ```
 
@@ -54,8 +80,8 @@ I (2622) CoAP_server: Connected to AP
 ...
 ```
 
-if a CoAP client query `/Espressif` resource, CoAP server would return `"no data"`  
-until a CoAP client does a PUT with some data.
+If a CoAP client queries the `/Espressif` resource, CoAP server will return `"Hello World!"`  
+until a CoAP client does a PUT with different data.
 
 ## libcoap Documentation
 This can be found at https://libcoap.net/doc/reference/4.2.0/
@@ -64,5 +90,7 @@ This can be found at https://libcoap.net/doc/reference/4.2.0/
 * Please make sure CoAP client fetchs or puts data under path: `/Espressif` or
 fetches `/.well-known/core`
 
-* libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0`
-to `#define COAP_LOGGING_LEVEL 9`
+* CoAP logging can be enabled by running 'make menuconfig' and enable debugging
+
+* Encryption (MbedTLS) can be enabled by running 'make menuconfig' and enable debugging
+
index c28a7be75eede498d0da742994b037f4cebccde8..d4738f6918755163c8bf500ce33e4fd334ae13bf 100644 (file)
@@ -1,2 +1,3 @@
 idf_component_register(SRCS "coap_server_example_main.c"
-                    INCLUDE_DIRS ".")
\ No newline at end of file
+                    INCLUDE_DIRS "."
+                    EMBED_TXTFILES coap_ca.pem coap_server.crt coap_server.key)
diff --git a/examples/protocols/coap_server/main/Kconfig.projbuild b/examples/protocols/coap_server/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..bdf3b12
--- /dev/null
@@ -0,0 +1,74 @@
+menu "Example CoAP Server Configuration"
+
+    config MBEDTLS_COAP_PSK
+        bool "Pre-Shared Keys (PSK)"
+        default n
+        help
+            Use Pre-Shared Keys to encrypt the communications between the
+            CoAP Server and CoAP Client. Both ends need the same
+            Pre-Shared Key.
+
+    config COAP_PSK_KEY
+        string "Preshared Key (PSK) to used in the connection from the CoAP client"
+        depends on MBEDTLS_COAP_PSK
+        default "secret-key"
+        help
+            The Preshared Key to use to encrypt the communicatons. The same key must be 
+            used at both ends of the CoAP connection, and the CoaP client must request 
+            an URI prefixed with coaps:// instead of coap:// for DTLS to be used.
+
+    config MBEDTLS_COAP_PKI
+        bool "Public Key Infrastructure (PKI)"
+        default n
+        help
+            Use PKI Certificates and Private Keys to encrypt the communications
+            between the CoAP Server and CoAP Client.
+
+    config MBEDTLS_COAP_DEBUG
+        bool "Enable CoAP debugging"
+        default n
+        help
+            Enable CoAP debugging functions at compile time for the example code.
+
+            If this option is enabled, call coap_set_log_level()
+            at runtime in order to enable CoAP debug output via the ESP
+            log mechanism.
+
+    choice MBEDTLS_COAP_DEBUG_LEVEL
+        bool "Set CoAP debugging level"
+        depends on MBEDTLS_COAP_DEBUG
+        default COAP_LOG_WARNING
+        help
+            Set CoAP debugging level
+
+        config COAP_LOG_EMERG
+            bool "Emergency"
+        config COAP_LOG_ALERT
+            bool "Alert"
+        config COAP_LOG_CRIT
+            bool "Critical"
+        config COAP_LOG_ERROR
+            bool "Error"
+        config COAP_LOG_WARNING
+            bool "Warning"
+        config COAP_LOG_NOTICE
+            bool "Notice"
+        config COAP_LOG_INFO
+            bool "Info"
+        config COAP_LOG_DEBUG
+            bool "Debug"
+    endchoice
+
+    config COAP_LOG_DEFAULT_LEVEL
+        int
+        default 0 if !MBEDTLS_COAP_DEBUG
+        default 0 if COAP_LOG_EMERG
+        default 1 if COAP_LOG_ALERT
+        default 2 if COAP_LOG_CRIT
+        default 3 if COAP_LOG_ERROR
+        default 4 if COAP_LOG_WARNING
+        default 5 if COAP_LOG_NOTICE
+        default 6 if COAP_LOG_INFO
+        default 7 if COAP_LOG_DEBUG
+
+endmenu
diff --git a/examples/protocols/coap_server/main/coap_ca.pem b/examples/protocols/coap_server/main/coap_ca.pem
new file mode 100644 (file)
index 0000000..1bdf23d
--- /dev/null
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
+VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT
+BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs
+ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w
+DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh
+bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG
+A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66
+1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe
+vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p
+AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG
+A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV
+BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG
+A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl
+LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ
+5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93
+d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA
+euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW
+S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt
+PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc=
+-----END CERTIFICATE-----
diff --git a/examples/protocols/coap_server/main/coap_server.crt b/examples/protocols/coap_server/main/coap_server.crt
new file mode 100644 (file)
index 0000000..8f1dfcd
--- /dev/null
@@ -0,0 +1,70 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 47 (0x2f)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority
+        Validity
+            Not Before: Jun  7 08:06:49 2017 GMT
+            Not After : Jun  5 08:06:49 2027 GMT
+        Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c9:d8:e2:e0:75:91:83:87:d8:c8:80:c6:20:4d:
+                    e9:14:24:30:98:33:53:fa:56:0e:ec:9a:43:7f:87:
+                    a9:22:94:26:06:c7:ac:b5:d9:ec:55:06:81:b7:0d:
+                    c9:24:51:49:fa:47:fb:4b:4e:fc:ed:75:8a:e1:28:
+                    32:bc:c5:e0:4c:45:c4:58:60:15:67:1e:6b:40:19:
+                    3f:f0:ab:92:61:92:2d:71:10:2e:f2:eb:bc:81:2f:
+                    5a:3b:74:ca:5f:fd:e0:ee:d1:d9:07:6a:6c:20:c0:
+                    07:88:b4:8b:0f:ad:1e:c9:4f:7c:11:98:37:89:15:
+                    de:24:b1:11:1a:7c:97:4a:cf:f3:c8:cb:79:9e:9c:
+                    c3:71:da:a6:94:97:f5:95:fd:61:06:44:e2:3f:12:
+                    43:0b:1d:33:48:91:d2:ce:4f:97:a1:ed:6a:30:c7:
+                    5d:98:b5:6e:0a:b7:4f:d9:03:ec:80:76:09:b0:40:
+                    a1:a1:af:ab:2a:59:c4:0f:56:22:bc:be:14:be:18:
+                    df:10:7d:5d:22:bf:e5:04:77:7a:75:6b:3e:eb:6d:
+                    20:a1:a7:60:d4:f1:87:9d:9f:60:b9:d3:db:2c:25:
+                    f4:91:4a:f1:d2:40:e5:a1:10:88:a0:41:5a:98:40:
+                    ca:15:d7:e3:e6:3e:c0:6a:d5:46:b2:b4:90:b4:ae:
+                    3b:e3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://www.example.com/example_ca.crl
+
+    Signature Algorithm: sha1WithRSAEncryption
+         a4:25:21:51:0b:22:6c:63:8d:a9:c1:4f:04:33:69:79:34:f0:
+         36:dd:8f:6a:27:5f:07:a2:1d:ef:8b:f0:96:e6:e7:a3:b8:3b:
+         85:5e:3f:26:43:8a:8e:95:58:9c:a6:db:9c:51:bf:ea:53:16:
+         3e:c1:a8:11:1a:c6:cf:0e:a1:17:18:64:d2:05:f1:c0:9c:a6:
+         2b:16:c4:29:54:03:d2:17:bd:15:74:d6:ad:8a:8f:2d:cc:27:
+         3b:88:88:f2:ea:d0:a2:cb:e9:42:57:df:26:9f:8a:a2:02:2f:
+         35:b6:19:1d:26:43:44:af:12:4b:bc:b9:84:50:02:fd:1d:fa:
+         50:e8
+-----BEGIN CERTIFICATE-----
+MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx
+DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
+eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw
+JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw
+ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS
+YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT
+ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
+Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG
+IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy
+vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL
+D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS
+zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6
+dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ
+tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug
+KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG
+SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4
+O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0
+1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo
+-----END CERTIFICATE-----
diff --git a/examples/protocols/coap_server/main/coap_server.key b/examples/protocols/coap_server/main/coap_server.key
new file mode 100644 (file)
index 0000000..9b34332
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAydji4HWRg4fYyIDGIE3pFCQwmDNT+lYO7JpDf4epIpQmBses
+tdnsVQaBtw3JJFFJ+kf7S0787XWK4SgyvMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu
+8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSLD60eyU98EZg3iRXeJLERGnyXSs/zyMt5
+npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHSzk+Xoe1qMMddmLVuCrdP2QPsgHYJsECh
+oa+rKlnED1YivL4UvhjfEH1dIr/lBHd6dWs+620goadg1PGHnZ9gudPbLCX0kUrx
+0kDloRCIoEFamEDKFdfj5j7AatVGsrSQtK474wIDAQABAoIBAQC2kGDEPBJdMSW2
+VCLfXRiPixwYzXQLXIMrJWwfkQg9qlmqkDd6U50aWkRA2UswegW7RhfYSZ0i+cmf
+VMhvTVpOIlwwwtcY6b5/v1bBy60eaySGuuh79xQMlFO8qynQIMStvUfbGTqrdIRb
+9VBB4YeS9T12fILejtTZwv2BQ2dj1Y1SCay6Ri85UzJqSClRKgHISybvVdLNjPvP
+0TRFBr57zyjL6WE8teKiKchzQko2u86No5uBCdKGsrAkrsdcR0YqlM/pZxd3VKNm
++eny0k+dZZlvcPxzkzP4hEp9+Rw5rP9/s3s/cCwvuuC5JO32ATBWKCbTvPv/XPDb
+MdSJtOshAoGBAPzk0eswkcbFYtpnpBNmBAr1dtAdW1lfjUI2ucMMwt7Wns0P/tt+
+gq6Hi1wTaGP0l/dIECgeHwjtWj31ZJjQtFJ1y/kafxo4o9cA8vCydpdvSZaldAfg
+sbLlDTDYzEpelaDIbNQBBXFoC5U9JlBhBsIFCL5Z8ZuIeFPsb7t5wwuHAoGBAMxT
+jyWfNm1uNxp1xgCnrRsLPQPVnURrSFAqcHrECqRu3F7sozTN7q/cZViemxPvVDGQ
+p9c+9bHwaYvW4trO5qDHJ++gGwm5L52bMAY1VUfeTt67fqrey43XpdmzcTX1V9Uj
+QWawPUCSDzFjL1MjfCIejtyYf5ash53vj+T8r/vFAoGAA/OPVB1uKazr3n3AEo2F
+gqZTNO1AgCT+EArK3EFWyiSQVqPpV4SihheYFdg3yVgJB9QYbIgL9BfBUTaEW97m
+8mLkzP+c/Mvlw3ZAVYJ0V+llPPVY2saoACOUES9SAdd4fwqiqK1baGo3xB0wfBEI
+CgAKIu9E1ylKuAT5ufQtGAECgYEAtP/kU5h5N3El4QupTdU7VDSdZTMqsHw0v8cI
+gsf9AXKvRmtrnBA8u46KPHmruHoO5CVXeSZtsaXdaaH+rYQQ6yXg67WxnehtFLlv
+TmCaXiLBTS9cYvMf8FOyuGnsBLeEietEOTov2G5KhR5uwsAxa2wUc7endor5S9/2
+YQuyvV0CgYALbiFpILd5l1ip65eE6JdA3hfttUbV2j2NSW12ej69vqbeOfaSgNse
+uYCcXFsBbQPhNPwA+4d1oCe8SyXZg1f7gE812z2Tyr/3vdVnNZlitoxhsHmGiyS7
+gZdaTYCb78l9z0EBdaCVvA16owEle4SR6f9eCwzSI0WPOUra+x/hrA==
+-----END RSA PRIVATE KEY-----
index eb5e131baaf9f12ea36500a16e22763a96b8741c..b2f78bb032705715722106fffecbc4b130b72ef6 100644 (file)
@@ -7,6 +7,13 @@
    CONDITIONS OF ANY KIND, either express or implied.
 */
 
+/*
+ * WARNING
+ * libcoap is not multi-thread safe, so only this thread must make any coap_*()
+ * calls.  Any external (to this thread) data transmitted in/out via libcoap
+ * therefore has to be passed in/out by xQueue*() via this thread.
+ */
+
 #include <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
  */
@@ -59,7 +91,7 @@ hnd_espressif_put(coap_context_t *ctx,
 
     coap_resource_notify_observers(resource, NULL);
 
-    if (strcmp (espressif_data, "no data") == 0) {
+    if (strcmp (espressif_data, INITIAL_DATA) == 0) {
         response->code = COAP_RESPONSE_CODE(201);
     }
     else {
@@ -70,7 +102,7 @@ hnd_espressif_put(coap_context_t *ctx,
     (void)coap_get_data(request, &size, &data);
 
     if (size == 0) {      /* re-init */
-        snprintf(espressif_data, sizeof(espressif_data), "no data");
+        snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
         espressif_data_len = strlen(espressif_data);
     } else {
         espressif_data_len = size > sizeof (espressif_data) ? sizeof (espressif_data) : size;
@@ -88,23 +120,50 @@ hnd_espressif_delete(coap_context_t *ctx,
                      coap_pdu_t *response)
 {
     coap_resource_notify_observers(resource, NULL);
-    snprintf(espressif_data, sizeof(espressif_data), "no data");
+    snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
     espressif_data_len = strlen(espressif_data);
     response->code = COAP_RESPONSE_CODE(202);
 }
 
-static void coap_example_thread(void *p)
+#ifdef CONFIG_MBEDTLS_COAP_PKI
+
+#ifdef __GNUC__
+#define UNUSED_PARAM __attribute__ ((unused))
+#else /* not a GCC */
+#define UNUSED_PARAM
+#endif /* GCC */
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+static int
+verify_cn_callback(const char *cn,
+                   const uint8_t *asn1_public_cert UNUSED_PARAM,
+                   size_t asn1_length UNUSED_PARAM,
+                   coap_session_t *session UNUSED_PARAM,
+                   unsigned depth,
+                   int validated UNUSED_PARAM,
+                   void *arg UNUSED_PARAM
+) {
+  coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n",
+           cn, depth ? "CA" : "Certificate");
+  return 1;
+}
+#endif /* CONFIG_MBEDTLS_COAP_PKI */
+
+static void coap_example_server(void *p)
 {
     coap_context_t *ctx = NULL;
-    coap_address_t   serv_addr;
+    coap_address_t serv_addr;
     coap_resource_t *resource = NULL;
 
-    snprintf(espressif_data, sizeof(espressif_data), "no data");
+    snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
     espressif_data_len = strlen(espressif_data);
-    coap_set_log_level(COAP_LOGGING_LEVEL);
+    coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
+
     while (1) {
-        coap_endpoint_t *ep_udp = NULL;
-        coap_endpoint_t *ep_tcp = NULL;
+        coap_endpoint_t *ep = NULL;
         unsigned wait_ms;
 
         /* Prepare the CoAP server socket */
@@ -117,14 +176,91 @@ static void coap_example_thread(void *p)
         if (!ctx) {
            continue;
         }
-        ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP);
-        if (!ep_udp) {
+#ifdef CONFIG_MBEDTLS_COAP_PSK
+        /* Need PSK setup before we set up endpoints */
+        coap_context_set_psk(ctx, "CoAP",
+                             (const uint8_t*)EXAMPLE_COAP_PSK_KEY,
+                             sizeof(EXAMPLE_COAP_PSK_KEY)-1);
+#endif /* CONFIG_MBEDTLS_COAP_PSK */
+
+#ifdef CONFIG_MBEDTLS_COAP_PKI
+/* CA cert, taken from coap_ca.pem
+   Server cert, taken from coap_server.crt
+   Server key, taken from coap_server.key
+
+   The PEM, CRT and KEY file are examples taken from the wpa2 enterprise
+   example.
+
+   To embed it in the app binary, the PEM, CRT and KEY file is named
+   in the component.mk COMPONENT_EMBED_TXTFILES variable.
+*/
+extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start");
+extern uint8_t ca_pem_end[]   asm("_binary_coap_ca_pem_end");
+extern uint8_t server_crt_start[] asm("_binary_coap_server_crt_start");
+extern uint8_t server_crt_end[]   asm("_binary_coap_server_crt_end");
+extern uint8_t server_key_start[] asm("_binary_coap_server_key_start");
+extern uint8_t server_key_end[]   asm("_binary_coap_server_key_end");
+            unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
+            unsigned int server_crt_bytes = server_crt_end - server_crt_start;
+            unsigned int server_key_bytes = server_key_end - server_key_start;
+            coap_dtls_pki_t dtls_pki;
+
+            memset (&dtls_pki, 0, sizeof(dtls_pki));
+            dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
+            if (ca_pem_bytes) {
+                /*
+                 * Add in additional certificate checking.
+                 * This list of enabled can be tuned for the specific
+                 * requirements - see 'man coap_encryption'.
+                 *
+                 * Note: A list of root ca file can be setup separately using
+                 * coap_context_set_pki_root_cas(), but the below is used to
+                 * define what checking actually takes place.
+                 */
+                dtls_pki.verify_peer_cert        = 1;
+                dtls_pki.require_peer_cert       = 1;
+                dtls_pki.allow_self_signed       = 1;
+                dtls_pki.allow_expired_certs     = 1;
+                dtls_pki.cert_chain_validation   = 1;
+                dtls_pki.cert_chain_verify_depth = 2;
+                dtls_pki.check_cert_revocation   = 1;
+                dtls_pki.allow_no_crl            = 1;
+                dtls_pki.allow_expired_crl       = 1;
+                dtls_pki.allow_bad_md_hash       = 1;
+                dtls_pki.allow_short_rsa_length  = 1;
+                dtls_pki.validate_cn_call_back   = verify_cn_callback;
+                dtls_pki.cn_call_back_arg        = NULL;
+                dtls_pki.validate_sni_call_back  = NULL;
+                dtls_pki.sni_call_back_arg       = NULL;
+            }
+            dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
+            dtls_pki.pki_key.key.pem_buf.public_cert = server_crt_start;
+            dtls_pki.pki_key.key.pem_buf.public_cert_len = server_crt_bytes;
+            dtls_pki.pki_key.key.pem_buf.private_key = server_key_start;
+            dtls_pki.pki_key.key.pem_buf.private_key_len = server_key_bytes;
+            dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
+            dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
+
+            coap_context_set_pki(ctx, &dtls_pki);
+#endif /* CONFIG_MBEDTLS_COAP_PKI */
+
+        ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP);
+        if (!ep) {
            goto clean_up;
         }
-        ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP);
-        if (!ep_tcp) {
+        ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP);
+        if (!ep) {
            goto clean_up;
         }
+#if defined(CONFIG_MBEDTLS_COAP_PSK) || defined(CONFIG_MBEDTLS_COAP_PKI)
+       if (coap_dtls_is_supported()) {
+            serv_addr.addr.sin.sin_port = htons(COAPS_DEFAULT_PORT);
+            ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_DTLS);
+            if (!ep) {
+               goto clean_up;
+            }
+        }
+#endif /* CONFIG_MBEDTLS_COAP_PSK CONFIG_MBEDTLS_COAP_PKI */
         resource = coap_resource_init(coap_make_str_const("Espressif"), 0);
         if (!resource) {
            goto clean_up;
@@ -165,11 +301,19 @@ void app_main(void)
     tcpip_adapter_init();
     ESP_ERROR_CHECK(esp_event_loop_create_default());
 
+#if 0
+/* See https://github.com/Ebiroll/qemu_esp32 for further information */
+#include "emul_ip.h"
+    if (is_running_qemu()) {
+        xTaskCreate(task_lwip_init, "task_lwip_init", 2*4096, NULL, 20, NULL); 
+    }
+    else
+#endif
     /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
      * Read "Establishing Wi-Fi or Ethernet Connection" section in
      * examples/protocols/README.md for more information about this function.
      */
     ESP_ERROR_CHECK(example_connect());
 
-    xTaskCreate(coap_example_thread, "coap", 1024 * 5, NULL, 5, NULL);
+    xTaskCreate(coap_example_server, "coap", 8 * 1024, NULL, 5, NULL);
 }
index 0b9d7585e76877a6b3e3a3c81197a0ad8147b3df..bccdac23fd6a210e1a9d8da6e559d39117b3f50d 100644 (file)
@@ -3,3 +3,8 @@
 #
 # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
 
+# embed files from the "certs" directory as binary data symbols
+# in the app
+COMPONENT_EMBED_TXTFILES := coap_ca.pem
+COMPONENT_EMBED_TXTFILES += coap_server.crt
+COMPONENT_EMBED_TXTFILES += coap_server.key