diff --git a/component.mk b/component.mk index 9b5235e1..9afdeb6f 100644 --- a/component.mk +++ b/component.mk @@ -19,12 +19,12 @@ endif build: cd $(COMPONENT_BUILD_DIR) ; \ echo "doing lws cmake" ; \ - cmake $(COMPONENT_PATH) -DLWS_C_FLAGS="$(CFLAGS) " \ + cmake $(COMPONENT_PATH) -DLWS_C_FLAGS="$(CFLAGS) -DNDEBUG=1" \ -DIDF_PATH=$(IDF_PATH) \ -DCROSS_PATH=$(CROSS_PATH) \ -DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \ -DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/contrib/cross-esp32.cmake \ - -DCMAKE_BUILD_TYPE=DEBUG \ + -DCMAKE_BUILD_TYPE=RELEASE \ -DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/include;${IDF_PATH}/components/mbedtls/port/include" \ -DLWS_WITH_STATS=0 \ -DLWS_WITH_HTTP2=1 \ diff --git a/lib/alloc.c b/lib/alloc.c index a7baca96..e8a37667 100644 --- a/lib/alloc.c +++ b/lib/alloc.c @@ -52,8 +52,8 @@ static void *_realloc(void *ptr, size_t size, const char *reason) { if (size) { #if defined(LWS_WITH_ESP32) - lwsl_notice("%s: size %lu: %s\n", __func__, - (unsigned long)size, reason); + lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__, + (unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size); #else lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); diff --git a/lib/context.c b/lib/context.c index 57ad8b5c..3843824f 100644 --- a/lib/context.c +++ b/lib/context.c @@ -838,12 +838,12 @@ lws_create_vhost(struct lws_context *context, vh->log_fd = (int)LWS_INVALID_FILE; #endif if (lws_context_init_server_ssl(info, vh)) - goto bail; + goto bail1; if (lws_context_init_client_ssl(info, vh)) - goto bail; + goto bail1; if (lws_context_init_server(info, vh)) { lwsl_err("init server failed\n"); - goto bail; + goto bail1; } while (1) { @@ -858,12 +858,19 @@ lws_create_vhost(struct lws_context *context, if (context->protocol_init_done) if (lws_protocol_init(context)) - goto bail; + goto bail1; return vh; +bail1: + lws_vhost_destroy(vh); + + return NULL; + +#ifdef LWS_WITH_ACCESS_LOG bail: lws_free(vh); +#endif return NULL; } diff --git a/lib/http2/http2.c b/lib/http2/http2.c index 87cc1c17..e5e5e4a8 100644 --- a/lib/http2/http2.c +++ b/lib/http2/http2.c @@ -1496,7 +1496,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen, h2n->inside += n; h2n->count += n - 1; - lwsl_notice("%s: count %d len %d\n", __func__, (int)h2n->count, (int)h2n->length); + // lwsl_notice("%s: count %d len %d\n", __func__, (int)h2n->count, (int)h2n->length); break; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 6c0e4439..9a35c6b9 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -571,6 +571,8 @@ struct lws_esp32 { char access_pw[16]; char hostname[32]; char mac[20]; + char le_dns[64]; + char le_email[64]; mdns_server_t *mdns; char region; char inet; @@ -584,6 +586,8 @@ struct lws_esp32 { struct lws_group_member *first; int extant_group_members; + char acme; + volatile char button_is_down; }; @@ -836,6 +840,45 @@ struct lws_ssl_info { int ret; }; +enum lws_cert_update_state { + LWS_CUS_IDLE, + LWS_CUS_STARTING, + LWS_CUS_SUCCESS, + LWS_CUS_FAILED, + + LWS_CUS_CREATE_KEYS, + LWS_CUS_REG, + LWS_CUS_AUTH, + LWS_CUS_CHALLENGE, + LWS_CUS_CREATE_REQ, + LWS_CUS_REQ, + LWS_CUS_CONFIRM, + LWS_CUS_ISSUE, +}; + +enum { + LWS_TLS_REQ_ELEMENT_COUNTRY, + LWS_TLS_REQ_ELEMENT_STATE, + LWS_TLS_REQ_ELEMENT_LOCALITY, + LWS_TLS_REQ_ELEMENT_ORGANIZATION, + LWS_TLS_REQ_ELEMENT_COMMON_NAME, + LWS_TLS_REQ_ELEMENT_EMAIL, + + LWS_TLS_REQ_ELEMENT_COUNT, + + LWS_TLS_SET_DIR_URL = LWS_TLS_REQ_ELEMENT_COUNT, + LWS_TLS_SET_AUTH_PATH, + LWS_TLS_SET_CERT_PATH, + LWS_TLS_SET_KEY_PATH, + + LWS_TLS_TOTAL_COUNT +}; + +struct lws_acme_cert_aging_args { + struct lws_vhost *vh; + const char *element_overrides[LWS_TLS_TOTAL_COUNT]; /* NULL = use pvo */ +}; + /* * NOTE: These public enums are part of the abi. If you want to add one, * add it at where specified so existing users are unaffected. @@ -1352,14 +1395,25 @@ enum lws_callback_reasons { /**< When a vhost TLS cert has its expiry checked, this callback * is broadcast to every protocol of every vhost in case the * protocol wants to take some action with this information. - * \p in is the lws_vhost and \p len is the number of days left - * before it expires, as a (ssize_t) */ + * \p in is a pointer to a struct lws_acme_cert_aging_args, + * and \p len is the number of days left before it expires, as + * a (ssize_t). In the struct lws_acme_cert_aging_args, vh + * points to the vhost the cert aging information applies to, + * and element_overrides[] is an optional way to update information + * from the pvos... NULL in an index means use the information from + * from the pvo for the cert renewal, non-NULL in the array index + * means use that pointer instead for the index. */ LWS_CALLBACK_TIMER = 73, /**< When the time elapsed after a call to lws_set_timer(wsi, secs) * is up, the wsi will get one of these callbacks. The deadline * can be continuously extended into the future by later calls * to lws_set_timer() before the deadline expires, or cancelled by * lws_set_timer(wsi, -1); */ + LWS_CALLBACK_VHOST_CERT_UPDATE = 74, + /**< When a vhost TLS cert is being updated, progress is + * reported to the vhost in question here, including completion + * and failure. in points to optional JSON, and len represents the + * connection state using enum lws_cert_update_state */ /****** add new things just above ---^ ******/ @@ -5492,6 +5546,17 @@ lws_is_ssl(struct lws *wsi); LWS_VISIBLE LWS_EXTERN int lws_is_cgi(struct lws *wsi); + +struct lws_wifi_scan { /* generic wlan scan item */ + struct lws_wifi_scan *next; + char ssid[32]; + int32_t rssi; /* divide by .count to get db */ + uint8_t bssid[6]; + uint8_t count; + uint8_t channel; + uint8_t authmode; +}; + #if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS) /** * lws_get_ssl() - Return wsi's SSL context structure @@ -5601,23 +5666,6 @@ LWS_VISIBLE LWS_EXTERN int lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, const char *san_b); -enum { - LWS_TLS_REQ_ELEMENT_COUNTRY, - LWS_TLS_REQ_ELEMENT_STATE, - LWS_TLS_REQ_ELEMENT_LOCALITY, - LWS_TLS_REQ_ELEMENT_ORGANIZATION, - LWS_TLS_REQ_ELEMENT_COMMON_NAME, - LWS_TLS_REQ_ELEMENT_EMAIL, - - LWS_TLS_REQ_ELEMENT_COUNT, - LWS_TLS_SET_DIR_URL = LWS_TLS_REQ_ELEMENT_COUNT, - LWS_TLS_SET_AUTH_PATH, - LWS_TLS_SET_CERT_PATH, - LWS_TLS_SET_KEY_PATH, - - LWS_TLS_TOTAL_COUNT -}; - /** * lws_tls_acme_sni_csr_create() - creates a CSR and related private key PEM * @@ -6614,31 +6662,31 @@ struct lejp_ctx { /* arrays */ struct _lejp_stack st[LEJP_MAX_DEPTH]; - unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */ - unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ + uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */ + uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ char path[LEJP_MAX_PATH]; char buf[LEJP_STRING_CHUNK]; /* int */ - unsigned int line; + uint32_t line; /* short */ - unsigned short uni; + uint16_t uni; /* char */ - unsigned char npos; - unsigned char dcount; - unsigned char f; - unsigned char sp; /* stack head */ - unsigned char ipos; /* index stack depth */ - unsigned char ppos; - unsigned char count_paths; - unsigned char path_match; - unsigned char path_match_len; - unsigned char wildcount; + uint8_t npos; + uint8_t dcount; + uint8_t f; + uint8_t sp; /* stack head */ + uint8_t ipos; /* index stack depth */ + uint8_t ppos; + uint8_t count_paths; + uint8_t path_match; + uint8_t path_match_len; + uint8_t wildcount; }; LWS_VISIBLE LWS_EXTERN void diff --git a/lib/plat/lws-plat-esp32.c b/lib/plat/lws-plat-esp32.c index b57c1c1a..9a58686b 100644 --- a/lib/plat/lws-plat-esp32.c +++ b/lib/plat/lws-plat-esp32.c @@ -110,7 +110,7 @@ lws_poll_listen_fd(struct lws_pollfd *fd) LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) { - printf("%d: %s", level, line); + lwsl_emit_stderr(level, line); } LWS_VISIBLE LWS_EXTERN int @@ -709,7 +709,7 @@ static const char *gapss_str[] = { }; static romfs_t lws_esp32_romfs; -static TimerHandle_t leds_timer, scan_timer, debounce_timer +static TimerHandle_t leds_timer, scan_timer, debounce_timer, association_timer #if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) , mdns_timer #endif @@ -811,6 +811,28 @@ static void lws_esp32_scan_timer_cb(TimerHandle_t th) lwsl_err("scan start failed %d\n", n); } +static void lws_esp32_assoc_timer_cb(TimerHandle_t th) +{ + int n; + + xTimerStop(association_timer, 0); + + if (gapss == LWS_GAPSS_STAT_HAPPY) { + lwsl_debug("%s: saw we were happy\n", __func__); + + return; + } + + lwsl_notice("%s: forcing rescan\n", __func__); + + lws_gapss_to(LWS_GAPSS_SCAN); + scan_ongoing = 0; + n = esp_wifi_scan_start(&scan_config, false); + if (n != ESP_OK) + lwsl_err("scan start failed %d\n", n); +} + + #if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) void __attribute__(( weak )) @@ -1018,7 +1040,7 @@ end_scan() goto passthru; if (gapss != LWS_GAPSS_SCAN) { - lwsl_notice("ignoring scan as gapss %s\n", gapss_str[gapss]); + lwsl_info("ignoring scan as gapss %s\n", gapss_str[gapss]); goto passthru; } @@ -1029,14 +1051,14 @@ end_scan() !lws_esp32.ssid[3][0]) goto passthru; - lwsl_notice("checking %d scan records\n", count_ap_records); + lwsl_info("checking %d scan records\n", count_ap_records); for (n = 0; n < 4; n++) { if (!lws_esp32.ssid[(n + try_slot + 1) & 3][0]) continue; - lwsl_notice("looking for %s\n", + lwsl_debug("looking for %s\n", lws_esp32.ssid[(n + try_slot + 1) & 3]); /* this ssid appears in scan results? */ @@ -1053,7 +1075,7 @@ end_scan() hit: m = (n + try_slot + 1) & 3; try_slot = m; - lwsl_notice("Attempting connection with slot %d: %s:\n", m, + lwsl_info("Attempting connection with slot %d: %s:\n", m, lws_esp32.ssid[m]); /* set the ssid we last tried to connect to */ strncpy(lws_esp32.active_ssid, lws_esp32.ssid[m], @@ -1068,6 +1090,8 @@ hit: tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, (const char *)&config.ap.ssid[7]); lws_gapss_to(LWS_GAPSS_STAT); + xTimerStop(association_timer, 0); + xTimerStart(association_timer, 0); esp_wifi_set_config(WIFI_IF_STA, &sta_config); esp_wifi_connect(); @@ -1484,6 +1508,8 @@ lws_esp32_wlan_config(void) }; int n; + lwsl_debug("%s\n", __func__); + ledc_timer_config(&ledc_timer); lws_set_genled(LWSESP32_GENLED__INIT); @@ -1496,6 +1522,8 @@ lws_esp32_wlan_config(void) (TimerCallbackFunction_t)lws_esp32_scan_timer_cb); debounce_timer = xTimerCreate("lws_db", pdMS_TO_TICKS(100), 0, NULL, (TimerCallbackFunction_t)lws_esp32_debounce_timer_cb); + association_timer = xTimerCreate("lws_assoc", pdMS_TO_TICKS(10000), 0, NULL, + (TimerCallbackFunction_t)lws_esp32_assoc_timer_cb); #if !defined(CONFIG_LWS_IS_FACTORY_APPLICATION) mdns_timer = xTimerCreate("lws_mdns", pdMS_TO_TICKS(5000), 0, NULL, @@ -1667,13 +1695,13 @@ lws_esp32_set_creation_defaults(struct lws_context_creation_info *info) info->vhost_name = "default"; info->port = 443; - info->fd_limit_per_thread = 30; - info->max_http_header_pool = 16; - info->max_http_header_data = 512; - info->pt_serv_buf_size = 2048; + info->fd_limit_per_thread = 16; + info->max_http_header_pool = 5; + info->max_http_header_data = 1024; + info->pt_serv_buf_size = 4096; info->keepalive_timeout = 30; info->timeout_secs = 30; - info->simultaneous_ssl_restriction = 4; + info->simultaneous_ssl_restriction = 2; info->options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; } @@ -1851,6 +1879,22 @@ fail: return -1; } +void +lws_esp32_update_acme_info(void) +{ + int n; + + n = lws_plat_read_file("acme-email", lws_esp32.le_email, + sizeof(lws_esp32.le_email) - 1); + if (n >= 0) + lws_esp32.le_email[n] = '\0'; + + n = lws_plat_read_file("acme-cn", lws_esp32.le_dns, + sizeof(lws_esp32.le_dns) - 1); + if (n >= 0) + lws_esp32.le_dns[n] = '\0'; +} + struct lws_context * lws_esp32_init(struct lws_context_creation_info *info, struct lws_vhost **pvh) { @@ -1893,6 +1937,8 @@ lws_esp32_init(struct lws_context_creation_info *info, struct lws_vhost **pvh) return NULL; } + lws_esp32_update_acme_info(); + lws_esp32_selfsigned(vhost); wsi.context = vhost->context; wsi.vhost = vhost; @@ -1960,16 +2006,16 @@ lws_plat_write_file(const char *filename, void *buf, int len) if (nvs_open("lws-station", NVS_READWRITE, &nvh)) { lwsl_notice("%s: failed to open nvs\n", __func__); - return 1; + return -1; } n = nvs_set_blob(nvh, filename, buf, len); - if (n) + if (n >= 0) nvs_commit(nvh); nvs_close(nvh); - lwsl_notice("%s: wrote %s\n", __func__, filename); + lwsl_notice("%s: wrote %s (%d)\n", __func__, filename, n); return n; } @@ -1985,7 +2031,7 @@ lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, if (is_key) name = vhost->key_path; - return lws_plat_write_file(name, buf, len); + return lws_plat_write_file(name, buf, len) < 0; } LWS_VISIBLE int diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 8f3374b2..6d3a1928 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1000,6 +1000,7 @@ struct lws_vhost { unsigned int created_vhost_protocols:1; unsigned int being_destroyed:1; unsigned int skipped_certs:1; + unsigned int acme_challenge:1; unsigned char default_protocol_index; unsigned char raw_protocol_index; diff --git a/lib/server/server.c b/lib/server/server.c index d1f91f5f..ec4cefe2 100644 --- a/lib/server/server.c +++ b/lib/server/server.c @@ -2540,10 +2540,13 @@ try_pollout: case LWSCM_SERVER_LISTENER: #if LWS_POSIX - /* pollin means a client has connected to us then */ + /* pollin means a client has connected to us then + * pollout is a hack on esp32 for background accepts signalling + * they completed + * */ do { - if (!(pollfd->revents & LWS_POLLIN) || + if (!(pollfd->revents & (LWS_POLLIN |LWS_POLLOUT)) || !(pollfd->events & LWS_POLLIN)) break; diff --git a/lib/server/ssl-server.c b/lib/server/ssl-server.c index ef8ba3d9..4e0d8b84 100644 --- a/lib/server/ssl-server.c +++ b/lib/server/ssl-server.c @@ -88,9 +88,10 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, lws_tls_server_client_cert_verify_config(vhost); - vhost->protocols[0].callback(&wsi, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, - vhost->ssl_ctx, vhost, 0); + if (vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, + vhost->ssl_ctx, vhost, 0)) + return -1; } if (vhost->use_ssl) @@ -142,6 +143,11 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) context->simultaneous_ssl_restriction) /* that was the last allowed SSL connection */ lws_gate_accepts(context, 0); + + //lwsl_notice("%s: ssl restr %d, simul %d\n", __func__, + // context->simultaneous_ssl_restriction, + // context->simultaneous_ssl); + #if defined(LWS_WITH_STATS) context->updated = 1; #endif diff --git a/lib/service.c b/lib/service.c index 940ed483..9dd18979 100644 --- a/lib/service.c +++ b/lib/service.c @@ -1000,7 +1000,7 @@ completed: if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP, wsi->user_space, NULL, 0)) { - lwsl_debug("Completed call returned -1\n"); + lwsl_debug("%s: Completed call returned nonzero (mode %d)\n", __func__, wsi->mode); return -1; } diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c index da3c1da5..5e8c9b86 100644 --- a/lib/tls/mbedtls/mbedtls-server.c +++ b/lib/tls/mbedtls/mbedtls-server.c @@ -213,6 +213,7 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info, const SSL_METHOD *method = TLS_server_method(); uint8_t *p; lws_filepos_t flen; + int n; vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ if (!vhost->ssl_ctx) { @@ -243,10 +244,13 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info, free(p); } - return lws_tls_server_certs_load(vhost, wsi, - info->ssl_cert_filepath, - info->ssl_private_key_filepath, - NULL, 0, NULL, 0); + n = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, + info->ssl_private_key_filepath, NULL, + 0, NULL, 0); + if (n) + return n; + + return 0; } int @@ -284,9 +288,17 @@ enum lws_ssl_capable_status lws_tls_server_accept(struct lws *wsi) { union lws_tls_cert_info_results ir; - int m, n = SSL_accept(wsi->ssl); + int m, n; + n = SSL_accept(wsi->ssl); if (n == 1) { + + if (strstr(wsi->vhost->name, ".invalid")) { + lwsl_notice("%s: vhost has .invalid, rejecting accept\n", __func__); + + return LWS_SSL_CAPABLE_ERROR; + } + n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir, sizeof(ir.ns.name)); if (!n) @@ -298,6 +310,8 @@ lws_tls_server_accept(struct lws *wsi) } m = SSL_get_error(wsi->ssl, n); + lwsl_debug("%s: %p: accept SSL_get_error %d errno %d\n", __func__, + wsi, m, errno); // mbedtls wrapper only if (m == SSL_ERROR_SYSCALL && errno == 11) @@ -439,7 +453,7 @@ lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, uint8_t digest[32]; struct lws_genhash_ctx hash_ctx; int pkey_asn1_len = 3 * 1024; - int n, keybits = lws_plat_recommended_rsa_bits(), adj; + int n, m, keybits = lws_plat_recommended_rsa_bits(), adj; if (!buf) return 1; @@ -522,35 +536,35 @@ lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, if (!pkey_asn1) goto bail2; - n = lws_genrsa_render_pkey_asn1(&ctx, 1, pkey_asn1, pkey_asn1_len); - if (n < 0) { + m = lws_genrsa_render_pkey_asn1(&ctx, 1, pkey_asn1, pkey_asn1_len); + if (m < 0) { lws_free(pkey_asn1); goto bail2; } - lwsl_debug("private key\n"); - lwsl_hexdump_level(LLL_DEBUG, pkey_asn1, n); - /* and to use our generated private key */ - n = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, pkey_asn1, n); - lws_free(pkey_asn1); +// lwsl_hexdump_level(LLL_DEBUG, buf, lws_ptr_diff(p, buf)); + n = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, + lws_ptr_diff(p, buf), buf); if (n != 1) { - lwsl_notice("%s: SSL_CTX_use_PrivateKey_ASN1 failed\n", - __func__); + lws_free(pkey_asn1); + lwsl_err("%s: generated cert failed to load 0x%x\n", + __func__, -n); + } else { + //lwsl_debug("private key\n"); + //lwsl_hexdump_level(LLL_DEBUG, pkey_asn1, n); + + /* and to use our generated private key */ + n = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, pkey_asn1, m); + lws_free(pkey_asn1); + if (n != 1) { + lwsl_err("%s: SSL_CTX_use_PrivateKey_ASN1 failed\n", + __func__); + } } lws_genrsa_destroy(&ctx); lws_jwk_destroy_genrsa_elements(&el); - if (n == 1) { - lwsl_hexdump_level(LLL_DEBUG, buf, lws_ptr_diff(p, buf)); - - n = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, - lws_ptr_diff(p, buf), buf); - if (n != 1) - lwsl_notice("%s: generated cert failed to load 0x%x\n", - __func__, -n); - } - lws_free(buf); return n != 1; @@ -579,6 +593,8 @@ _rngf(void *context, unsigned char *buf, size_t len) return -1; } +static const char *x5[] = { "C", "ST", "L", "O", "CN" }; + /* * CSR is output formatted as b64url(DER) * Private key is output as a PEM in memory @@ -589,9 +605,9 @@ lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], size_t *privkey_len) { mbedtls_x509write_csr csr; - char subject[200]; mbedtls_pk_context mpk; int buf_size = 4096, n; + char subject[200], *p = subject, *end = p + sizeof(subject) - 1; uint8_t *buf = malloc(buf_size); /* malloc because given to user code */ if (!buf) @@ -615,13 +631,14 @@ lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], /* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */ - lws_snprintf(subject, sizeof(subject) - 1, - "C=%s,ST=%s,L=%s,O=%s,CN=%s", - elements[LWS_TLS_REQ_ELEMENT_COUNTRY], - elements[LWS_TLS_REQ_ELEMENT_STATE], - elements[LWS_TLS_REQ_ELEMENT_LOCALITY], - elements[LWS_TLS_REQ_ELEMENT_ORGANIZATION], - elements[LWS_TLS_REQ_ELEMENT_COMMON_NAME]); + for (n = 0; n < ARRAY_SIZE(x5); n++) { + if (p != subject) + *p++ = ','; + if (elements[n]) + p += lws_snprintf(p, end - p, "%s=%s", x5[n], + elements[n]); + } + if (mbedtls_x509write_csr_set_subject_name(&csr, subject)) goto fail1; diff --git a/lib/tls/mbedtls/ssl.c b/lib/tls/mbedtls/ssl.c index 57d75c19..10c03884 100644 --- a/lib/tls/mbedtls/ssl.c +++ b/lib/tls/mbedtls/ssl.c @@ -257,11 +257,16 @@ lws_ssl_close(struct lws *wsi) SSL_free(wsi->ssl); wsi->ssl = NULL; - if (wsi->context->simultaneous_ssl_restriction && + if (!(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) && + wsi->context->simultaneous_ssl_restriction && wsi->context->simultaneous_ssl-- == wsi->context->simultaneous_ssl_restriction) /* we made space and can do an accept */ lws_gate_accepts(wsi->context, 1); + + //lwsl_notice("%s: ssl restr %d, simul %d\n", __func__, + // wsi->context->simultaneous_ssl_restriction, + // wsi->context->simultaneous_ssl); #if defined(LWS_WITH_STATS) wsi->context->updated = 1; #endif diff --git a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c index 354b0c7f..1d7d0932 100755 --- a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c +++ b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c @@ -25,6 +25,8 @@ #include "mbedtls/error.h" #include "mbedtls/certs.h" +#include + #define X509_INFO_STRING_LENGTH 8192 struct ssl_pm @@ -64,7 +66,7 @@ unsigned int max_content_len; /*********************************************************************************************/ /************************************ SSL arch interface *************************************/ -#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG +//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG /* mbedtls debug level */ #define MBEDTLS_DEBUG_LEVEL 4 @@ -81,13 +83,13 @@ static void ssl_platform_debug(void *ctx, int level, This is a bit wasteful because the macros are compiled in with the full _FILE_ path in each case. */ - char *file_sep = rindex(file, '/'); - if(file_sep) - file = file_sep + 1; +// char *file_sep = rindex(file, '/'); + // if(file_sep) + // file = file_sep + 1; - SSL_DEBUG(SSL_DEBUG_ON, "%s:%d %s", file, line, str); + printf("%s:%d %s", file, line, str); } -#endif +//#endif /** * @brief create SSL low-level object @@ -163,12 +165,12 @@ int ssl_pm_new(SSL *ssl) mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); -#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG - mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); +//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG + // mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); +// mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL); +//#else mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL); -#else - mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL); -#endif +//#endif ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); if (ret) { @@ -265,7 +267,7 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl ) while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { ret = mbedtls_ssl_handshake_step(ssl); - SSL_DEBUG(SSL_PLATFORM_DEBUG_LEVEL, "ssl ret %d state %d", ret, ssl->state); + lwsl_notice("%s: ssl ret -%x state %d\n", __func__, -ret, ssl->state); if (ret != 0) break; @@ -274,14 +276,23 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl ) return ret; } +#include + int ssl_pm_handshake(SSL *ssl) { int ret; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + lwsl_notice("%s\n", __func__); + + ssl->err = 0; + errno = 0; + ret = ssl_pm_reload_crt(ssl); - if (ret) + if (ret) { + printf("%s: cert reload failed\n", __func__); return 0; + } if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { ssl_speed_up_enter(); @@ -302,6 +313,7 @@ int ssl_pm_handshake(SSL *ssl) * <0 = death */ if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + ssl->err = ret; SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret); return 0; /* OpenSSL: did not complete but may be retried */ } @@ -313,6 +325,14 @@ int ssl_pm_handshake(SSL *ssl) return 1; /* openssl successful */ } + if (errno == 11) { + ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ; + + return 0; + } + + printf("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret); + /* it's had it */ ssl->err = SSL_ERROR_SYSCALL; @@ -377,7 +397,7 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len) ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len); if (ret < 0) { - //printf("%s: mbedtls_ssl_read says -0x%x\n", __func__, -ret); + // lwsl_notice("%s: mbedtls_ssl_read says -0x%x\n", __func__, -ret); SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret); if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret <= MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) /* fatal errors */ diff --git a/lib/tls/openssl/openssl-server.c b/lib/tls/openssl/openssl-server.c index a3cf45ac..6d2e0f19 100644 --- a/lib/tls/openssl/openssl-server.c +++ b/lib/tls/openssl/openssl-server.c @@ -282,7 +282,7 @@ check_key: lwsl_err("%s: no extra certs\n", __func__); #endif if (!x) { - lwsl_err("%s: x is NULL\n", __func__); + //lwsl_err("%s: x is NULL\n", __func__); goto post_ecdh; } /* Get the public key from certificate */ diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c index 249a1db2..b98691f5 100644 --- a/lib/tls/openssl/ssl.c +++ b/lib/tls/openssl/ssl.c @@ -401,6 +401,11 @@ lws_ssl_close(struct lws *wsi) wsi->context->simultaneous_ssl_restriction) /* we made space and can do an accept */ lws_gate_accepts(wsi->context, 1); + + // lwsl_notice("%s: ssl restr %d, simul %d\n", __func__, + // wsi->context->simultaneous_ssl_restriction, + // wsi->context->simultaneous_ssl); + #if defined(LWS_WITH_STATS) wsi->context->updated = 1; #endif diff --git a/lib/tls/tls.c b/lib/tls/tls.c index 09ce7810..d3d66707 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -225,6 +225,7 @@ lws_tls_check_cert_lifetime(struct lws_vhost *v) { union lws_tls_cert_info_results ir; time_t now = (time_t)lws_now_secs(), life = 0; + struct lws_acme_cert_aging_args caa; int n; if (v->ssl_ctx && !v->skipped_certs) { @@ -242,7 +243,9 @@ lws_tls_check_cert_lifetime(struct lws_vhost *v) } else lwsl_notice(" vhost %s: no cert\n", v->name); - lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, v, + memset(&caa, 0, sizeof(caa)); + caa.vh = v; + lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa, (size_t)(ssize_t)life); return 0; @@ -446,7 +449,7 @@ lws_gate_accepts(struct lws_context *context, int on) { struct lws_vhost *v = context->vhost_list; - lwsl_info("gating accepts %d\n", on); + lwsl_notice("%s: on = %d\n", __func__, on); context->ssl_gate_accepts = !on; #if defined(LWS_WITH_STATS) context->updated = 1; @@ -456,7 +459,7 @@ lws_gate_accepts(struct lws_context *context, int on) if (v->use_ssl && v->lserv_wsi && lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, (LWS_POLLIN) * on)) - lwsl_info("Unable to set accept POLLIN %d\n", on); + lwsl_notice("Unable to set accept POLLIN %d\n", on); v = v->vhost_next; } diff --git a/plugins/acme-client/protocol_lws_acme_client.c b/plugins/acme-client/protocol_lws_acme_client.c index aa0ca798..e1f33340 100644 --- a/plugins/acme-client/protocol_lws_acme_client.c +++ b/plugins/acme-client/protocol_lws_acme_client.c @@ -56,6 +56,7 @@ struct acme_connection { char replay_nonce[64]; char chall_token[64]; char challenge_uri[256]; + char detail[64]; char status[16]; char san_a[100]; char san_b[100]; @@ -105,6 +106,7 @@ struct per_vhost_data__lws_acme_client { char *pvo_data; char *pvop[LWS_TLS_TOTAL_COUNT]; + const char *pvop_active[LWS_TLS_TOTAL_COUNT]; int count_live_pss; char *dest; int pos; @@ -191,6 +193,7 @@ static const char * const jauthz_tok[] = { "challenges[].status", "challenges[].uri", "challenges[].token", + "detail" }; enum enum_jauthz_tok { JAAZ_ID_TYPE, @@ -201,6 +204,7 @@ enum enum_jauthz_tok { JAAZ_CHALLENGES_STATUS, JAAZ_CHALLENGES_URI, JAAZ_CHALLENGES_TOKEN, + JAAZ_DETAIL, }; static signed char cb_authz(struct lejp_ctx *ctx, char reason) @@ -226,6 +230,9 @@ cb_authz(struct lejp_ctx *ctx, char reason) break; case JAAZ_EXPIRES: break; + case JAAZ_DETAIL: + lws_snprintf(s->detail, sizeof(s->detail), "%s", ctx->buf); + break; case JAAZ_CHALLENGES_TYPE: if (s->is_sni_02) break; @@ -263,12 +270,14 @@ static const char * const jchac_tok[] = { "status", "uri", "token", + "error.detail" }; enum enum_jchac_tok { JCAC_TYPE, JCAC_STATUS, JCAC_URI, JCAC_TOKEN, + JCAC_DETAIL, }; static signed char cb_chac(struct lejp_ctx *ctx, char reason) @@ -300,6 +309,9 @@ cb_chac(struct lejp_ctx *ctx, char reason) sizeof(s->chall_token) - 1); s->yes |= 1; break; + case JCAC_DETAIL: + lws_snprintf(s->detail, sizeof(s->detail), "%s", ctx->buf); + break; } return 0; @@ -328,6 +340,14 @@ cb_chac(struct lejp_ctx *ctx, char reason) * (ie, just use new-cert instead of new-order, use the directory for links) */ +static int +lws_acme_report_status(struct lws_vhost *v, int state, const char *json) +{ + lws_callback_vhost_protocols_vhost(v, LWS_CALLBACK_VHOST_CERT_UPDATE, + (void *)json, state); + + return 0; +} /* * Notice: trashes i and url @@ -339,6 +359,7 @@ lws_acme_client_connect(struct lws_context *context, struct lws_vhost *vh, { const char *prot, *p; char path[200], _url[256]; + struct lws *wsi; memset(i, 0, sizeof(*i)); i->port = 443; @@ -364,13 +385,21 @@ lws_acme_client_connect(struct lws_context *context, struct lws_vhost *vh, i->pwsi = pwsi; i->protocol = "lws-acme-client"; - return lws_client_connect_via_info(i); + wsi = lws_client_connect_via_info(i); + if (!wsi) { + lws_snprintf(path, sizeof(path) - 1, + "Unable to connect to %s", url); + lwsl_notice("%s: %s\n", __func__, path); + lws_acme_report_status(vh, LWS_CUS_FAILED, path); + } + + return wsi; } static void lws_acme_finished(struct per_vhost_data__lws_acme_client *vhd) { - lwsl_notice("finishing up jws stuff\n"); + lwsl_debug("%s\n", __func__); if (vhd->ac) { if (vhd->ac->vhost) @@ -384,6 +413,9 @@ lws_acme_finished(struct per_vhost_data__lws_acme_client *vhd) lws_jwk_destroy(&vhd->jwk); vhd->ac = NULL; +#if defined(LWS_WITH_ESP32) + lws_esp32.acme = 0; /* enable scanning */ +#endif } static const char * const pvo_names[] = { @@ -399,6 +431,113 @@ static const char * const pvo_names[] = { "key-path", }; +static int +lws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd, + int bits) +{ + int n; + + if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH])) + return 0; + + strcpy(vhd->jwk.keytype, "RSA"); + lwsl_notice("Generating ACME %d-bit keypair... " + "will take a little while\n", bits); + n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, &vhd->jwk.el, + bits); + if (n) { + lwsl_notice("failed to create keypair\n"); + + return 1; + } + + lwsl_notice("...keypair generated\n"); + + if (lws_jwk_save(&vhd->jwk, + vhd->pvop[LWS_TLS_SET_AUTH_PATH])) { + lwsl_notice("unable to save %s\n", + vhd->pvop[LWS_TLS_SET_AUTH_PATH]); + + return 1; + } + + return 0; +} + +static int +lws_acme_start_acquisition(struct per_vhost_data__lws_acme_client *vhd, + struct lws_vhost *v) +{ + char buf[128]; + + /* ...and we were given enough info to do the update? */ + + if (!vhd->pvop[LWS_TLS_REQ_ELEMENT_COMMON_NAME]) + return -1; + + /* + * ...well... we should try to do something about it then... + */ + lwsl_notice("%s: ACME cert needs creating / updating: " + "vhost %s\n", __func__, lws_get_vhost_name(vhd->vhost)); + + vhd->ac = malloc(sizeof(*vhd->ac)); + memset(vhd->ac, 0, sizeof(*vhd->ac)); + + /* + * So if we don't have it, the first job is get the directory. + * + * If we already have the directory, jump straight into trying + * to register our key. + * + * We always try to register the keys... if it's not the first + * time, we will get a JSON body in the (legal, nonfatal) + * response like this + * + * { + * "type": "urn:acme:error:malformed", + * "detail": "Registration key is already in use", + * "status": 409 + * } + */ + if (!vhd->ac->urls[0][0]) { + vhd->ac->state = ACME_STATE_DIRECTORY; + lws_snprintf(buf, sizeof(buf) - 1, "%s", + vhd->pvop_active[LWS_TLS_SET_DIR_URL]); + } else { + vhd->ac->state = ACME_STATE_NEW_REG; + lws_snprintf(buf, sizeof(buf) - 1, "%s", + vhd->ac->urls[JAD_NEW_REG_URL]); + } + + vhd->ac->real_vh_port = lws_get_vhost_port(vhd->vhost); + vhd->ac->real_vh_name = lws_get_vhost_name(vhd->vhost); + vhd->ac->real_vh_iface = lws_get_vhost_iface(vhd->vhost); + + lws_acme_report_status(vhd->vhost, LWS_CUS_STARTING, NULL); + +#if defined(LWS_WITH_ESP32) + lws_acme_report_status(vhd->vhost, LWS_CUS_CREATE_KEYS, + "Generating keys, please wait"); + if (lws_acme_load_create_auth_keys(vhd, 2048)) + goto bail; + lws_acme_report_status(vhd->vhost, LWS_CUS_CREATE_KEYS, + "Auth keys created"); +#endif + + if (lws_acme_client_connect(vhd->context, vhd->vhost, + &vhd->ac->cwsi, &vhd->ac->i, buf, "GET")) + return 0; + +#if defined(LWS_WITH_ESP32) +bail: +#endif + free(vhd->ac); + vhd->ac = NULL; + + return 1; +} + static int callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) @@ -408,10 +547,11 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); char buf[LWS_PRE + 2536], *start = buf + LWS_PRE, *p = start, - *end = buf + sizeof(buf) - 1, digest[32]; - unsigned char **pp = (unsigned char **)in, *pend = in + len; - const char *content_type = "application/jose+json"; + *end = buf + sizeof(buf) - 1, digest[32], *failreason = NULL; + unsigned char **pp, *pend; + const char *content_type; const struct lws_protocol_vhost_options *pvo; + struct lws_acme_cert_aging_args *caa; struct acme_connection *ac = NULL; struct lws_genhash_ctx hctx; struct lws *cwsi; @@ -420,7 +560,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, if (vhd) ac = vhd->ac; - switch (reason) { + switch ((int)reason) { case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), @@ -461,8 +601,9 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, pvo_names[m]); n |= 1; } else - lwsl_info(" %s: %s\n", pvo_names[m], - vhd->pvop[m]); + if (vhd->pvop[m]) + lwsl_info(" %s: %s\n", pvo_names[m], + vhd->pvop[m]); if (n) { free(vhd->pvo_data); vhd->pvo_data = NULL; @@ -470,30 +611,14 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, return -1; } +#if !defined(LWS_WITH_ESP32) /* * load (or create) the registration keypair while we * still have root */ - if (lws_jwk_load(&vhd->jwk, - vhd->pvop[LWS_TLS_SET_AUTH_PATH])) { - strcpy(vhd->jwk.keytype, "RSA"); - n = lws_genrsa_new_keypair(lws_get_context(wsi), - &vhd->rsactx, &vhd->jwk.el, - 4096); - if (n) { - lwsl_notice("failed to create keypair\n"); + if (lws_acme_load_create_auth_keys(vhd, 4096)) + return 1; - return 1; - } - - if (lws_jwk_save(&vhd->jwk, - vhd->pvop[LWS_TLS_SET_AUTH_PATH])) { - lwsl_notice("unable to save %s\n", - vhd->pvop[LWS_TLS_SET_AUTH_PATH]); - - return 1; - } - } /* * in case we do an update, open the update files while we * still have root @@ -514,6 +639,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, lwsl_err("unable to create update key file %s\n", buf); return -1; } +#endif break; case LWS_CALLBACK_PROTOCOL_DESTROY: @@ -528,6 +654,8 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_VHOST_CERT_AGING: if (!vhd) break; + + caa = (struct lws_acme_cert_aging_args *)in; /* * Somebody is telling us about a cert some vhost is using. * @@ -536,64 +664,23 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, */ if ((int)(ssize_t)len > 14) break; + /* * ...is this a vhost we were configured on? */ - if (vhd->vhost != (struct lws_vhost *)in) - break; + if (vhd->vhost != caa->vh) + return 1; - /* ...and we were given enough info to do the update? */ + for (n = 0; n < (int)ARRAY_SIZE(vhd->pvop);n++) + if (caa->element_overrides[n]) + vhd->pvop_active[n] = caa->element_overrides[n]; + else + vhd->pvop_active[n] = vhd->pvop[n]; - if (!vhd->pvop[LWS_TLS_REQ_ELEMENT_COUNTRY]) - break; + lwsl_notice("starting acme acquisition on %s: %s\n", + lws_get_vhost_name(caa->vh), vhd->pvop_active[LWS_TLS_SET_DIR_URL]); - /* - * ...well... we should try to do something about it then... - */ - lwsl_notice("%s: ACME cert needs updating: " - "vhost %s: %dd left\n", __func__, - lws_get_vhost_name(in), (int)(ssize_t)len); - - vhd->ac = ac = malloc(sizeof(*vhd->ac)); - memset(ac, 0, sizeof(*ac)); - - /* - * So if we don't have it, the first job is get the directory. - * - * If we already have the directory, jump straight into trying - * to register our key. - * - * We always try to register the keys... if it's not the first - * time, we will get a JSON body in the (legal, nonfatal) - * response like this - * - * { - * "type": "urn:acme:error:malformed", - * "detail": "Registration key is already in use", - * "status": 409 - * } - */ - if (!ac->urls[0][0]) { - ac->state = ACME_STATE_DIRECTORY; - lws_snprintf(buf, sizeof(buf) - 1, "%s", - vhd->pvop[LWS_TLS_SET_DIR_URL]); - } else { - ac->state = ACME_STATE_NEW_REG; - lws_snprintf(buf, sizeof(buf) - 1, "%s", - ac->urls[JAD_NEW_REG_URL]); - } - - ac->real_vh_port = lws_get_vhost_port((struct lws_vhost *)in); - ac->real_vh_name = lws_get_vhost_name((struct lws_vhost *)in); - ac->real_vh_iface = lws_get_vhost_iface((struct lws_vhost *)in); - - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, buf, "GET"); - if (!cwsi) { - lwsl_notice("%s: acme connect failed\n", __func__); - free(vhd->ac); - vhd->ac = NULL; - } + lws_acme_start_acquisition(vhd, caa->vh); break; /* @@ -605,11 +692,15 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_notice("%s: CLIENT_CONNECTION_ERROR\n", __func__); + lwsl_notice("%s: CLIENT_CONNECTION_ERROR: %p\n", __func__, wsi); break; case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - lwsl_notice("%s: CLOSED_CLIENT_HTTP\n", __func__); + lwsl_notice("%s: CLOSED_CLIENT_HTTP: %p\n", __func__, wsi); + break; + + case LWS_CALLBACK_CLOSED: + lwsl_notice("%s: CLOSED: %p\n", __func__, wsi); break; case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: @@ -625,7 +716,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, WSI_TOKEN_REPLAY_NONCE) < 0) { lwsl_notice("%s: nonce too large\n", __func__); - return -1; + goto failed; } switch (ac->state) { @@ -687,8 +778,10 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, "\"mailto:%s\"" "],\"agreement\":\"%s\"" "}", - vhd->pvop[LWS_TLS_REQ_ELEMENT_EMAIL], + vhd->pvop_active[LWS_TLS_REQ_ELEMENT_EMAIL], ac->urls[JAD_TOS_URL]); + + puts(start); pkt_add_hdrs: ac->len = lws_jws_create_packet(&vhd->jwk, start, p - start, @@ -701,23 +794,33 @@ pkt_add_hdrs: lwsl_notice("lws_jws_create_packet failed\n"); goto failed; } + + pp = (unsigned char **)in; + pend = (*pp) + len; + ac->pos = 0; + content_type = "application/jose+json"; if (ac->state == ACME_STATE_POLLING_CSR) content_type = "application/pkix-cert"; if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, - (uint8_t *)content_type, 21, pp, pend)) + (uint8_t *)content_type, 21, pp, pend)) { + lwsl_notice("could not add content type\n"); goto failed; + } n = sprintf(buf, "%d", ac->len); if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, - (uint8_t *)buf, n, pp, pend)) + (uint8_t *)buf, n, pp, pend)) { + lwsl_notice("could not add content length\n"); goto failed; + } lws_client_http_body_pending(wsi, 1); lws_callback_on_writable(wsi); + lwsl_notice("prepare to send ACME_STATE_NEW_REG\n"); break; case ACME_STATE_NEW_AUTH: p += lws_snprintf(p, end - p, @@ -727,7 +830,7 @@ pkt_add_hdrs: "\"type\":\"http-01\"," "\"value\":\"%s\"" "}" - "}", ac->real_vh_name); + "}", vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME]); goto pkt_add_hdrs; case ACME_STATE_ACCEPT_CHALL: @@ -842,8 +945,7 @@ pkt_add_hdrs: "{\"resource\":\"new-cert\"," "\"csr\":\""); n = lws_tls_acme_sni_csr_create(vhd->context, - (const char **) - vhd->pvop, + &vhd->pvop_active[0], (uint8_t *)p, end - p, &ac->alloc_privkey_pem, &ac->len_privkey_pem); @@ -948,15 +1050,18 @@ pkt_add_hdrs: * not complete for some reason... */ ac->state = ACME_STATE_NEW_REG; + lws_acme_report_status(vhd->vhost, LWS_CUS_REG, NULL); strcpy(buf, ac->urls[JAD_NEW_REG_URL]); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); - if (!cwsi) + if (!cwsi) { lwsl_notice("%s: failed to connect to acme\n", __func__); - break; + goto failed; + } + return -1; /* close the completed client connection */ case ACME_STATE_NEW_REG: if ((ac->resp >= 200 && ac->resp < 299) || @@ -967,6 +1072,8 @@ pkt_add_hdrs: * Move on to requesting a cert auth. */ ac->state = ACME_STATE_NEW_AUTH; + lws_acme_report_status(vhd->vhost, LWS_CUS_AUTH, + NULL); strcpy(buf, ac->urls[JAD_NEW_AUTHZ_URL]); cwsi = lws_acme_client_connect(vhd->context, @@ -975,16 +1082,32 @@ pkt_add_hdrs: if (!cwsi) lwsl_notice("%s: failed to connect\n", __func__); - break; + return -1; /* close the completed client connection */ } else { lwsl_notice("new-reg replied %d\n", ac->resp); goto failed; } - break; + return -1; /* close the completed client connection */ case ACME_STATE_NEW_AUTH: lejp_destruct(&ac->jctx); - lwsl_notice("chall: %s\n", ac->chall_token); + if (ac->resp / 100 == 4) { + lws_snprintf(buf, sizeof(buf), + "Auth failed: %s", ac->detail); + failreason = buf; + lwsl_notice("auth failed\n"); + goto failed; + } + lwsl_notice("chall: %s (%d)\n", ac->chall_token, ac->resp); + if (!ac->chall_token[0]) { + lwsl_notice("no challenge\n"); + goto failed; + } + + + ac->state = ACME_STATE_ACCEPT_CHALL; + lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, + NULL); /* tls-sni-01 ... what a mess. * The stuff in @@ -1141,7 +1264,6 @@ pkt_add_hdrs: * server know we are ready to roll... */ - ac->state = ACME_STATE_ACCEPT_CHALL; ac->goes_around = 0; cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, @@ -1152,7 +1274,7 @@ pkt_add_hdrs: __func__); goto failed; } - break; + return -1; /* close the completed client connection */ case ACME_STATE_ACCEPT_CHALL: /* @@ -1176,23 +1298,18 @@ pkt_add_hdrs: __func__, ac->challenge_uri); poll_again: ac->state = ACME_STATE_POLLING; + lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, NULL); - if (ac->goes_around++ == 10) { + if (ac->goes_around++ == 20) { lwsl_notice("%s: too many chall retries\n", __func__); goto failed; } - cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, - &ac->cwsi, &ac->i, - ac->challenge_uri, - "GET"); - if (!cwsi) { - lwsl_notice("%s: failed to connect\n", - __func__); - goto failed; - } - break; + + lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, + LWS_CALLBACK_USER + 0xac33, ac->goes_around == 1 ? 10 : 2); + return -1; /* close the completed client connection */ case ACME_STATE_POLLING: @@ -1205,13 +1322,16 @@ poll_again: if (!strcmp(ac->status, "invalid")) { lwsl_notice("%s: polling failed\n", __func__); + lws_snprintf(buf, sizeof(buf), + "Challenge Invalid: %s", ac->detail); + failreason = buf; goto failed; } - lwsl_notice("Authorization accepted\n"); + lwsl_notice("Challenge passed\n"); /* - * our authorization was validated... so delete the + * The challenge was validated... so delete the * temp SNI vhost now its job is done */ if (ac->vhost) @@ -1225,6 +1345,7 @@ poll_again: * server to request the actual certs. */ ac->state = ACME_STATE_POLLING_CSR; + lws_acme_report_status(vhd->vhost, LWS_CUS_REQ, NULL); ac->goes_around = 0; strcpy(buf, ac->urls[JAD_NEW_CERT_URL]); @@ -1237,7 +1358,7 @@ poll_again: goto failed; } - break; + return -1; /* close the completed client connection */ case ACME_STATE_POLLING_CSR: /* @@ -1273,6 +1394,10 @@ poll_again: int max; lwsl_notice("The cert was sent..\n"); + + lws_acme_report_status(vhd->vhost, + LWS_CUS_ISSUE, NULL); + /* * That means we have the issued cert DER in * ac->buf, length in ac->cpos; and the key in @@ -1334,7 +1459,7 @@ poll_again: lws_ptr_diff(p, start)); free(start); if (n) { - lwsl_err("unable to write ACME cert!\n"); + lwsl_err("unable to write ACME cert! %d\n", n); goto failed; } /* @@ -1356,15 +1481,15 @@ poll_again: lwsl_notice("%s: Updated certs written for %s " "to %s.upd and %s.upd\n", __func__, - vhd->pvop[LWS_TLS_REQ_ELEMENT_COMMON_NAME], - vhd->pvop[LWS_TLS_SET_CERT_PATH], - vhd->pvop[LWS_TLS_SET_KEY_PATH]); + vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME], + vhd->pvop_active[LWS_TLS_SET_CERT_PATH], + vhd->pvop_active[LWS_TLS_SET_KEY_PATH]); /* notify lws there was a cert update */ if (lws_tls_cert_updated(vhd->context, - vhd->pvop[LWS_TLS_SET_CERT_PATH], - vhd->pvop[LWS_TLS_SET_KEY_PATH], + vhd->pvop_active[LWS_TLS_SET_CERT_PATH], + vhd->pvop_active[LWS_TLS_SET_KEY_PATH], ac->buf, ac->cpos, ac->alloc_privkey_pem, ac->len_privkey_pem)) { @@ -1372,10 +1497,14 @@ poll_again: } lws_acme_finished(vhd); + lws_acme_report_status(vhd->vhost, + LWS_CUS_SUCCESS, NULL); return 0; } + lws_acme_report_status(vhd->vhost, LWS_CUS_CONFIRM, NULL); + /* he is preparing the cert, go again with a GET */ if (ac->goes_around++ == 30) { @@ -1395,13 +1524,24 @@ poll_again: goto failed; } - break; + return -1; /* close the completed client connection */ default: break; } break; + case LWS_CALLBACK_USER + 0xac33: + cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, + &ac->cwsi, &ac->i, + ac->challenge_uri, + "GET"); + if (!cwsi) { + lwsl_notice("%s: failed to connect\n", __func__); + goto failed; + } + break; + case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS: /* * This goes to vhost->protocols[0], but for our temp certs @@ -1414,9 +1554,17 @@ poll_again: lwsl_debug("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS\n"); ac = (struct acme_connection *)lws_get_vhost_user( (struct lws_vhost *)in); + + lws_acme_report_status((struct lws_vhost *)in, + LWS_CUS_CREATE_REQ, + "creating challenge cert"); + if (lws_tls_acme_sni_cert_create((struct lws_vhost *)in, - ac->san_a, ac->san_b)) + ac->san_a, ac->san_b)) { + lwsl_err("%s: creating the sni test cert failed\n", __func__); + return -1; + } break; default: @@ -1427,6 +1575,7 @@ poll_again: failed: lwsl_err("%s: failed out\n", __func__); + lws_acme_report_status(vhd->vhost, LWS_CUS_FAILED, failreason); lws_acme_finished(vhd); return -1; diff --git a/plugins/generic-sessions/handlers.c b/plugins/generic-sessions/handlers.c index 8b8665f6..f57f44e6 100644 --- a/plugins/generic-sessions/handlers.c +++ b/plugins/generic-sessions/handlers.c @@ -72,7 +72,8 @@ lwsgs_handler_confirm(struct per_vhost_data__gs *vhd, struct lws *wsi, a.event = LWSGSE_CREATED; a.username = u.username; a.email = u.email; - lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0); + lws_callback_vhost_protocols_vhost(lws_get_vhost(wsi), + LWS_CALLBACK_GS_EVENT, &a, 0); lws_snprintf(pss->onward, sizeof(pss->onward), "%s/post-verify-ok.html", vhd->email_confirm_url); @@ -325,7 +326,8 @@ lwsgs_handler_change_password(struct per_vhost_data__gs *vhd, struct lws *wsi, a.event = LWSGSE_DELETED; a.username = u.username; a.email = ""; - lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0); + lws_callback_vhost_protocols_vhost(lws_get_vhost(wsi), + LWS_CALLBACK_GS_EVENT, &a, 0); lws_snprintf(s, sizeof(s) - 1, "delete from users where username='%s';" diff --git a/plugins/generic-sessions/protocol_lws_messageboard.c b/plugins/generic-sessions/protocol_lws_messageboard.c index f8aef733..9cc4cb26 100644 --- a/plugins/generic-sessions/protocol_lws_messageboard.c +++ b/plugins/generic-sessions/protocol_lws_messageboard.c @@ -213,7 +213,7 @@ callback_messageboard(struct lws *wsi, enum lws_callback_reasons reason, break; case LWS_CALLBACK_PROTOCOL_DESTROY: - if (vhd->pdb) + if (vhd && vhd->pdb) sqlite3_close(vhd->pdb); goto passthru; diff --git a/plugins/protocol_esp32_lws_ota.c b/plugins/protocol_esp32_lws_ota.c index cfb5748b..1fd80af5 100644 --- a/plugins/protocol_esp32_lws_ota.c +++ b/plugins/protocol_esp32_lws_ota.c @@ -33,6 +33,7 @@ struct per_session_data__esplws_ota { esp_ota_handle_t otahandle; const esp_partition_t *part; long file_length; + long last_rep; nvs_handle nvh; TimerHandle_t reboot_timer; }; @@ -117,6 +118,7 @@ ota_file_upload_cb(void *data, const char *name, const char *filename, } pss->file_length = 0; + pss->last_rep = -1; break; case LWS_UFS_FINAL_CONTENT: @@ -126,9 +128,11 @@ ota_file_upload_cb(void *data, const char *name, const char *filename, return 1; } - lwsl_notice("writing 0x%lx... 0x%lx\n", - pss->part->address + pss->file_length, - pss->part->address + pss->file_length + len); + if ((pss->file_length & ~0xffff) != (pss->last_rep & ~0xffff)) { + lwsl_notice("writing 0x%lx...\n", + pss->part->address + pss->file_length); + pss->last_rep = pss->file_length; + } if (esp_ota_write(pss->otahandle, buf, len) != ESP_OK) { lwsl_err("OTA: Failed to write\n"); return 1; diff --git a/plugins/protocol_esp32_lws_scan.c b/plugins/protocol_esp32_lws_scan.c index 0030700d..7537bd66 100644 --- a/plugins/protocol_esp32_lws_scan.c +++ b/plugins/protocol_esp32_lws_scan.c @@ -56,6 +56,8 @@ struct per_session_data__esplws_scan { unsigned char changed_partway:1; }; +#define max_aps 12 + struct per_vhost_data__esplws_scan { wifi_ap_record_t ap_records[10]; TimerHandle_t timer, reboot_timer; @@ -63,6 +65,7 @@ struct per_vhost_data__esplws_scan { struct lws_context *context; struct lws_vhost *vhost; const struct lws_protocols *protocol; + struct lws_wifi_scan *known_aps_list; const esp_partition_t *part; esp_ota_handle_t otahandle; @@ -75,6 +78,9 @@ struct per_vhost_data__esplws_scan { char json[2048]; int json_len; + int acme_state; + char acme_msg[256]; + uint16_t count_ap_records; char count_live_pss; unsigned char scan_ongoing:1; @@ -117,6 +123,9 @@ static const char * const param_names[] = { "pri", "serial", "opts", + "group", + "role", + "updsettings", }; enum enum_param_names { @@ -125,6 +134,9 @@ enum enum_param_names { EPN_PRI, EPN_SERIAL, EPN_OPTS, + EPN_GROUP, + EPN_ROLE, + EPN_UPDSETTINGS, }; @@ -159,6 +171,9 @@ scan_start(struct per_vhost_data__esplws_scan *vhd) if (vhd->scan_ongoing) return; + if (lws_esp32.acme) + return; + vhd->scan_ongoing = 1; lws_esp32.scan_consumer = scan_finished; lws_esp32.scan_consumer_arg = vhd; @@ -167,10 +182,17 @@ scan_start(struct per_vhost_data__esplws_scan *vhd) lwsl_err("scan start failed %d\n", n); } +static char scan_defer; + static void timer_cb(TimerHandle_t t) { struct per_vhost_data__esplws_scan *vhd = pvTimerGetTimerID(t); + if (!lws_esp32.inet && (scan_defer & 1)) { + /* if connected in AP mode, wait twice as long between scans */ + return; + } + scan_start(vhd); } @@ -214,11 +236,101 @@ client_connection(struct per_vhost_data__esplws_scan *vhd, const char *file) return 0; /* ongoing */ } +static int +lws_wifi_scan_rssi(struct lws_wifi_scan *p) +{ + if (!p->count) + return -127; + + return p->rssi / p->count; +} + +/* + * Insert new lws_wifi_scan into linkedlist in rssi-sorted order, trimming the + * list if needed to keep it at or below max_aps entries. + */ + +static int +lws_wifi_scan_insert_trim(struct lws_wifi_scan **list, struct lws_wifi_scan *ns) +{ + int count = 0, ins = 1, worst; + struct lws_wifi_scan *newlist, **pworst, *pp1; + + lws_start_foreach_llp(struct lws_wifi_scan **, pp, *list) { + /* try to find existing match */ + if (!strcmp((*pp)->ssid, ns->ssid) && + !memcmp((*pp)->bssid, ns->bssid, 6)) { + if ((*pp)->count > 127) { + (*pp)->count /= 2; + (*pp)->rssi /= 2; + } + (*pp)->rssi += ns->rssi; + (*pp)->count++; + ins = 0; + break; + } + } lws_end_foreach_llp(pp, next); + + if (ins) { + lws_start_foreach_llp(struct lws_wifi_scan **, pp, *list) { + /* trim any excess guys */ + if (count++ >= max_aps - 1) { + pp1 = *pp; + *pp = (*pp)->next; + free(pp1); + continue; /* stay where we are */ + } + } lws_end_foreach_llp(pp, next); + + /* we are inserting... so alloc a copy of him */ + pp1 = malloc(sizeof(*pp1)); + if (!pp1) + return -1; + + memcpy(pp1, ns, sizeof(*pp1)); + pp1->next = *list; + *list = pp1; + } + + /* sort the list ... worst first, but added at the newlist head */ + + newlist = NULL; + + /* while anybody left on the old list */ + while (*list) { + worst = 0; + pworst = NULL; + + /* who is the worst guy still left on the old list? */ + lws_start_foreach_llp(struct lws_wifi_scan **, pp, *list) { + if (lws_wifi_scan_rssi(*pp) <= worst) { + worst = lws_wifi_scan_rssi(*pp); + pworst = pp; + } + } lws_end_foreach_llp(pp, next); + + if (pworst) { + /* move the worst to the head of the new list */ + pp1 = *pworst; + *pworst = (*pworst)->next; + pp1->next = newlist; + newlist = pp1; + } + } + + *list = newlist; + + return 0; +} + static void scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v) { struct per_vhost_data__esplws_scan *vhd = v; struct per_session_data__esplws_scan *p = vhd->live_pss_list; + struct lws_wifi_scan lws; + wifi_ap_record_t *r; + int m; lwsl_notice("%s: count %d\n", __func__, count); @@ -232,13 +344,31 @@ scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v) memcpy(vhd->ap_records, recs, vhd->count_ap_records * sizeof(*recs)); while (p) { - if (p->scan_state != SCAN_STATE_INITIAL && p->scan_state != SCAN_STATE_NONE) + if (p->scan_state != SCAN_STATE_INITIAL && + p->scan_state != SCAN_STATE_NONE) p->changed_partway = 1; else p->scan_state = SCAN_STATE_INITIAL; p = p->next; } + /* convert to generic, cumulative scan results */ + + for (m = 0; m < vhd->count_ap_records; m++) { + + r = &vhd->ap_records[m]; + + lws.authmode = r->authmode; + lws.channel = r->primary; + lws.rssi = r->rssi; + lws.count = 1; + memcpy(&lws.bssid, r->bssid, 6); + strncpy(lws.ssid, (const char *)r->ssid, sizeof(lws.ssid) - 1); + lws.ssid[sizeof(lws.ssid) - 1] = '\0'; + + lws_wifi_scan_insert_trim(&vhd->known_aps_list, &lws); + } + lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol); if (lws_esp32.inet && !vhd->cwsi && !vhd->checked_updates) @@ -320,9 +450,8 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, unsigned char *start = pss->buffer + LWS_PRE - 1, *p = start, *end = pss->buffer + sizeof(pss->buffer) - 1; union lws_tls_cert_info_results ir; + struct lws_wifi_scan *lwscan; char subject[64]; - const char *pp; - wifi_ap_record_t *r; int n, m; nvs_handle nvh; size_t s; @@ -341,7 +470,7 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, (TimerCallbackFunction_t)timer_cb); vhd->scan_ongoing = 0; strcpy(vhd->json, " { }"); - scan_start(vhd); + // scan_start(vhd); break; case LWS_CALLBACK_PROTOCOL_DESTROY: @@ -354,17 +483,17 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_ESTABLISHED: lwsl_notice("%s: ESTABLISHED\n", __func__); if (!vhd->live_pss_list) { - scan_start(vhd); + // scan_start(vhd); xTimerStart(vhd->timer, 0); } vhd->count_live_pss++; pss->next = vhd->live_pss_list; vhd->live_pss_list = pss; /* if we have scan results, update them. Otherwise wait */ - if (vhd->count_ap_records) { +// if (vhd->count_ap_records) { pss->scan_state = SCAN_STATE_INITIAL; lws_callback_on_writable(wsi); - } +// } break; case LWS_CALLBACK_SERVER_WRITEABLE: @@ -481,6 +610,10 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, " \"unixtime\":\"%llu\",\n" " \"certissuer\":\"%s\",\n" " \"certsubject\":\"%s\",\n" + " \"le_dns\":\"%s\",\n" + " \"le_email\":\"%s\",\n" + " \"acme_state\":\"%d\",\n" + " \"acme_msg\":\"%s\",\n" " \"button\":\"%d\",\n" " \"group\":\"%s\",\n" " \"role\":\"%s\",\n", @@ -499,6 +632,10 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, vhd->cert_remaining_days, (unsigned long long)t.tv_sec, ir.ns.name, subject, + lws_esp32.le_dns, + lws_esp32.le_email, + vhd->acme_state, + vhd->acme_msg, ((volatile struct lws_esp32 *)(&lws_esp32))->button_is_down, group, role); p += snprintf((char *)p, end - p, @@ -561,6 +698,7 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, ssid, pp, use); } nvs_close(nvh); + pss->ap_record = 0; p += snprintf((char *)p, end - p, "], \"aps\":[\n"); @@ -570,27 +708,36 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, break; case SCAN_STATE_LIST: + lwscan = vhd->known_aps_list; + + n = pss->ap_record; + while (lwscan && n--) + lwscan = lwscan->next; + for (m = 0; m < 6; m++) { n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN; - if (pss->ap_record >= vhd->count_ap_records) + if (!lwscan) goto scan_state_final; if (pss->subsequent) *p++ = ','; pss->subsequent = 1; + pss->ap_record++; - r = &vhd->ap_records[(int)pss->ap_record++]; p += snprintf((char *)p, end - p, "{\"ssid\":\"%s\",\n" "\"bssid\":\"%02X:%02X:%02X:%02X:%02X:%02X\",\n" "\"rssi\":\"%d\",\n" "\"chan\":\"%d\",\n" "\"auth\":\"%d\"}\n", - r->ssid, - r->bssid[0], r->bssid[1], r->bssid[2], - r->bssid[3], r->bssid[4], r->bssid[5], - r->rssi, r->primary, r->authmode); - if (pss->ap_record >= vhd->count_ap_records) + lwscan->ssid, + lwscan->bssid[0], lwscan->bssid[1], lwscan->bssid[2], + lwscan->bssid[3], lwscan->bssid[4], lwscan->bssid[5], + lws_wifi_scan_rssi(lwscan), + lwscan->channel, lwscan->authmode); + + lwscan = lwscan->next; + if (!lwscan) pss->scan_state = SCAN_STATE_FINAL; } break; @@ -600,6 +747,7 @@ scan_state_final: n = LWS_WRITE_CONTINUATION; p += sprintf((char *)p, "]\n}\n"); if (pss->changed_partway) { + pss->changed_partway = 0; pss->subsequent = 0; pss->scan_state = SCAN_STATE_INITIAL; } else { @@ -622,6 +770,17 @@ issue: break; + case LWS_CALLBACK_VHOST_CERT_UPDATE: + lwsl_notice("LWS_CALLBACK_VHOST_CERT_UPDATE: %d\n", (int)len); + vhd->acme_state = (int)len; + if (in) { + strncpy(vhd->acme_msg, in, sizeof(vhd->acme_msg) - 1); + vhd->acme_msg[sizeof(vhd->acme_msg) - 1] = '\0'; + lwsl_notice("acme_msg: %s\n", (char *)in); + } + lws_callback_on_writable_all_protocol_vhost(vhd->vhost, vhd->protocol); + break; + case LWS_CALLBACK_RECEIVE: { const char *sect = "\"app\": {", *b; @@ -644,6 +803,10 @@ issue: if (strstr((const char *)in, "\"reset\"")) goto sched_reset; + if (!strncmp((const char *)in, "{\"job\":\"start-le\"", 17)) + goto start_le; + + if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) { lwsl_err("Unable to open nvs\n"); break; @@ -662,16 +825,6 @@ issue: if (n == 8 && lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) continue; - //lwsl_notice("%s: %s '%s'\n", __func__, store_json[n].nvs, p); - if (n == 9) { - strncpy(lws_esp32.group, p, sizeof(lws_esp32.group) - 1); - lws_esp32.group[sizeof(lws_esp32.group) - 1] = '\0'; - } - if (n == 10) { - strncpy(lws_esp32.role, p, sizeof(lws_esp32.role) - 1); - lws_esp32.role[sizeof(lws_esp32.role) - 1] = '\0'; - } - if (lws_nvs_set_str(nvh, store_json[n].nvs, p) != ESP_OK) { lwsl_err("Unable to store %s in nvm\n", store_json[n].nvs); goto bail_nvs; @@ -767,6 +920,75 @@ auton: vhd->autonomous_update = 0; break; + +start_le: + lws_esp32.acme = 1; /* hold off scanning */ + puts(in); + /* + * {"job":"start-le","cn":"home.warmcat.com", + * "email":"andy@warmcat.com", "staging":"true"} + */ + + if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) { + lwsl_err("Unable to open nvs\n"); + break; + } + + n = 0; + b = strstr(in, ",\"cn\":\""); + if (b) { + b += 7; + while (*b && *b != '\"' && n < sizeof(lws_esp32.le_dns) - 1) + lws_esp32.le_dns[n++] = *b++; + } + lws_esp32.le_dns[n] = '\0'; + + lws_nvs_set_str(nvh, "acme-cn", lws_esp32.le_dns); + n = 0; + b = strstr(in, ",\"email\":\""); + if (b) { + b += 10; + while (*b && *b != '\"' && n < sizeof(lws_esp32.le_email) - 1) + lws_esp32.le_email[n++] = *b++; + } + lws_esp32.le_email[n] = '\0'; + lws_nvs_set_str(nvh, "acme-email", lws_esp32.le_email); + nvs_commit(nvh); + + nvs_close(nvh); + + n = 1; + b = strstr(in, ",\"staging\":\""); + if (b) + lwsl_notice("staging: %s\n", b); + if (b && b[12] == 'f') + n = 0; + + lwsl_notice("cn: %s, email: %s, staging: %d\n", lws_esp32.le_dns, lws_esp32.le_email, n); + + { + struct lws_acme_cert_aging_args caa; + + memset(&caa, 0, sizeof(caa)); + caa.vh = vhd->vhost; + + caa.element_overrides[LWS_TLS_REQ_ELEMENT_COMMON_NAME] = lws_esp32.le_dns; + caa.element_overrides[LWS_TLS_REQ_ELEMENT_EMAIL] = lws_esp32.le_email; + + if (n) + caa.element_overrides[LWS_TLS_SET_DIR_URL] = + "https://acme-staging.api.letsencrypt.org/directory"; /* staging */ + else + caa.element_overrides[LWS_TLS_SET_DIR_URL] = + "https://acme-v01.api.letsencrypt.org/directory"; /* real */ + + lws_callback_vhost_protocols_vhost(vhd->vhost, + LWS_CALLBACK_VHOST_CERT_AGING, + (void *)&caa, 0); + } + + break; + } case LWS_CALLBACK_CLOSED: @@ -802,7 +1024,7 @@ auton: pss->filename[0] = '\0'; pss->file_length = 0; } - + //puts((const char *)in); /* let it parse the POST data */ if (lws_spa_process(pss->spa, in, len)) return -1; @@ -813,6 +1035,14 @@ auton: /* call to inform no more payload data coming */ lws_spa_finalize(pss->spa); + for (n = 0; n < ARRAY_SIZE(param_names); n++) + if (lws_spa_get_string(pss->spa, n)) + lwsl_notice(" Param %s: %s\n", param_names[n], + lws_spa_get_string(pss->spa, n)); + else + lwsl_notice(" Param %s: (none)\n", + param_names[n]); + if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) { lwsl_err("Unable to open nvs\n"); break; @@ -838,15 +1068,29 @@ auton: nvs_commit(nvh); } } + + if (lws_spa_get_string(pss->spa, EPN_GROUP)) { + if (lws_nvs_set_str(nvh, "group", lws_spa_get_string(pss->spa, EPN_GROUP)) != ESP_OK) { + lwsl_err("Unable to store group in nvm\n"); + goto bail_nvs; + } + + nvs_commit(nvh); + } + + if (lws_spa_get_string(pss->spa, EPN_ROLE)) { + if (lws_nvs_set_str(nvh, "role", lws_spa_get_string(pss->spa, EPN_ROLE)) != ESP_OK) { + lwsl_err("Unable to store group in nvm\n"); + goto bail_nvs; + } + + nvs_commit(nvh); + } + nvs_close(nvh); - pp = lws_spa_get_string(pss->spa, EPN_SERIAL); - if (!pp) - pp = "unknown"; pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1, - "Rebooting after storing certs...
connect to AP 'config-%s-%s' and continue here: " - "https://192.168.4.1", - lws_esp32.model, pp); + "OK"); if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) goto bail; @@ -860,11 +1104,7 @@ auton: goto bail; n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); - if (n < 0) - goto bail; - - lws_callback_on_writable(wsi); - break; + goto bail; case LWS_CALLBACK_HTTP_WRITEABLE: lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",