diff --git a/examples/zephyr/Makefile b/examples/zephyr/Makefile deleted file mode 100644 index 55643c81..00000000 --- a/examples/zephyr/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -CWD = $(realpath $(CURDIR)) -ZEPHYR_DIR ?= $(realpath ../../../zephyrproject) - -example: - true - -build: - cp ../../mongoose.c ../../mongoose.h http-server/src/ - cd $(ZEPHYR_DIR) && west build -b nucleo_f746zg -p auto $(CWD)/http-server - -flash: - cd $(ZEPHYR_DIR) && west flash - -clean: - rm -rf */*/mongoose.* diff --git a/examples/zephyr/http-client/CMakeLists.txt b/examples/zephyr/http-client/CMakeLists.txt new file mode 100644 index 00000000..a64e924d --- /dev/null +++ b/examples/zephyr/http-client/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2022 Cesanta Software Limited +# Mail: support@cesanta.com +# +# SPDX-License-Identifier: GPL2.0 or commercial + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(http_server) +#add_definitions(-DMG_ENABLE_LINES=1) +add_definitions(-DMG_ENABLE_MBEDTLS=1 -DMG_MBEDTLS_DEBUG_LEVEL=4) +target_sources(app PRIVATE src/main.c src/mongoose.c) diff --git a/examples/zephyr/http-client/Makefile b/examples/zephyr/http-client/Makefile new file mode 100644 index 00000000..1e56e8c0 --- /dev/null +++ b/examples/zephyr/http-client/Makefile @@ -0,0 +1,16 @@ +CWD = $(realpath $(CURDIR)) +ZEPHYR_DIR ?= $(realpath ../../../../zephyrproject) +BOARD ?= nucleo_h743zi + +example: + true + +build: + cp ../../../mongoose.c ../../../mongoose.h src/ + cd $(ZEPHYR_DIR) && west build -b $(BOARD) -p auto $(CWD) + +flash: + cd $(ZEPHYR_DIR) && west flash + +clean: + rm -rf */*/mongoose.* diff --git a/examples/zephyr/http-client/prj.conf b/examples/zephyr/http-client/prj.conf new file mode 100644 index 00000000..2622e4df --- /dev/null +++ b/examples/zephyr/http-client/prj.conf @@ -0,0 +1,32 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POLL_MAX=32 +CONFIG_POSIX_MAX_FDS=32 +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_LOG=y + +CONFIG_LOG=y +CONFIG_ISR_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=16000 +CONFIG_IDLE_STACK_SIZE=1024 + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=8192 +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_ECP_ALL_ENABLED=y +CONFIG_MBEDTLS_TLS_VERSION_1_2=y + +CONFIG_MINIMAL_LIBC_RAND=y +CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=131072 + +CONFIG_FILE_SYSTEM_LITTLEFS=y + +CONFIG_PRINTK=y +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=4 diff --git a/examples/zephyr/http-client/src/certs.h b/examples/zephyr/http-client/src/certs.h new file mode 100644 index 00000000..bc10e9eb --- /dev/null +++ b/examples/zephyr/http-client/src/certs.h @@ -0,0 +1,87 @@ +#ifndef CERTS_H_ +#define CERTS_H_ + +static const char *s_ca = +"-\n" +"Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA\n" +"Not Before: Nov 10 00:00:00 2006 GMT\n" +"Not After : Nov 10 00:00:00 2031 GMT\n" +"-----BEGIN CERTIFICATE-----\n" +"MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs\n" +"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" +"d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" +"ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL\n" +"MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" +"LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n" +"RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm\n" +"+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW\n" +"PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n" +"xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB\n" +"Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3\n" +"hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg\n" +"EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF\n" +"MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA\n" +"FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec\n" +"nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z\n" +"eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF\n" +"hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n" +"Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\n" +"vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep\n" +"+OkuE6N36B9K\n" +"-----END CERTIFICATE-----\n" +"\n" +"Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA\n" +"Not Before: Nov 10 00:00:00 2006 GMT\n" +"Not After : Nov 10 00:00:00 2031 GMT\n" +"-----BEGIN CERTIFICATE-----\n" +"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" +"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" +"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" +"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" +"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" +"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" +"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" +"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" +"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" +"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" +"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" +"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" +"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" +"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" +"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" +"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" +"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" +"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" +"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" +"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" +"-----END CERTIFICATE-----\n" +"\n" +"Not Before: Nov 10 00:00:00 2006 GMT\n" +"Not After : Nov 10 00:00:00 2031 GMT\n" +"Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA\n" +"-----BEGIN CERTIFICATE-----\n" +"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" +"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" +"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" +"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" +"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" +"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" +"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" +"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" +"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" +"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" +"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" +"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" +"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" +"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" +"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" +"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" +"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" +"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" +"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" +"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" +"-----END CERTIFICATE-----\n" +"\n" +""; + +#endif diff --git a/examples/zephyr/http-client/src/main.c b/examples/zephyr/http-client/src/main.c new file mode 100644 index 00000000..76508660 --- /dev/null +++ b/examples/zephyr/http-client/src/main.c @@ -0,0 +1,112 @@ +// Copyright (c) 2020 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" +#include "certs.h" + +static const char *s_debug_level = "3"; +static time_t s_boot_timestamp = 0; +static struct mg_connection *s_sntp_conn = NULL; +static const char *s_url = "https://example.org/"; +static const char *s_post_data = NULL; // POST data +static const int64_t s_timeout_ms = 1500; // Connect timeout in milliseconds +struct mg_mgr s_mgr; +static int s_connected = 0; + +// Print HTTP response and signal that we're done +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_OPEN) { + // Connection created. Store connect expiration time in c->label + *(int64_t *) c->label = mg_millis() + s_timeout_ms; + } else if (ev == MG_EV_POLL) { + if (mg_millis() > *(int64_t *) c->label && + (c->is_connecting || c->is_resolving)) { + mg_error(c, "Connect timeout"); + } + } else if (ev == MG_EV_CONNECT) { + // Connected to server. Extract host name from URL + struct mg_str host = mg_url_host(s_url); + + // If s_url is https://, tell client connection to use TLS + if (mg_url_is_ssl(s_url)) { + struct mg_tls_opts opts = {.ca = s_ca, .srvname = host }; + mg_tls_init(c, &opts); + } + + // Send request + int content_length = s_post_data ? strlen(s_post_data) : 0; + mg_printf(c, + "%s %s HTTP/1.0\r\n" + "Host: %.*s\r\n" + "Content-Type: octet-stream\r\n" + "Content-Length: %d\r\n" + "\r\n", + s_post_data ? "POST" : "GET", mg_url_uri(s_url), (int) host.len, + host.ptr, content_length); + mg_send(c, s_post_data, content_length); + } else if (ev == MG_EV_HTTP_MSG) { + // Response is received. Print it + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + MG_INFO(("%.*s", (int) hm->message.len, hm->message.ptr)); + c->is_closing = 1; // Tell mongoose to close this connection + *(bool *) fn_data = true; // Tell event loop to stop + } else if (ev == MG_EV_ERROR) { + *(bool *) fn_data = true; // Error, tell event loop to stop + } +} + +// We have no valid system time(), and we need it for TLS. Implement it +time_t time(time_t *tp) { + time_t t = s_boot_timestamp + k_uptime_get() / 1000; + if (tp != NULL) *tp = t; + return t; +} + +// SNTP callback. Modifies s_boot_timestamp, to make time() correct +static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_SNTP_TIME) { + int64_t t = *(int64_t *) ev_data; + MG_INFO(("Got SNTP time: %lld ms from epoch", t)); + s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000); + + // We need correct time in order to get HTTPs working, therefore, + // making https request from SMTP callback + if(!s_connected) { + MG_INFO(("Connecting to : [%s]", s_url)); + mg_http_connect(&s_mgr, s_url, fn, NULL); // Create client connection + s_connected = 1; + } + } else if (ev == MG_EV_CLOSE) { + s_sntp_conn = NULL; + } +} + +// Periodic timer syncs time via SNTP +static void timer_fn(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; + if (s_sntp_conn == NULL) s_sntp_conn = mg_sntp_connect(mgr, NULL, sfn, NULL); + if (s_boot_timestamp < 9999) mg_sntp_send(s_sntp_conn, time(NULL)); +} + +// Use Zephyr's printk() for Mongooose MG_* logging +static void logfn(const void *ptr, size_t len, void *userdata) { + printk("%.*s", (int) len, (char *) ptr); +} + +int main(int argc, char *argv[]) { + mg_log_set(s_debug_level); + mg_log_set_callback(logfn, NULL); + + mg_mgr_init(&s_mgr); + + struct mg_timer t; + mg_timer_init(&t, 5000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &s_mgr); + + // Start infinite event loop + MG_INFO(("Mongoose version : v%s", MG_VERSION)); + + for (;;) mg_mgr_poll(&s_mgr, 1000); + mg_mgr_free(&s_mgr); + + return 0; +} diff --git a/examples/zephyr/http-server/CMakeLists.txt b/examples/zephyr/http-server/CMakeLists.txt index f385d74e..ca982051 100644 --- a/examples/zephyr/http-server/CMakeLists.txt +++ b/examples/zephyr/http-server/CMakeLists.txt @@ -6,6 +6,5 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(http_server) -#add_definitions(-DMG_ENABLE_LINES=1) add_definitions(-DMG_ENABLE_MBEDTLS=1) target_sources(app PRIVATE src/main.c src/mongoose.c) diff --git a/examples/zephyr/http-server/Makefile b/examples/zephyr/http-server/Makefile new file mode 100644 index 00000000..1e56e8c0 --- /dev/null +++ b/examples/zephyr/http-server/Makefile @@ -0,0 +1,16 @@ +CWD = $(realpath $(CURDIR)) +ZEPHYR_DIR ?= $(realpath ../../../../zephyrproject) +BOARD ?= nucleo_h743zi + +example: + true + +build: + cp ../../../mongoose.c ../../../mongoose.h src/ + cd $(ZEPHYR_DIR) && west build -b $(BOARD) -p auto $(CWD) + +flash: + cd $(ZEPHYR_DIR) && west flash + +clean: + rm -rf */*/mongoose.* diff --git a/examples/zephyr/http-server/prj.conf b/examples/zephyr/http-server/prj.conf index d4fdf335..449ba01f 100644 --- a/examples/zephyr/http-server/prj.conf +++ b/examples/zephyr/http-server/prj.conf @@ -6,6 +6,7 @@ CONFIG_NET_UDP=y CONFIG_NET_DHCPV4=y CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POLL_MAX=32 +CONFIG_POSIX_MAX_FDS=32 CONFIG_NET_CONFIG_SETTINGS=y CONFIG_NET_LOG=y @@ -16,6 +17,9 @@ CONFIG_IDLE_STACK_SIZE=1024 CONFIG_MBEDTLS=y CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2048 +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_ECP_ALL_ENABLED=y CONFIG_MINIMAL_LIBC_RAND=y CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=32756 diff --git a/examples/zephyr/http-server/src/certs.h b/examples/zephyr/http-server/src/certs.h new file mode 100644 index 00000000..3efc0725 --- /dev/null +++ b/examples/zephyr/http-server/src/certs.h @@ -0,0 +1,24 @@ +#ifndef CERTS_H_ +#define CERTS_H_ + +static const char *s_ssl_cert = +"-----BEGIN CERTIFICATE-----\r\n" \ +"MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw\r\n" \ +"RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\r\n" \ +"YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx\r\n" \ +"NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\r\n" \ +"QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW\r\n" \ +"H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw\r\n" \ +"EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ\r\n" \ +"QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50\r\n" \ +"BllCI0eYQ9ggp/o=\r\n" \ +"-----END CERTIFICATE-----\r\n"; + +static const char *s_ssl_key = +"-----BEGIN PRIVATE KEY-----\r\n" \ +"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w\r\n" \ +"kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70\r\n" \ +"wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo\r\n" \ +"-----END PRIVATE KEY-----\r\n"; + +#endif diff --git a/examples/zephyr/http-server/src/main.c b/examples/zephyr/http-server/src/main.c index b4c9a199..d5239a47 100644 --- a/examples/zephyr/http-server/src/main.c +++ b/examples/zephyr/http-server/src/main.c @@ -2,20 +2,19 @@ // All rights reserved #include "mongoose.h" +#include "certs.h" static const char *s_debug_level = "3"; static const char *s_web_dir = "/"; static const char *s_http_addr = "http://0.0.0.0:8000"; static const char *s_https_addr = "https://0.0.0.0:8443"; -static const char *s_cert = "cert.pem"; -static const char *s_key = "key.pem"; static time_t s_boot_timestamp = 0; static struct mg_connection *s_sntp_conn = NULL; // Event handler for the listening HTTP/HTTPS connection. static void wcb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_ACCEPT && fn_data != NULL) { - struct mg_tls_opts opts = {.cert = s_cert, .certkey = s_key}; + struct mg_tls_opts opts = {.cert = s_ssl_cert, .certkey = s_ssl_key}; mg_tls_init(c, &opts); } else if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = ev_data; diff --git a/examples/zephyr/mqtt-client-aws-client/CMakeLists.txt b/examples/zephyr/mqtt-client-aws-client/CMakeLists.txt new file mode 100644 index 00000000..a64e924d --- /dev/null +++ b/examples/zephyr/mqtt-client-aws-client/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2022 Cesanta Software Limited +# Mail: support@cesanta.com +# +# SPDX-License-Identifier: GPL2.0 or commercial + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(http_server) +#add_definitions(-DMG_ENABLE_LINES=1) +add_definitions(-DMG_ENABLE_MBEDTLS=1 -DMG_MBEDTLS_DEBUG_LEVEL=4) +target_sources(app PRIVATE src/main.c src/mongoose.c) diff --git a/examples/zephyr/mqtt-client-aws-client/Makefile b/examples/zephyr/mqtt-client-aws-client/Makefile new file mode 100644 index 00000000..1e56e8c0 --- /dev/null +++ b/examples/zephyr/mqtt-client-aws-client/Makefile @@ -0,0 +1,16 @@ +CWD = $(realpath $(CURDIR)) +ZEPHYR_DIR ?= $(realpath ../../../../zephyrproject) +BOARD ?= nucleo_h743zi + +example: + true + +build: + cp ../../../mongoose.c ../../../mongoose.h src/ + cd $(ZEPHYR_DIR) && west build -b $(BOARD) -p auto $(CWD) + +flash: + cd $(ZEPHYR_DIR) && west flash + +clean: + rm -rf */*/mongoose.* diff --git a/examples/zephyr/mqtt-client-aws-client/prj.conf b/examples/zephyr/mqtt-client-aws-client/prj.conf new file mode 100644 index 00000000..2622e4df --- /dev/null +++ b/examples/zephyr/mqtt-client-aws-client/prj.conf @@ -0,0 +1,32 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POLL_MAX=32 +CONFIG_POSIX_MAX_FDS=32 +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_LOG=y + +CONFIG_LOG=y +CONFIG_ISR_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=16000 +CONFIG_IDLE_STACK_SIZE=1024 + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=8192 +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_ECP_ALL_ENABLED=y +CONFIG_MBEDTLS_TLS_VERSION_1_2=y + +CONFIG_MINIMAL_LIBC_RAND=y +CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=131072 + +CONFIG_FILE_SYSTEM_LITTLEFS=y + +CONFIG_PRINTK=y +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=4 diff --git a/examples/zephyr/mqtt-client-aws-client/src/certs.h b/examples/zephyr/mqtt-client-aws-client/src/certs.h new file mode 100644 index 00000000..b5f1c58b --- /dev/null +++ b/examples/zephyr/mqtt-client-aws-client/src/certs.h @@ -0,0 +1,115 @@ +#ifndef CERTS_H_ +#define CERTS_H_ + +static const char *s_ca = +"-\n" +"# Note: Amazon ATS endpoint uses this (2018/12/18)\n" +"Subject: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority\n" +"Not Before: Jun 29 17:39:16 2004 GMT\n" +"Not After : Jun 29 17:39:16 2034 GMT\n" +"-----BEGIN CERTIFICATE-----\n" +"MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" +"MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" +"U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n" +"NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" +"ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n" +"ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n" +"DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n" +"8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n" +"+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" +"X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n" +"K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n" +"1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n" +"A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n" +"zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n" +"YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n" +"bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n" +"DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n" +"L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" +"eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n" +"xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n" +"VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n" +"WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n" +"-----END CERTIFICATE-----\n" +"\n" +"# https://www.amazontrust.com/repository/\n" +"Subject: C = US, O = Amazon, CN = Amazon Root CA 1\n" +"Not Before: May 26 00:00:00 2015 GMT\n" +"Not After : Jan 17 00:00:00 2038 GMT\n" +"-----BEGIN CERTIFICATE-----\n" +"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" +"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" +"b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" +"MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" +"b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" +"ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" +"9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" +"IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" +"VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" +"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" +"jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" +"AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" +"A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" +"U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" +"N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" +"o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" +"5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" +"rqXRfboQnoZsG4q5WTP468SQvvG5\n" +"-----END CERTIFICATE-----\n" +"\n" +""; + +static const char *s_cert = +"-----BEGIN CERTIFICATE-----\n" +"MIIDWjCCAkKgAwIBAgIVAPoY7Fz1DxA+/VgB/GdsOXwg45cRMA0GCSqGSIb3DQEB\n" +"CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t\n" +"IEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0yMjA0MTExMjQw\n" +"MTRaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh\n" +"dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvwVAUCWq4PCS0bsU5\n" +"b4meX8klfoyp7yuSkHcPVhBv846TSIh5VeRSR68LOYAYRHaN6C2ybqhb5E42QKRX\n" +"SOA37OFXR3HvcbLF7c7BPkT13kFpymZJA+KUIPZ2Ki2zwWDKi6uP2lZfNxMv9xLo\n" +"8PGuWBCphzadbf4Hi4tGEs2BeXSXRCb4ZaI7gv3DdZHLHJuyrkF040E6flZrXmPv\n" +"dumvmszNZxWl8iIA68Pk7M1npXCr/za9CkIdoeSslKVBY4S0FNSISOxMCMqpV8fS\n" +"w4c8xQ0G4Whxgt1a6sGtm6TBGB1CjbAvQNyLWFeL2U8RRbG82xo5GYuemkaodCvH\n" +"/sBRAgMBAAGjYDBeMB8GA1UdIwQYMBaAFMdnILw/0ZLaKJ2dH2GZTXKu0ciZMB0G\n" +"A1UdDgQWBBQahyFNkNwZFKmVi+c6uWW9Iif95TAMBgNVHRMBAf8EAjAAMA4GA1Ud\n" +"DwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAb4Tlb2kvWcv5Dz4kCc43AFbA\n" +"Jv1MHwltznedpiyAyqXGU8s6UJCrhHKpjABHHcigkCD3iUlxLMTNhzEuTNBR1zfS\n" +"PMVP5EVmzOSRGvQNvhURRGkGVlNytv4VHTzaZUcuqhqNvw/Slvo0i2vlAPK0VIKQ\n" +"EPNle86zpUypAf2UlyrOT5vD1s7x5HmoHiKdUMqEiB6G/rack4vtUpA2V8fugKrH\n" +"QmBVXWHqTbpTUbqPk3PxmV1zqt5C9ZRVjjIDvW+Hl2zfVEoVq8l6BcLZmxFUnvA/\n" +"/aBWQ3k+V1fg3dUck3OCRGMsYYgBsvey6X+9oa+JFZh5mFtjA45C2SRtYtLesQ==\n" +"-----END CERTIFICATE-----\n" +""; + +static const char *s_key = +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEowIBAAKCAQEAr8FQFAlquDwktG7FOW+Jnl/JJX6Mqe8rkpB3D1YQb/OOk0iI\n" +"eVXkUkevCzmAGER2jegtsm6oW+RONkCkV0jgN+zhV0dx73Gyxe3OwT5E9d5Bacpm\n" +"SQPilCD2diots8Fgyourj9pWXzcTL/cS6PDxrlgQqYc2nW3+B4uLRhLNgXl0l0Qm\n" +"+GWiO4L9w3WRyxybsq5BdONBOn5Wa15j73bpr5rMzWcVpfIiAOvD5OzNZ6Vwq/82\n" +"vQpCHaHkrJSlQWOEtBTUiEjsTAjKqVfH0sOHPMUNBuFocYLdWurBrZukwRgdQo2w\n" +"L0Dci1hXi9lPEUWxvNsaORmLnppGqHQrx/7AUQIDAQABAoIBAHX/c6QALoZC2uHI\n" +"/ODy4ZJS/NkPfaEZ0kc0drr1LVP8UVzQ9c+AuyoCtqLyl+2zCWV83DFP2fjX7yq9\n" +"e5iedOsXPrM6ZGQ+EaiRYJ6dD++CqKg+Gy4qTZTmDJKI/uYQjsttumF6VULX1yn8\n" +"19VM7vX+ajFndmN71XF7XqZNOWwS4kEk5znQ7oesMOzPazRDsdJmCCnvVc4h99jJ\n" +"IYOtCs9s2aLoSD/7lATKaVquuRNBdkcWmKqc3vYMUQwjk14ID7t31TBZtlLJC+Df\n" +"tn+LCH1C0cuTq86VV17lc9ObTj/xkc8tb3OEYAv8E7rOioJVKU6EMViGa0oJk1+f\n" +"tW6hqYECgYEA1GR0z69HzeGZun2ZbjX7DC8EPAI4PZY5EZ1O7RZQorLLKP7R+9sg\n" +"KtgSJwtKAgpi84I0iuMbE2VciCYfXao+UpWLxkAPZkAt6qcMGJ0kYVd42n0Rwesh\n" +"8bd/r6ojSnj0DY7zAsoXctdQiNDiGmCyrDvWqmGuM6XCDcuzlG4LbkMCgYEA09cq\n" +"tSDyZowGWafKIDW4JXLE3PQdJStM3hAmNisrx0mRB7HmNgs3gR4/L4giZnE2hI+O\n" +"6IofiLjDjSmenWkvWw8Jy/QrhWpAuWlfeEpHaN910IXemFyqzMz0jL9s/Bn4BOYQ\n" +"WiqV19Pj4EjSrum76e17mBNq8vc72C4wXS3gj9sCgYB2q/KAoIVUSbtlcgwqgkrV\n" +"Uefhx7O45ZjZeLXCzvcbER9mtoqJWLQufDAYVLxzS25idNv6xk44ligPgo1//kF4\n" +"T0qb6OsxzKZbOB8QGa1cHk3OdpdXbJO4xTi45C13zQFAGiE568USS7AZ4eyTpHfw\n" +"uDaHWLDG9tTodGrQgnQslQKBgFvdPwP1Gakmp8pRXPHC2exDbON0aP8pW9ggoLF0\n" +"3zM4z8/Kcc6V5qfzeese2ewaxE2QA6UL4MjldsfMeKBCcOrIBrOPWyAsB41gCKqd\n" +"a4IrBWg75V+lL9xINPSkPprEmC1np0eyl/BUTsmASvzXF0pGVgaIxSQ/2o/Q1+BQ\n" +"eMVpAoGBALiFGVLxSow60Cve7BRaUuKa+pfqPeuCW+bSLD5hy6hQeHInUtRJT7tW\n" +"NFC8y7zUpKRCENCqXbBsYe1x2HEdGtDv9NV4vjcvF63Dj90ZZVXQkSiLXkWvJu76\n" +"7sRX7dX2coR6LlQ+zwwkyDIh2wP2MAWXbYLeG8xbLp8a2ybueSZl\n" +"-----END RSA PRIVATE KEY-----\n" +""; + +#endif diff --git a/examples/zephyr/mqtt-client-aws-client/src/main.c b/examples/zephyr/mqtt-client-aws-client/src/main.c new file mode 100644 index 00000000..cfa0b2d9 --- /dev/null +++ b/examples/zephyr/mqtt-client-aws-client/src/main.c @@ -0,0 +1,109 @@ +// Copyright (c) 2020 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" +#include "certs.h" + +struct mg_mgr mgr; + +static const char *s_debug_level = "3"; +static time_t s_boot_timestamp = 0; +static struct mg_connection *s_sntp_conn = NULL; +static const char *s_url = "mqtts://a3nkain3cvvy7l-ats.iot.us-east-1.amazonaws.com"; + +static const char *s_rx_topic = "d/rx"; +static const char *s_tx_topic = "d/tx"; +static int s_qos = 1; +static int s_connected = 0; + +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_OPEN) { + // c->is_hexdumping = 1; + } else if (ev == MG_EV_ERROR) { + // On error, log error message + MG_ERROR(("%p %s", c->fd, (char *) ev_data)); + } else if (ev == MG_EV_CONNECT) { + // Set up 2-way TLS that is required by AWS IoT + struct mg_tls_opts opts = { + .ca = s_ca, .cert = s_cert, .certkey = s_key}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_MQTT_OPEN) { + // MQTT connect is successful + struct mg_str topic = mg_str(s_rx_topic); + MG_INFO(("Connected to %s", s_url)); + MG_INFO(("Subscribing to %s", s_rx_topic)); + mg_mqtt_sub(c, topic, s_qos); + c->label[0] = 'X'; // Set a label that we're logged in + } else if (ev == MG_EV_MQTT_MSG) { + // When we receive MQTT message, print it + struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; + MG_INFO(("Received on %.*s : %.*s", (int) mm->topic.len, mm->topic.ptr, + (int) mm->data.len, mm->data.ptr)); + } else if (ev == MG_EV_POLL && c->label[0] == 'X') { + static unsigned long prev_second; + unsigned long now_second = (*(unsigned long *) ev_data) / 1000; + if (now_second != prev_second) { + struct mg_str topic = mg_str(s_tx_topic), data = mg_str("{\"a\":123}"); + MG_INFO(("Publishing to %s", s_tx_topic)); + mg_mqtt_pub(c, topic, data, s_qos, false); + prev_second = now_second; + } + } +} + +// We have no valid system time(), and we need it for TLS. Implement it +time_t time(time_t *tp) { + time_t t = s_boot_timestamp + k_uptime_get() / 1000; + if (tp != NULL) *tp = t; + return t; +} + +// SNTP callback. Modifies s_boot_timestamp, to make time() correct +static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_SNTP_TIME) { + int64_t t = *(int64_t *) ev_data; + MG_INFO(("Got SNTP time: %lld ms from epoch", t)); + s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000); + + // We need correct time in order to get HTTPs working, therefore, + // making https request from SMTP callback + if(!s_connected) { + MG_INFO(("Connecting to : [%s]", s_url)); + struct mg_mqtt_opts opts = {.clean = true}; + mg_mqtt_connect(&mgr, s_url, &opts, fn, NULL); // Create client connection + s_connected = 1; + } + } else if (ev == MG_EV_CLOSE) { + s_sntp_conn = NULL; + } +} + +// Periodic timer syncs time via SNTP +static void timer_fn(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; + if (s_sntp_conn == NULL) s_sntp_conn = mg_sntp_connect(mgr, NULL, sfn, NULL); + if (s_boot_timestamp < 9999) mg_sntp_send(s_sntp_conn, time(NULL)); +} + +// Use Zephyr's printk() for Mongooose MG_* logging +static void logfn(const void *ptr, size_t len, void *userdata) { + printk("%.*s", (int) len, (char *) ptr); +} + +int main(int argc, char *argv[]) { + mg_log_set(s_debug_level); + mg_log_set_callback(logfn, NULL); + + mg_mgr_init(&mgr); + + struct mg_timer t; + mg_timer_init(&t, 5000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr); + + // Start infinite event loop + MG_INFO(("Mongoose version : v%s", MG_VERSION)); + mg_http_connect(&mgr, s_url, fn, NULL); // Create client connection + + for (;;) mg_mgr_poll(&mgr, 1000); + mg_mgr_free(&mgr); + return 0; +} diff --git a/examples/zephyr/websocket-server/CMakeLists.txt b/examples/zephyr/websocket-server/CMakeLists.txt new file mode 100644 index 00000000..f385d74e --- /dev/null +++ b/examples/zephyr/websocket-server/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2022 Cesanta Software Limited +# Mail: support@cesanta.com +# +# SPDX-License-Identifier: GPL2.0 or commercial + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(http_server) +#add_definitions(-DMG_ENABLE_LINES=1) +add_definitions(-DMG_ENABLE_MBEDTLS=1) +target_sources(app PRIVATE src/main.c src/mongoose.c) diff --git a/examples/zephyr/websocket-server/Makefile b/examples/zephyr/websocket-server/Makefile new file mode 100644 index 00000000..1e56e8c0 --- /dev/null +++ b/examples/zephyr/websocket-server/Makefile @@ -0,0 +1,16 @@ +CWD = $(realpath $(CURDIR)) +ZEPHYR_DIR ?= $(realpath ../../../../zephyrproject) +BOARD ?= nucleo_h743zi + +example: + true + +build: + cp ../../../mongoose.c ../../../mongoose.h src/ + cd $(ZEPHYR_DIR) && west build -b $(BOARD) -p auto $(CWD) + +flash: + cd $(ZEPHYR_DIR) && west flash + +clean: + rm -rf */*/mongoose.* diff --git a/examples/zephyr/websocket-server/prj.conf b/examples/zephyr/websocket-server/prj.conf new file mode 100644 index 00000000..449ba01f --- /dev/null +++ b/examples/zephyr/websocket-server/prj.conf @@ -0,0 +1,27 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POLL_MAX=32 +CONFIG_POSIX_MAX_FDS=32 +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_LOG=y + +CONFIG_LOG=y +CONFIG_ISR_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_IDLE_STACK_SIZE=1024 + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2048 +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_ECP_ALL_ENABLED=y + +CONFIG_MINIMAL_LIBC_RAND=y +CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=32756 + +CONFIG_FILE_SYSTEM_LITTLEFS=y diff --git a/examples/zephyr/websocket-server/src/certs.h b/examples/zephyr/websocket-server/src/certs.h new file mode 100644 index 00000000..3efc0725 --- /dev/null +++ b/examples/zephyr/websocket-server/src/certs.h @@ -0,0 +1,24 @@ +#ifndef CERTS_H_ +#define CERTS_H_ + +static const char *s_ssl_cert = +"-----BEGIN CERTIFICATE-----\r\n" \ +"MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw\r\n" \ +"RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\r\n" \ +"YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx\r\n" \ +"NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\r\n" \ +"QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW\r\n" \ +"H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw\r\n" \ +"EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ\r\n" \ +"QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50\r\n" \ +"BllCI0eYQ9ggp/o=\r\n" \ +"-----END CERTIFICATE-----\r\n"; + +static const char *s_ssl_key = +"-----BEGIN PRIVATE KEY-----\r\n" \ +"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w\r\n" \ +"kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70\r\n" \ +"wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo\r\n" \ +"-----END PRIVATE KEY-----\r\n"; + +#endif diff --git a/examples/zephyr/websocket-server/src/main.c b/examples/zephyr/websocket-server/src/main.c new file mode 100644 index 00000000..eda67dfd --- /dev/null +++ b/examples/zephyr/websocket-server/src/main.c @@ -0,0 +1,92 @@ +// Copyright (c) 2020 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" +#include "certs.h" + +static const char *s_debug_level = "3"; +static const char *s_web_dir = "/"; +static const char *s_ws_addr = "ws://0.0.0.0:8000"; +static const char *s_wss_addr = "wss://0.0.0.0:8443"; +static time_t s_boot_timestamp = 0; +static struct mg_connection *s_sntp_conn = NULL; + +// This RESTful server implements the following endpoints: +// /websocket - upgrade to Websocket, and implement websocket echo server +// /api/rest - respond with JSON string {"result": 123} +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_OPEN) { + c->is_hexdumping = 1; + } else if (ev == MG_EV_ACCEPT && fn_data != NULL) { + struct mg_tls_opts opts = {.cert = s_ssl_cert, .certkey = s_ssl_key}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + if (mg_http_match_uri(hm, "/websocket")) { + // Upgrade to websocket. From now on, a connection is a full-duplex + // Websocket connection, which will receive MG_EV_WS_MSG events. + mg_ws_upgrade(c, hm, NULL); + } else if (mg_http_match_uri(hm, "/rest")) { + // Serve REST response + mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123); + } + } else if (ev == MG_EV_WS_MSG) { + // Got websocket frame. Received data is wm->data. Echo it back! + struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; + mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT); + } + (void) fn_data; +} + +// We have no valid system time(), and we need it for TLS. Implement it +time_t time(time_t *tp) { + time_t t = s_boot_timestamp + k_uptime_get() / 1000; + if (tp != NULL) *tp = t; + return t; +} + +// SNTP callback. Modifies s_boot_timestamp, to make time() correct +static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_SNTP_TIME) { + int64_t t = *(int64_t *) ev_data; + MG_INFO(("Got SNTP time: %lld ms from epoch", t)); + s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000); + } else if (ev == MG_EV_CLOSE) { + s_sntp_conn = NULL; + } +} + +// Periodic timer syncs time via SNTP +static void timer_fn(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; + if (s_sntp_conn == NULL) s_sntp_conn = mg_sntp_connect(mgr, NULL, sfn, NULL); + if (s_boot_timestamp < 9999) mg_sntp_send(s_sntp_conn, time(NULL)); +} + +// Use Zephyr's printk() for Mongooose MG_* logging +static void logfn(const void *ptr, size_t len, void *userdata) { + printk("%.*s", (int) len, (char *) ptr); +} + +int main(int argc, char *argv[]) { + struct mg_mgr mgr; + + mg_log_set(s_debug_level); + mg_log_set_callback(logfn, NULL); + + mg_mgr_init(&mgr); + mg_http_listen(&mgr, s_ws_addr, fn, NULL); + mg_http_listen(&mgr, s_wss_addr, fn, &mgr); + + struct mg_timer t; + mg_timer_init(&t, 5000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr); + + // Start infinite event loop + MG_INFO(("Mongoose version : v%s", MG_VERSION)); + MG_INFO(("Listening on : %s", s_ws_addr)); + MG_INFO(("Listening on : %s", s_wss_addr)); + MG_INFO(("Web root : [%s]", s_web_dir)); + for (;;) mg_mgr_poll(&mgr, 1000); + mg_mgr_free(&mgr); + return 0; +}