diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..fcbc6e0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "sdk"]
+ path = sdk
+ url = https://github.com/Azure/azure-iot-sdk-c.git
diff --git a/README.md b/README.md
index 72f1506..e2ba56c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,100 @@
+# Get Started with Microsoft Azure IoT Starter Kit - ESP32-DevKitC ("Core Board")
+
+This sample was tested with Espressif's **ESP32-DevKitC ("Core Board")**, but many other kits would work as well.
+
+Don't have a kit yet? Click [here](http://esp32.net/)
+
+This sample was modified from [this one](https://github.com/ustccw/AzureESP32.git).
+
+## Step 1 - Download the ESP32 SDK
+
+Clone the [Espressif IoT Development Framework](https://github.com/espressif/esp-idf) repository with the following command:
+
+`git clone https://github.com/espressif/esp-idf.git --recursive`
+
+## Step 2 - Set up the ESP32 toolchain
+
+Follow the instructions for setting up the ESP32 toolchain found [here](http://esp-idf.readthedocs.io/en/latest/#setup-toolchain).
+
+## Step 3 - Set IDF_PATH
+
+Set the IDF_PATH environment variable to point to the location of the **esp-idf** directory that you
+cloned in Step 1.
+
+If you're using MSYS on Windows, a good place to set the IDF_PATH variable is in the
+`~\msys32\home\user\.bashrc` file that gets created the first time you run `msys2_shell.cmd`.
+
+## Step 4 - Install the Azure IoT C SDK
+
+Change to the ESP32 SDK's `components` directory:
+`cd $IDF_PATH/components`
+
+Clone this repository into the `components` directory as `azure-iot`:
+`git clone --recursive https://github.com/Azure-Samples/iot-hub-c-esp32.git azure-iot`
+
+## Step 5 - Assemble the sample project
+
+Your `$IDF_PATH/components/azure-iot/sample` directory contains a sample project skeleton. You may
+compile it from that location, or you may copy it to a new location, whichever you prefer.
+To flesh out the skeleton,
+copy the sample files from the Azure IoT SDK into `main` directory of the copy of the sample that
+you're using:
+
+`sloc=$IDF_PATH/components/azure-iot/sdk/iothub_client/samples/iothub_client_sample_mqtt`
+`cp $sloc/iothub_client_sample_mqtt.h /main`
+`cp $sloc/iothub_client_sample_mqtt.c /main`
+
+## Step 6 - Set your device's connection string
+
+Create an IoT Hub and an associated device identity
+[as shown here](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-csharp-csharp-getstarted).
+Then open the `main/iothub_client_sample_mqtt.c` file in the `main` directory of your
+project and find the line near the top that reads
+
+```c
+static const char* connectionString = "";
+
+```
+
+and set the value of the connectionString variable to the be the connection string of the device
+identity that you created.
+
+## Step 7- Configure the make process
+
+Using the toolchain you installed in Step 2 (MSYS, for example), navigate to the location of the
+sample you assembled in Step 4 and run the following command:
+
+`make menuconfig`
+
+This command will bring up a configuration dialog.
+
+1. Under "Serial flasher config --->Default serial port" set the serial port ID to that of your ESP32 device. (On Windows you can find the serial port ID under Computer Management.)
+
+1. Under "Example Configuration --->" enter your WiFi router SSID and password.
+
+1. Save the configuration and exit the dialog.
+
+## Step 8 - Run the make process
+
+Build the sample with the simple command:
+
+`make`
+
+This will produce a iothub_client_sample_mqtt.bin file, a partitions_singleapp.bin file, a bootloader/bootloader.bin file, plus associated maps.
+
+## Step 9 - Flash the ESP32 device
+
+Make sure the ESP32 device is plugged in and run the command:
+
+`make flash`
+
+This will flash the project onto the ESP32 device. Alternate methods of flashing the device can be found [here](https://espressif.com/en/support/download/other-tools)
+
+## Step 10 - Monitor the device output
+
+The sample program sends status output to the device's serial port at a default 115200 baud. You monitor this output by connecting to the serial port with any terminal program such as [Putty](http://www.putty.org/).
+
# Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
diff --git a/component.mk b/component.mk
new file mode 100644
index 0000000..016290d
--- /dev/null
+++ b/component.mk
@@ -0,0 +1,110 @@
+#
+# Component Makefile
+#
+
+# Component configuration in preprocessor defines
+CFLAGS += -DUSE_LWIP_SOCKET_FOR_AZURE_IOT
+
+
+
+#COMPONENT_ADD_INCLUDEDIRS := sdk/iothub_client/inc sdk/c-utility/inc sdk/uamqp/inc
+
+#COMPONENT_SRCDIRS := sdk/iothub_client/src sdk/c-utility/src sdk/uamqp/src
+
+COMPONENT_ADD_INCLUDEDIRS := \
+pal \
+sdk/c-utility/inc \
+sdk/c-utility/inc/azure_c_shared_utility \
+sdk/c-utility/pal/inc \
+sdk/iothub_client/inc \
+sdk/umqtt/inc \
+sdk/umqtt/inc/azure_umqtt_c \
+sdk/parson
+
+COMPONENT_OBJS = \
+sdk/c-utility/src/xlogging.o \
+sdk/c-utility/src/buffer.o \
+sdk/c-utility/src/consolelogger.o \
+sdk/c-utility/src/constbuffer.o \
+sdk/c-utility/src/constmap.o \
+sdk/c-utility/src/crt_abstractions.o \
+sdk/c-utility/src/doublylinkedlist.o \
+sdk/c-utility/src/gballoc.o \
+sdk/c-utility/src/gb_stdio.o \
+sdk/c-utility/src/gb_time.o \
+sdk/c-utility/src/hmac.o \
+sdk/c-utility/src/hmacsha256.o \
+sdk/c-utility/src/httpapiex.o \
+sdk/c-utility/src/httpapiexsas.o \
+sdk/c-utility/src/httpheaders.o \
+sdk/c-utility/src/map.o \
+sdk/c-utility/src/optionhandler.o \
+sdk/c-utility/src/sastoken.o \
+sdk/c-utility/src/sha1.o \
+sdk/c-utility/src/sha224.o \
+sdk/c-utility/src/sha384-512.o \
+sdk/c-utility/src/strings.o \
+sdk/c-utility/src/string_tokenizer.o \
+sdk/c-utility/src/urlencode.o \
+sdk/c-utility/src/usha.o \
+sdk/c-utility/src/vector.o \
+sdk/c-utility/src/xio.o \
+sdk/c-utility/src/base64.o \
+\
+\
+sdk/iothub_client/src/iothub_client.o \
+sdk/iothub_client/src/iothub_client_ll.o \
+sdk/iothub_client/src/iothub_client_ll_uploadtoblob.o \
+sdk/iothub_client/src/iothub_client_authorization.o \
+sdk/iothub_client/src/iothub_client_retry_control.o \
+sdk/iothub_client/src/iothub_message.o \
+sdk/iothub_client/src/iothubtransport.o \
+sdk/iothub_client/src/iothubtransportmqtt.o \
+sdk/iothub_client/src/iothubtransport_mqtt_common.o \
+sdk/iothub_client/src/version.o \
+\
+\
+sdk/umqtt/src/mqtt_client.o \
+sdk/umqtt/src/mqtt_codec.o \
+sdk/umqtt/src/mqtt_message.o \
+\
+\
+\
+sdk/c-utility/adapters/agenttime.o \
+sdk/c-utility/src/singlylinkedlist.o \
+\
+\
+sdk/c-utility/pal/dns_async.o \
+sdk/c-utility/pal/socket_async.o \
+sdk/c-utility/pal/free_rtos/threadapi.o \
+sdk/c-utility/pal/free_rtos/tickcounter.o \
+sdk/c-utility/pal/lwip/sntp_lwip.o \
+\
+pal/platform_openssl_compact.o \
+pal/tlsio_openssl_compact.o
+
+COMPONENT_SRCDIRS := \
+pal \
+sdk/c-utility/pal \
+sdk/c-utility/pal/free_rtos \
+sdk/c-utility/pal/lwip \
+sdk/c-utility/src \
+sdk/c-utility/adapters \
+sdk/umqtt/src \
+sdk/iothub_client/src \
+sdk/parson \
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pal/platform_openssl_compact.c b/pal/platform_openssl_compact.c
new file mode 100644
index 0000000..b5b8df2
--- /dev/null
+++ b/pal/platform_openssl_compact.c
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include "azure_c_shared_utility/platform.h"
+#include "sntp.h"
+#include "tlsio_openssl_compact.h"
+
+static const char* const ntpServer = "pool.ntp.org";
+
+/* Codes_SRS_PLATFORM_OPENSSL_COMPACT_30_004: [ The platform_init shall initialize the tlsio adapter. ] */
+/* Codes_SRS_PLATFORM_OPENSSL_COMPACT_30_005: [ The platform_init shall initialize the sntp client. ] */
+int platform_init(void)
+{
+ // SNTP_SetServerName logs any necessary errors
+ int result = SNTP_SetServerName(ntpServer);
+ if (result == 0)
+ {
+ // SNTP_Init will have logged its own errors if necessary
+ result = SNTP_Init();
+ }
+ return result;
+}
+
+/* Codes_SRS_PLATFORM_OPENSSL_COMPACT_30_008: [ The platform_get_default_tlsio shall return a set of tlsio functions provided by the OpenSSL micro tlsio implementation. ] */
+const IO_INTERFACE_DESCRIPTION* platform_get_default_tlsio(void)
+{
+ return tlsio_openssl_compact_get_interface_description();
+}
+
+STRING_HANDLE platform_get_platform_info(void)
+{
+ return STRING_construct("(openssl_compact)");
+}
+
+/* Codes_SRS_PLATFORM_OPENSSL_COMPACT_30_006: [ The platform_deinit shall deinitialize the sntp client. ] */
+/* Codes_SRS_PLATFORM_OPENSSL_COMPACT_30_007: [ The platform_deinit shall deinitialize the tlsio adapter. ] */
+void platform_deinit(void)
+{
+ SNTP_Deinit();
+
+ // The tlsio adapter for this platform does not need (or support) deinitialization
+}
diff --git a/pal/sntp_os.h b/pal/sntp_os.h
new file mode 100644
index 0000000..72d7140
--- /dev/null
+++ b/pal/sntp_os.h
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+//This file pulls in OS-specific header files to allow compilation of socket_async.c under
+// most OS's except for Windows.
+
+// For ESP32 lwIP systems which use the ESP-IDF's non-standard lwIP include structure
+// Tested with:
+// ESP32
+
+#ifndef LWIP_SNTP_OS_H
+#define LWIP_SNTP_OS_H
+
+#include "apps/sntp/sntp.h"
+
+#endif // LWIP_SNTP_OS_H
\ No newline at end of file
diff --git a/pal/socket_async_os.h b/pal/socket_async_os.h
new file mode 100644
index 0000000..f91398e
--- /dev/null
+++ b/pal/socket_async_os.h
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+//This file pulls in OS-specific header files to allow compilation of socket_async.c under
+// most OS's except for Windows.
+
+// For lwIP systems
+// Tested with:
+// ESP32
+
+#ifndef SOCKET_ASYNC_OS_H
+#define SOCKET_ASYNC_OS_H
+
+#include "lwip/sockets.h"
+#include "lwip/netdb.h"
+
+#endif // SOCKET_ASYNC_OS_H
\ No newline at end of file
diff --git a/pal/tlsio_openssl_compact.c b/pal/tlsio_openssl_compact.c
new file mode 100644
index 0000000..691f1e9
--- /dev/null
+++ b/pal/tlsio_openssl_compact.c
@@ -0,0 +1,879 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include
+
+#include "openssl/ssl.h"
+
+#include
+#include
+#include
+#include "socket_async.h"
+#include "tlsio_openssl_compact.h"
+#include "dns_async.h"
+#include "azure_c_shared_utility/gballoc.h"
+#include "azure_c_shared_utility/tlsio.h"
+#include "azure_c_shared_utility/xlogging.h"
+#include "azure_c_shared_utility/agenttime.h"
+#include "azure_c_shared_utility/singlylinkedlist.h"
+#include "azure_c_shared_utility/crt_abstractions.h"
+
+typedef struct
+{
+ unsigned char* bytes;
+ size_t size;
+ size_t unsent_size;
+ ON_SEND_COMPLETE on_send_complete;
+ void* callback_context;
+} PENDING_TRANSMISSION;
+
+#define MAX_VALID_PORT 0xffff
+
+// The TLSIO_RECEIVE_BUFFER_SIZE has very little effect on performance, and is kept small
+// to minimize memory consumption.
+#define TLSIO_RECEIVE_BUFFER_SIZE 64
+
+
+typedef enum TLSIO_STATE_TAG
+{
+ TLSIO_STATE_CLOSED,
+ TLSIO_STATE_OPENING_WAITING_DNS,
+ TLSIO_STATE_OPENING_WAITING_SOCKET,
+ TLSIO_STATE_OPENING_WAITING_SSL,
+ TLSIO_STATE_OPEN,
+ TLSIO_STATE_ERROR,
+} TLSIO_STATE;
+
+bool is_an_opening_state(TLSIO_STATE state)
+{
+ return state == TLSIO_STATE_OPENING_WAITING_DNS ||
+ state == TLSIO_STATE_OPENING_WAITING_SOCKET ||
+ state == TLSIO_STATE_OPENING_WAITING_SSL;
+}
+
+// This structure definition is mirrored in the unit tests, so if you change
+// this struct, keep it in sync with the one in tlsio_openssl_compact_ut.c
+typedef struct TLS_IO_INSTANCE_TAG
+{
+ ON_BYTES_RECEIVED on_bytes_received;
+ ON_IO_ERROR on_io_error;
+ ON_IO_OPEN_COMPLETE on_open_complete;
+ void* on_bytes_received_context;
+ void* on_io_error_context;
+ void* on_open_complete_context;
+ SSL* ssl;
+ SSL_CTX* ssl_context;
+ TLSIO_STATE tlsio_state;
+ DNS_ASYNC_HANDLE dns;
+ char* hostname;
+ uint16_t port;
+ SOCKET_ASYNC_HANDLE sock;
+ SINGLYLINKEDLIST_HANDLE pending_transmission_list;
+} TLS_IO_INSTANCE;
+
+/* Codes_SRS_TLSIO_30_005: [ The phrase "enter TLSIO_STATE_EXT_ERROR" means the adapter shall call the on_io_error function and pass the on_io_error_context that was supplied in tlsio_open_async. ]*/
+static void enter_tlsio_error_state(TLS_IO_INSTANCE* tls_io_instance)
+{
+ if (tls_io_instance->tlsio_state != TLSIO_STATE_ERROR)
+ {
+ tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
+ tls_io_instance->on_io_error(tls_io_instance->on_io_error_context);
+ }
+}
+
+/* Codes_SRS_TLSIO_30_005: [ When the adapter enters TLSIO_STATE_EXT_ERROR it shall call the on_io_error function and pass the on_io_error_context that were supplied in tlsio_open . ]*/
+static void enter_open_error_state(TLS_IO_INSTANCE* tls_io_instance)
+{
+ enter_tlsio_error_state(tls_io_instance);
+ // on_open_complete has already been checked for non-NULL
+ tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_ERROR);
+}
+
+// Return true if a message was available to remove
+static bool process_and_destroy_head_message(TLS_IO_INSTANCE* tls_io_instance, IO_SEND_RESULT send_result)
+{
+ bool result;
+ LIST_ITEM_HANDLE head_pending_io;
+ if (send_result == IO_SEND_ERROR)
+ {
+ /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, the tlsio_dowork shall call the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/
+ enter_tlsio_error_state(tls_io_instance);
+ }
+ head_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_transmission_list);
+ if (head_pending_io != NULL)
+ {
+ PENDING_TRANSMISSION* head_message = (PENDING_TRANSMISSION*)singlylinkedlist_item_get_value(head_pending_io);
+ // on_send_complete is checked for NULL during PENDING_TRANSMISSION creation
+ /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, the tlsio_dowork shall call the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/
+ head_message->on_send_complete(head_message->callback_context, send_result);
+
+ free(head_message->bytes);
+ free(head_message);
+ if (singlylinkedlist_remove(tls_io_instance->pending_transmission_list, head_pending_io) != 0)
+ {
+ // This particular situation is a bizarre and unrecoverable internal error
+ /* Codes_SRS_TLSIO_30_094: [ If the send process encounters an internal error or calls on_send_complete with IO_SEND_ERROR due to either failure or timeout, it shall also call on_io_error and pass in the associated on_io_error_context. ]*/
+ enter_tlsio_error_state(tls_io_instance);
+ LogError("Failed to remove message from list");
+ }
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ return result;
+}
+
+static void internal_close(TLS_IO_INSTANCE* tls_io_instance)
+{
+ /* Codes_SRS_TLSIO_30_009: [ The phrase "enter TLSIO_STATE_EXT_CLOSING" means the adapter shall iterate through any unsent messages in the queue and shall delete each message after calling its on_send_complete with the associated callback_context and IO_SEND_CANCELLED. ]*/
+ /* Codes_SRS_TLSIO_30_006: [ The phrase "enter TLSIO_STATE_EXT_CLOSED" means the adapter shall forcibly close any existing connections then call the on_io_close_complete function and pass the on_io_close_complete_context that was supplied in tlsio_close_async. ]*/
+ /* Codes_SRS_TLSIO_30_051: [ On success, if the underlying TLS does not support asynchronous closing, then the adapter shall enter TLSIO_STATE_EXT_CLOSED immediately after entering TLSIO_STATE_EX_CLOSING. ]*/
+ if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN)
+ {
+ // From the OpenSSL manual pages: "According to the TLS standard, it is acceptable
+ // for an application to only send its shutdown alert and then close the
+ // underlying connection without waiting for the peer's response...". It goes
+ // on to say that waiting for shutdown only makes sense if the underlying
+ // connection is being re-used, which we do not do. So there's no need
+ // to wait for shutdown. The SSL_shutdown result is not logged because the
+ // return values are of no interest for unidirectional shutdown, which is
+ // what we use.
+ (void)SSL_shutdown(tls_io_instance->ssl);
+ }
+
+ if (tls_io_instance->dns != NULL)
+ {
+ dns_async_destroy(tls_io_instance->dns);
+ tls_io_instance->dns = NULL;
+ }
+ if (tls_io_instance->ssl != NULL)
+ {
+ SSL_free(tls_io_instance->ssl);
+ tls_io_instance->ssl = NULL;
+ }
+ if (tls_io_instance->ssl_context != NULL)
+ {
+ SSL_CTX_free(tls_io_instance->ssl_context);
+ tls_io_instance->ssl_context = NULL;
+ }
+ if (tls_io_instance->sock >= 0)
+ {
+ // The underlying socket API does not support waiting for close
+ // to complete, so it isn't possible to do so.
+ socket_async_destroy(tls_io_instance->sock);
+ tls_io_instance->sock = -1;
+ }
+
+ while (process_and_destroy_head_message(tls_io_instance, IO_SEND_CANCELLED));
+ // singlylinkedlist_destroy gets called in the main destroy
+
+ tls_io_instance->on_bytes_received = NULL;
+ tls_io_instance->on_io_error = NULL;
+ tls_io_instance->on_bytes_received_context = NULL;
+ tls_io_instance->on_io_error_context = NULL;
+ tls_io_instance->tlsio_state = TLSIO_STATE_CLOSED;
+ tls_io_instance->on_open_complete = NULL;
+ tls_io_instance->on_open_complete_context = NULL;
+}
+
+// This method tests for hard errors returned from either SSL_write or SSL_connect.
+// Returns
+// 0 for SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
+// The actual error for other errors (real failures)
+static int is_hard_ssl_error(SSL* ssl, int callReturn)
+{
+ int result = SSL_get_error(ssl, callReturn);
+ if (result == SSL_ERROR_WANT_READ || result == SSL_ERROR_WANT_WRITE)
+ {
+ result = 0;
+ }
+ return result;
+}
+
+static void tlsio_openssl_destroy(CONCRETE_IO_HANDLE tls_io)
+{
+ if (tls_io == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_020: [ If tlsio_handle is NULL, tlsio_destroy shall do nothing. ]*/
+ LogError("NULL tlsio");
+ }
+ else
+ {
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+ if (tls_io_instance->tlsio_state != TLSIO_STATE_CLOSED)
+ {
+ /* Codes_SRS_TLSIO_30_022: [ If the adapter is in any state other than TLSIO_STATE_EX_CLOSED when tlsio_destroy is called, the adapter shall enter TLSIO_STATE_EX_CLOSING and then enter TLSIO_STATE_EX_CLOSED before completing the destroy process. ]*/
+ LogError("tlsio_openssl_destroy called while not in TLSIO_STATE_CLOSED.");
+ internal_close(tls_io_instance);
+ }
+ /* Codes_SRS_TLSIO_30_021: [ The tlsio_destroy shall release all allocated resources and then release tlsio_handle. ]*/
+ if (tls_io_instance->hostname != NULL)
+ {
+ free(tls_io_instance->hostname);
+ }
+
+ if (tls_io_instance->pending_transmission_list != NULL)
+ {
+ /* Pending messages were cleared in internal_close */
+ singlylinkedlist_destroy(tls_io_instance->pending_transmission_list);
+ }
+
+ free(tls_io_instance);
+ }
+}
+
+/* Codes_SRS_TLSIO_30_010: [ The tlsio_create shall allocate and initialize all necessary resources and return an instance of the tlsio_openssl_compact. ]*/
+static CONCRETE_IO_HANDLE tlsio_openssl_create(void* io_create_parameters)
+{
+ TLS_IO_INSTANCE* result;
+
+ if (io_create_parameters == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_013: [ If the io_create_parameters value is NULL, tlsio_create shall log an error and return NULL. ]*/
+ LogError("NULL tls_io_config");
+ result = NULL;
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_012: [ The tlsio_create shall receive the connection configuration as a TLSIO_CONFIG* in io_create_parameters. ]*/
+ TLSIO_CONFIG* tls_io_config = (TLSIO_CONFIG*)io_create_parameters;
+ if (tls_io_config->hostname == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_014: [ If the hostname member of io_create_parameters value is NULL, tlsio_create shall log an error and return NULL. ]*/
+ LogError("NULL tls_io_config->hostname");
+ result = NULL;
+ }
+ else
+ {
+ if (tls_io_config->port < 0 || tls_io_config->port > MAX_VALID_PORT)
+ {
+ /* Codes_SRS_TLSIO_30_015: [ If the port member of io_create_parameters value is less than 0 or greater than 0xffff, tlsio_create shall log an error and return NULL. ]*/
+ LogError("tls_io_config->port out of range");
+ result = NULL;
+ }
+ else
+ {
+ result = malloc(sizeof(TLS_IO_INSTANCE));
+ if (result == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/
+ LogError("malloc failed");
+ }
+ else
+ {
+ int ms_result;
+ memset(result, 0, sizeof(TLS_IO_INSTANCE));
+ result->port = (uint16_t)tls_io_config->port;
+ result->tlsio_state = TLSIO_STATE_CLOSED;
+ result->sock = SOCKET_ASYNC_INVALID_SOCKET;
+ result->hostname = NULL;
+ result->dns = NULL;
+ result->pending_transmission_list = NULL;
+ /* Codes_SRS_TLSIO_30_016: [ tlsio_create shall make a copy of the hostname member of io_create_parameters to allow deletion of hostname immediately after the call. ]*/
+ ms_result = mallocAndStrcpy_s(&result->hostname, tls_io_config->hostname);
+ if (ms_result != 0)
+ {
+ /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/
+ LogError("malloc failed");
+ tlsio_openssl_destroy(result);
+ result = NULL;
+ }
+ else
+ {
+ // Create the message queue
+ result->pending_transmission_list = singlylinkedlist_create();
+ if (result->pending_transmission_list == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/
+ LogError("Failed singlylinkedlist_create");
+ tlsio_openssl_destroy(result);
+ result = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (CONCRETE_IO_HANDLE)result;
+}
+
+
+static int tlsio_openssl_open_async(CONCRETE_IO_HANDLE tls_io,
+ ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context,
+ ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context,
+ ON_IO_ERROR on_io_error, void* on_io_error_context)
+{
+
+ int result;
+ if (on_io_open_complete == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_031: [ If the on_io_open_complete parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/
+ LogError("Required parameter on_io_open_complete is NULL");
+ result = __FAILURE__;
+ }
+ else
+ {
+ if (tls_io == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_030: [ If the tlsio_handle parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("NULL tlsio");
+ }
+ else
+ {
+ if (on_bytes_received == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_032: [ If the on_bytes_received parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/
+ LogError("Required parameter on_bytes_received is NULL");
+ result = __FAILURE__;
+ }
+ else
+ {
+ if (on_io_error == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_033: [ If the on_io_error parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/
+ LogError("Required parameter on_io_error is NULL");
+ result = __FAILURE__;
+ }
+ else
+ {
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+
+ if (tls_io_instance->tlsio_state != TLSIO_STATE_CLOSED)
+ {
+ /* Codes_SRS_TLSIO_30_037: [ If the adapter is in any state other than TLSIO_STATE_EXT_CLOSED when tlsio_open is called, it shall log an error, and return FAILURE. ]*/
+ LogError("Invalid tlsio_state. Expected state is TLSIO_STATE_CLOSED.");
+ result = __FAILURE__;
+ }
+ else
+ {
+ tls_io_instance->dns = dns_async_create(tls_io_instance->hostname, NULL);
+ if (tls_io_instance->dns == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_038: [ If tlsio_open fails to enter TLSIO_STATE_EX_OPENING it shall return FAILURE. ]*/
+ LogError("dns_async_create failed");
+ result = __FAILURE__;
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_034: [ The tlsio_open shall store the provided on_bytes_received, on_bytes_received_context, on_io_error, on_io_error_context, on_io_open_complete, and on_io_open_complete_context parameters for later use as specified and tested per other line entries in this document. ]*/
+ tls_io_instance->on_bytes_received = on_bytes_received;
+ tls_io_instance->on_bytes_received_context = on_bytes_received_context;
+
+ tls_io_instance->on_io_error = on_io_error;
+ tls_io_instance->on_io_error_context = on_io_error_context;
+
+ tls_io_instance->on_open_complete = on_io_open_complete;
+ tls_io_instance->on_open_complete_context = on_io_open_complete_context;
+
+ /* Codes_SRS_TLSIO_30_035: [ On tlsio_open success the adapter shall enter TLSIO_STATE_EX_OPENING and return 0. ]*/
+ // All the real work happens in dowork
+ tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_WAITING_DNS;
+ result = 0;
+ }
+ }
+ }
+ }
+ }
+ /* Codes_SRS_TLSIO_30_039: [ On failure, tlsio_open_async shall not call on_io_open_complete. ]*/
+ }
+
+ return result;
+}
+
+// This implementation does not have asynchronous close, but uses the _async name for consistencty with the spec
+static int tlsio_openssl_close_async(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context)
+{
+ int result;
+
+ if (tls_io == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_050: [ If the tlsio_handle parameter is NULL, tlsio_openssl_close_async shall log an error and return FAILURE. ]*/
+ LogError("NULL tlsio");
+ result = __FAILURE__;
+ }
+ else
+ {
+ if (on_io_close_complete == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_055: [ If the on_io_close_complete parameter is NULL, tlsio_openssl_close_async shall log an error and return FAILURE. ]*/
+ LogError("NULL on_io_close_complete");
+ result = __FAILURE__;
+ }
+ else
+ {
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+
+ if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN &&
+ tls_io_instance->tlsio_state != TLSIO_STATE_ERROR)
+ {
+ /* Codes_SRS_TLSIO_30_053: [ If the adapter is in any state other than TLSIO_STATE_EXT_OPEN or TLSIO_STATE_EXT_ERROR then tlsio_close_async shall log that tlsio_close_async has been called and then continue normally. ]*/
+ // LogInfo rather than LogError because this is an unusual but not erroneous situation
+ LogInfo("tlsio_openssl_close has been called when in neither TLSIO_STATE_OPEN nor TLSIO_STATE_ERROR.");
+ }
+
+ if (is_an_opening_state(tls_io_instance->tlsio_state))
+ {
+ /* Codes_SRS_TLSIO_30_057: [ On success, if the adapter is in TLSIO_STATE_EXT_OPENING, it shall call on_io_open_complete with the on_io_open_complete_context supplied in tlsio_open_async and IO_OPEN_CANCELLED. This callback shall be made before changing the internal state of the adapter. ]*/
+ tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_CANCELLED);
+ }
+ // This adapter does not support asynchronous closing
+ /* Codes_SRS_TLSIO_30_056: [ On success the adapter shall enter TLSIO_STATE_EX_CLOSING. ]*/
+ /* Codes_SRS_TLSIO_30_051: [ On success, if the underlying TLS does not support asynchronous closing, then the adapter shall enter TLSIO_STATE_EX_CLOSED immediately after entering TLSIO_STATE_EX_CLOSING. ]*/
+ /* Codes_SRS_TLSIO_30_052: [ On success tlsio_close shall return 0. ]*/
+ internal_close(tls_io_instance);
+ on_io_close_complete(callback_context);
+ result = 0;
+ }
+ }
+ /* Codes_SRS_TLSIO_30_054: [ On failure, the adapter shall not call on_io_close_complete. ]*/
+
+ return result;
+}
+
+static int tlsio_openssl_send_async(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context)
+{
+ int result;
+ if (on_send_complete == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_062: [ If the on_send_complete is NULL, tlsio_openssl_compact_send shall log the error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("NULL on_send_complete");
+ }
+ else
+ {
+ if (tls_io == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_060: [ If the tlsio_handle parameter is NULL, tlsio_openssl_compact_send shall log an error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("NULL tlsio");
+ }
+ else
+ {
+ if (buffer == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_061: [ If the buffer is NULL, tlsio_openssl_compact_send shall log the error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("NULL buffer");
+ }
+ else
+ {
+ if (size == 0)
+ {
+ /* Codes_SRS_TLSIO_30_067: [ If the size is 0, tlsio_send shall log the error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("0 size");
+ }
+ else
+ {
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+ if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN)
+ {
+ /* Codes_SRS_TLSIO_30_065: [ If tlsio_openssl_compact_open has not been called or the opening process has not been completed, tlsio_openssl_compact_send shall log an error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("tlsio_openssl_send_async without a prior successful open");
+ }
+ else
+ {
+ PENDING_TRANSMISSION* pending_transmission = (PENDING_TRANSMISSION*)malloc(sizeof(PENDING_TRANSMISSION));
+ if (pending_transmission == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_openssl_compact_send shall log an error and return FAILURE. ]*/
+ result = __FAILURE__;
+ LogError("malloc failed");
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_063: [ The tlsio_openssl_compact_send shall enqueue for transmission the on_send_complete, the callback_context, the size, and the contents of buffer. ]*/
+ pending_transmission->bytes = (unsigned char*)malloc(size);
+
+ if (pending_transmission->bytes == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_openssl_compact_send shall log an error and return FAILURE. ]*/
+ LogError("malloc failed");
+ free(pending_transmission);
+ result = __FAILURE__;
+ }
+ else
+ {
+ pending_transmission->size = size;
+ pending_transmission->unsent_size = size;
+ pending_transmission->on_send_complete = on_send_complete;
+ pending_transmission->callback_context = callback_context;
+ (void)memcpy(pending_transmission->bytes, buffer, size);
+
+ if (singlylinkedlist_add(tls_io_instance->pending_transmission_list, pending_transmission) == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_openssl_compact_send shall log an error and return FAILURE. ]*/
+ LogError("Unable to add socket to pending list.");
+ free(pending_transmission->bytes);
+ free(pending_transmission);
+ result = __FAILURE__;
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_063: [ On success, tlsio_send shall enqueue for transmission the on_send_complete , the callback_context , the size , and the contents of buffer and then return 0. ]*/
+ result = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ /* Codes_SRS_TLSIO_30_066: [ On failure, on_send_complete shall not be called. ]*/
+ }
+ return result;
+}
+
+static void dowork_read(TLS_IO_INSTANCE* tls_io_instance)
+{
+ // TRANSFER_BUFFER_SIZE is not very important because if the message is bigger
+ // then the framework just calls dowork repeatedly until it gets everything. So
+ // a bigger buffer would just use memory without buying anything.
+ // Putting this buffer in a small function also allows it to exist on the stack
+ // rather than adding to heap fragmentation.
+ unsigned char buffer[TLSIO_RECEIVE_BUFFER_SIZE];
+ int rcv_bytes;
+
+ if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN)
+ {
+ // SSL_read is not checked for errors because the "no data" condition is reported as a
+ // failure, but the docs do not guarantee that it will always be the same failure,
+ // so we have no reliable way to distinguish "no data" from something else.
+ //
+ // Pump all of the bytes currently available out
+ rcv_bytes = SSL_read(tls_io_instance->ssl, buffer, sizeof(buffer));
+ while (rcv_bytes > 0)
+ {
+ // tls_io_instance->on_bytes_received was already checked for NULL
+ // in the call to tlsio_openssl_open_async
+ /* Codes_SRS_TLSIO_30_100: [ As long as the TLS connection is able to provide received data, tlsio_dowork shall repeatedly read this data and call on_bytes_received with the pointer to the buffer containing the data, the number of bytes received, and the on_bytes_received_context. ]*/
+ tls_io_instance->on_bytes_received(tls_io_instance->on_bytes_received_context, buffer, rcv_bytes);
+ rcv_bytes = SSL_read(tls_io_instance->ssl, buffer, sizeof(buffer));
+ }
+ /* Codes_SRS_TLSIO_30_102: [ If the TLS connection receives no data then tlsio_dowork shall not call the on_bytes_received callback. ]*/
+ }
+}
+
+
+static int create_ssl(TLS_IO_INSTANCE* tls_io_instance)
+{
+ int result;
+ int ret;
+
+ tls_io_instance->ssl_context = SSL_CTX_new(TLSv1_2_client_method());
+ if (tls_io_instance->ssl_context == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_082: [ If the connection process fails for any reason, tlsio_dowork shall log an error, call on_io_open_complete with the on_io_open_complete_context parameter provided in tlsio_open and IO_OPEN_ERROR, and enter TLSIO_STATE_EX_ERROR. ]*/
+ result = __FAILURE__;
+ LogError("create new SSL CTX failed");
+ }
+ else
+ {
+ tls_io_instance->ssl = SSL_new(tls_io_instance->ssl_context);
+ if (tls_io_instance->ssl == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_082: [ If the connection process fails for any reason, tlsio_dowork shall log an error, call on_io_open_complete with the on_io_open_complete_context parameter provided in tlsio_open and IO_OPEN_ERROR, and enter TLSIO_STATE_EX_ERROR. ]*/
+ result = __FAILURE__;
+ LogError("SSL_new failed");
+ }
+ else
+ {
+ // returns 1 on success
+ ret = SSL_set_fd(tls_io_instance->ssl, tls_io_instance->sock);
+ if (ret != 1)
+ {
+ /* Codes_SRS_TLSIO_30_082: [ If the connection process fails for any reason, tlsio_dowork shall log an error, call on_io_open_complete with the on_io_open_complete_context parameter provided in tlsio_open and IO_OPEN_ERROR, and enter TLSIO_STATE_EX_ERROR. ]*/
+ result = __FAILURE__;
+ LogError("SSL_set_fd failed");
+ }
+ else
+ {
+ result = 0;
+ }
+ }
+ }
+
+ return result;
+}
+
+static void dowork_send(TLS_IO_INSTANCE* tls_io_instance)
+{
+ LIST_ITEM_HANDLE first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_transmission_list);
+ if (first_pending_io != NULL)
+ {
+ PENDING_TRANSMISSION* pending_message = (PENDING_TRANSMISSION*)singlylinkedlist_item_get_value(first_pending_io);
+ uint8_t* buffer = ((uint8_t*)pending_message->bytes) +
+ pending_message->size - pending_message->unsent_size;
+ int write_result = SSL_write(tls_io_instance->ssl, buffer, pending_message->unsent_size);
+ // https://wiki.openssl.org/index.php/Manual:SSL_write(3)
+
+ if (write_result > 0)
+ {
+ pending_message->unsent_size -= write_result;
+ if (pending_message->unsent_size == 0)
+ {
+ /* Codes_SRS_TLSIO_30_091: [ If tlsio_openssl_compact_dowork is able to send all the bytes in an enqueued message, it shall call the messages's on_send_complete along with its associated callback_context and IO_SEND_OK. ]*/
+ // The whole message has been sent successfully
+ process_and_destroy_head_message(tls_io_instance, IO_SEND_OK);
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_093: [ If the TLS connection was not able to send an entire enqueued message at once, subsequent calls to tlsio_dowork shall continue to send the remaining bytes. ]*/
+ // Repeat the send on the next pass with the rest of the message
+ // This empty else compiles to nothing but helps readability
+ }
+ }
+ else
+ {
+ // SSL_write returned non-success. It may just be busy, or it may be broken.
+ int hard_error = is_hard_ssl_error(tls_io_instance->ssl, write_result);
+ if (hard_error != 0)
+ {
+ /* Codes_SRS_TLSIO_30_002: [ The phrase "destroy the failed message" means that the adapter shall remove the message from the queue and destroy it after calling the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/
+ /* Codes_SRS_TLSIO_30_005: [ When the adapter enters TLSIO_STATE_EXT_ERROR it shall call the on_io_error function and pass the on_io_error_context that were supplied in tlsio_open . ]*/
+ /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, tlsio_dowork shall destroy the failed message and enter TLSIO_STATE_EX_ERROR. ]*/
+ // This is an unexpected error, and we need to bail out. Probably lost internet connection.
+ LogInfo("Error from SSL_write: %d", hard_error);
+ process_and_destroy_head_message(tls_io_instance, IO_SEND_ERROR);
+ }
+ }
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_096: [ If there are no enqueued messages available, tlsio_openssl_compact_dowork shall do nothing. ]*/
+ }
+}
+
+static void dowork_poll_dns(TLS_IO_INSTANCE* tls_io_instance)
+{
+ bool dns_is_complete = dns_async_is_lookup_complete(tls_io_instance->dns);
+
+ if (dns_is_complete)
+ {
+ uint32_t host_ipV4_address = dns_async_get_ipv4(tls_io_instance->dns);
+ dns_async_destroy(tls_io_instance->dns);
+ tls_io_instance->dns = NULL;
+ if (host_ipV4_address == 0)
+ {
+ // Transition to TSLIO_STATE_ERROR
+ /* Codes_SRS_TLSIO_30_082: [ If the connection process fails for any reason, tlsio_dowork shall log an error, call on_io_open_complete with the on_io_open_complete_context parameter provided in tlsio_open and IO_OPEN_ERROR, and enter TLSIO_STATE_EX_ERROR. ]*/
+ // The DNS failure has already been logged
+ enter_open_error_state(tls_io_instance);
+ }
+ else
+ {
+ SOCKET_ASYNC_HANDLE sock = socket_async_create(host_ipV4_address, tls_io_instance->port, false, NULL);
+ if (sock < 0)
+ {
+ // This is a communication interruption rather than a program bug
+ /* Codes_SRS_TLSIO_30_082: [ If the connection process fails for any reason, tlsio_dowork shall log an error, call on_io_open_complete with the on_io_open_complete_context parameter provided in tlsio_open and IO_OPEN_ERROR, and enter TLSIO_STATE_EX_ERROR. ]*/
+ LogInfo("Could not open the socket");
+ enter_open_error_state(tls_io_instance);
+ }
+ else
+ {
+ // The socket has been created successfully, so now wait for it to
+ // finish the TCP handshake.
+ tls_io_instance->sock = sock;
+ tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_WAITING_SOCKET;
+ }
+ }
+ }
+}
+
+static void dowork_poll_socket(TLS_IO_INSTANCE* tls_io_instance)
+{
+ bool is_complete;
+ int result = socket_async_is_create_complete(tls_io_instance->sock, &is_complete);
+ if (result != 0)
+ {
+ // Transition to TSLIO_STATE_ERROR
+ LogInfo("socket_async_is_create_complete failure");
+ enter_open_error_state(tls_io_instance);
+ }
+ else
+ {
+ if (is_complete)
+ {
+ // Attempt to transition to TLSIO_STATE_OPENING_WAITING_SSL
+ int create_ssl_result = create_ssl(tls_io_instance);
+ if (create_ssl_result != 0)
+ {
+ // Transition to TSLIO_STATE_ERROR
+ // create_ssl already did error logging
+ enter_open_error_state(tls_io_instance);
+ }
+ else
+ {
+ tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_WAITING_SSL;
+ }
+ }
+ }
+}
+
+static void dowork_poll_open_ssl(TLS_IO_INSTANCE* tls_io_instance)
+{
+ // https://www.openssl.org/docs/man1.0.2/ssl/SSL_connect.html
+
+ // "If the underlying BIO is non - blocking, SSL_connect() will also
+ // return when the underlying BIO could not satisfy the needs of
+ // SSL_connect() to continue the handshake, indicating the
+ // problem by the return value -1. In this case a call to
+ // SSL_get_error() with the return value of SSL_connect() will
+ // yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.The calling
+ // process then must repeat the call after taking appropriate
+ // action to satisfy the needs of SSL_connect().The action
+ // depends on the underlying BIO. When using a non - blocking
+ // socket, nothing is to be done, but select() can be used to
+ // check for the required condition."
+
+ int connect_result = SSL_connect(tls_io_instance->ssl);
+
+ // The following note applies to the Espressif ESP32 implementation
+ // of OpenSSL:
+ // The manual pages say that 0 is a failure,
+ // but by experiment, 0 is the success result, at least when using
+ // SSL_set_fd instead of custom BIO.
+ // https://www.openssl.org/docs/man1.0.2/ssl/SSL_connect.html
+ if (connect_result == 1 || connect_result == 0)
+ {
+ /* Codes_SRS_TLSIO_30_080: [ The tlsio_dowork shall establish a TLS connection using the hostName and port provided during tlsio_open. ]*/
+ // Connect succeeded
+ tls_io_instance->tlsio_state = TLSIO_STATE_OPEN;
+ /* Codes_SRS_TLSIO_30_007: [ The phrase "enter TLSIO_STATE_EXT_OPEN" means the adapter shall call the on_io_open_complete function and pass IO_OPEN_OK and the on_io_open_complete_context that was supplied in tlsio_open . ]*/
+ /* Codes_SRS_TLSIO_30_083: [ If tlsio_dowork successfully opens the TLS connection it shall enter TLSIO_STATE_EX_OPEN. ]*/
+ tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_OK);
+ }
+ else
+ {
+ int hard_error = is_hard_ssl_error(tls_io_instance->ssl, connect_result);
+ if (hard_error != 0)
+ {
+ LogInfo("Hard error from SSL_connect: %d", hard_error);
+ enter_open_error_state(tls_io_instance);
+ }
+ }
+}
+
+static void tlsio_openssl_dowork(CONCRETE_IO_HANDLE tls_io)
+{
+ if (tls_io == NULL)
+ {
+ /* Codes_SRS_TLSIO_30_070: [ If the tlsio_handle parameter is NULL, tlsio_dowork shall do nothing except log an error. ]*/
+ LogError("NULL tlsio");
+ }
+ else
+ {
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+
+ // This switch statement handles all of the state transitions during the opening process
+ switch (tls_io_instance->tlsio_state)
+ {
+ case TLSIO_STATE_CLOSED:
+ /* Codes_SRS_TLSIO_30_075: [ If the adapter is in TLSIO_STATE_EXT_CLOSED then tlsio_dowork shall do nothing. ]*/
+ // Waiting to be opened, nothing to do
+ break;
+ case TLSIO_STATE_OPENING_WAITING_DNS:
+ //LogInfo("dowork_poll_dns");
+ dowork_poll_dns(tls_io_instance);
+ break;
+ case TLSIO_STATE_OPENING_WAITING_SOCKET:
+ //LogInfo("dowork_poll_socket");
+ dowork_poll_socket(tls_io_instance);
+ break;
+ case TLSIO_STATE_OPENING_WAITING_SSL:
+ //LogInfo("dowork_poll_ssl");
+ dowork_poll_open_ssl(tls_io_instance);
+ break;
+ case TLSIO_STATE_OPEN:
+ dowork_read(tls_io_instance);
+ dowork_send(tls_io_instance);
+ break;
+ case TLSIO_STATE_ERROR:
+ /* Codes_SRS_TLSIO_30_071: [ If the adapter is in TLSIO_STATE_EXT_ERROR then tlsio_dowork shall do nothing. ]*/
+ // There's nothing valid to do here but wait to be retried
+ break;
+ default:
+ LogError("Unexpected internal tlsio state");
+ break;
+ }
+ }
+}
+
+static int tlsio_openssl_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
+{
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+ /* Codes_SRS_TLSIO_30_120: [ If the tlsio_handle parameter is NULL, tlsio_openssl_compact_setoption shall do nothing except log an error and return FAILURE. ]*/
+ int result;
+ if (tls_io_instance == NULL)
+ {
+ LogError("NULL tlsio");
+ result = __FAILURE__;
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_121: [ If the optionName parameter is NULL, tlsio_openssl_compact_setoption shall do nothing except log an error and return FAILURE. ]*/
+ if (optionName == NULL)
+ {
+ LogError("Required optionName parameter is NULL");
+ result = __FAILURE__;
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_30_122: [ If the value parameter is NULL, tlsio_openssl_compact_setoption shall do nothing except log an error and return FAILURE. ]*/
+ if (value == NULL)
+ {
+ LogError("Required value parameter is NULL");
+ result = __FAILURE__;
+ }
+ else
+ {
+ /* Codes_SRS_TLSIO_OPENSSL_COMPACT_30_520 [ The tlsio_setoption shall do nothing and return 0. ]*/
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+/* Codes_SRS_TLSIO_OPENSSL_COMPACT_30_560: [ The tlsio_retrieveoptions shall do nothing and return NULL. ]*/
+static OPTIONHANDLER_HANDLE tlsio_openssl_retrieveoptions(CONCRETE_IO_HANDLE tls_io)
+{
+ TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
+ /* Codes_SRS_TLSIO_30_160: [ If the tlsio_handle parameter is NULL, tlsio_openssl_compact_retrieveoptions shall do nothing except log an error and return FAILURE. ]*/
+ OPTIONHANDLER_HANDLE result;
+ if (tls_io_instance == NULL)
+ {
+ LogError("NULL tlsio");
+ result = NULL;
+ }
+ else
+ {
+ result = NULL;
+ }
+ return result;
+}
+
+/* Codes_SRS_TLSIO_30_008: [ The tlsio_get_interface_description shall return the VTable IO_INTERFACE_DESCRIPTION. ]*/
+static const IO_INTERFACE_DESCRIPTION tlsio_openssl_interface_description =
+{
+ tlsio_openssl_retrieveoptions,
+ tlsio_openssl_create,
+ tlsio_openssl_destroy,
+ tlsio_openssl_open_async,
+ tlsio_openssl_close_async,
+ tlsio_openssl_send_async,
+ tlsio_openssl_dowork,
+ tlsio_openssl_setoption
+};
+
+/* Codes_SRS_TLSIO_30_001: [ The tlsio_openssl_compact shall implement and export all the Concrete functions in the VTable IO_INTERFACE_DESCRIPTION defined in the xio.h. ]*/
+const IO_INTERFACE_DESCRIPTION* tlsio_openssl_compact_get_interface_description(void)
+{
+ return &tlsio_openssl_interface_description;
+}
diff --git a/pal/tlsio_openssl_compact.h b/pal/tlsio_openssl_compact.h
new file mode 100644
index 0000000..9f29ed1
--- /dev/null
+++ b/pal/tlsio_openssl_compact.h
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef TLSIO_OPENSSL_COMPACT_H
+#define TLSIO_OPENSSL_COMPACT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "azure_c_shared_utility/tlsio.h"
+
+ MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, tlsio_openssl_compact_get_interface_description);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* TLSIO_OPENSSL_COMPACT_H */
diff --git a/sample/Makefile b/sample/Makefile
new file mode 100644
index 0000000..9b28497
--- /dev/null
+++ b/sample/Makefile
@@ -0,0 +1,8 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := esp32
+
+include $(IDF_PATH)/make/project.mk
diff --git a/sample/main/Kconfig.projbuild b/sample/main/Kconfig.projbuild
new file mode 100644
index 0000000..02ac330
--- /dev/null
+++ b/sample/main/Kconfig.projbuild
@@ -0,0 +1,17 @@
+menu "Example Configuration"
+
+config WIFI_SSID
+ string "WiFi SSID"
+ default "myssid"
+ help
+ SSID (network name) for the example to connect to.
+
+config WIFI_PASSWORD
+ string "WiFi Password"
+ default "myssid"
+ help
+ WiFi password (WPA or WPA2) for the example to use.
+
+ Can be left blank if the network has no security set.
+
+endmenu
diff --git a/sample/main/azure_main.c b/sample/main/azure_main.c
new file mode 100644
index 0000000..2afc93d
--- /dev/null
+++ b/sample/main/azure_main.c
@@ -0,0 +1,97 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+
+#include "esp_system.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+
+#include "nvs_flash.h"
+#include "iothub_client_sample_mqtt.h"
+
+
+
+#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
+#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected & ready to make a request */
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+ but we only care about one event - are we connected
+ to the AP with an IP? */
+const int CONNECTED_BIT = BIT0;
+
+static const char *TAG = "azure";
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+ switch(event->event_id) {
+ case SYSTEM_EVENT_STA_START:
+ esp_wifi_connect();
+ break;
+ case SYSTEM_EVENT_STA_GOT_IP:
+ xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
+ break;
+ case SYSTEM_EVENT_STA_DISCONNECTED:
+ /* This is a workaround as ESP32 WiFi libs don't currently
+ auto-reassociate. */
+ esp_wifi_connect();
+ xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
+ break;
+ default:
+ break;
+ }
+ return ESP_OK;
+}
+
+static void initialise_wifi(void)
+{
+ tcpip_adapter_init();
+ wifi_event_group = xEventGroupCreate();
+ ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
+ ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
+ wifi_config_t wifi_config = {
+ .sta = {
+ .ssid = EXAMPLE_WIFI_SSID,
+ .password = EXAMPLE_WIFI_PASS,
+ },
+ };
+ ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
+ ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
+ ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+ ESP_ERROR_CHECK( esp_wifi_start() );
+}
+
+void azure_task(void *pvParameter)
+{
+ ESP_LOGI(TAG, "Waiting for WiFi access point ...");
+ xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
+ false, true, portMAX_DELAY);
+ ESP_LOGI(TAG, "Connected to access point success");
+
+
+ iothub_client_sample_mqtt_run();
+ while(1)
+ {
+ vTaskDelay(1000);
+ }
+
+
+}
+
+void app_main()
+{
+ nvs_flash_init();
+ initialise_wifi();
+ xTaskCreate(&azure_task, "azure_task", 8192, NULL, 5, NULL);
+}
diff --git a/sample/main/component.mk b/sample/main/component.mk
new file mode 100644
index 0000000..0b9d758
--- /dev/null
+++ b/sample/main/component.mk
@@ -0,0 +1,5 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
diff --git a/sdk b/sdk
new file mode 160000
index 0000000..04aa51b
--- /dev/null
+++ b/sdk
@@ -0,0 +1 @@
+Subproject commit 04aa51bf78ce819b6ebfda448f8c3a57c9c42b99