From 3d98e29518b1e7d28ecca1a0e492be4ceff75cfa Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 25 Jan 2022 19:31:36 +0000 Subject: [PATCH] raw: tls conns Raw + tls needs a little extra handling during connect. --- lib/roles/raw-skt/ops-raw-skt.c | 74 +++++- .../raw/minimal-raw-client/CMakeLists.txt | 23 ++ .../raw/minimal-raw-client/main.c | 214 ++++++++++++++++++ 3 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 minimal-examples-lowlevel/raw/minimal-raw-client/CMakeLists.txt create mode 100644 minimal-examples-lowlevel/raw/minimal-raw-client/main.c diff --git a/lib/roles/raw-skt/ops-raw-skt.c b/lib/roles/raw-skt/ops-raw-skt.c index fd4fd341a..453c64e75 100644 --- a/lib/roles/raw-skt/ops-raw-skt.c +++ b/lib/roles/raw-skt/ops-raw-skt.c @@ -24,6 +24,57 @@ #include +#if defined(LWS_WITH_CLIENT) +static int +lws_raw_skt_connect(struct lws *wsi) +{ + int n; +#if defined(LWS_WITH_TLS) + const char *cce = NULL; + char ccebuf[128]; + +#if !defined(LWS_WITH_SYS_ASYNC_DNS) + switch (lws_client_create_tls(wsi, &cce, 1)) { +#else + switch (lws_client_create_tls(wsi, &cce, 0)) { +#endif + case CCTLS_RETURN_ERROR: + lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce)); + return -1; + case CCTLS_RETURN_RETRY: + return 0; + case CCTLS_RETURN_DONE: + break; + } + + if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { + n = lws_ssl_client_connect2(wsi, ccebuf, sizeof(ccebuf)); + if (n < 0) { + lws_inform_client_conn_fail(wsi, (void *)ccebuf, + strlen(ccebuf)); + + return -1; + } + if (n != 1) + return 0; /* wait */ + } +#endif + + n = user_callback_handle_rxflow(wsi->a.protocol->callback, + wsi, wsi->role_ops->adoption_cb[lwsi_role_server(wsi)], + wsi->user_space, NULL, 0); + if (n) { + lws_inform_client_conn_fail(wsi, (void *)"user", 4); + return 1; + } + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lwsi_set_state(wsi, LRS_ESTABLISHED); + + return 1; /* success */ +} +#endif + static int rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi, struct lws_pollfd *pollfd) @@ -82,6 +133,14 @@ rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi, case LRS_WAITING_CONNECT: goto nope; + case LRS_WAITING_SSL: +#if defined(LWS_WITH_CLIENT) + n = lws_raw_skt_connect(wsi); + if (n < 0) + goto fail; +#endif + break; + #if defined(LWS_WITH_SOCKS5) /* SOCKS Greeting Reply */ @@ -177,16 +236,21 @@ try_pollout: return LWS_HPI_RET_HANDLED; #if defined(LWS_WITH_CLIENT) - if (lwsi_state(wsi) == LRS_WAITING_CONNECT && - !lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL)) + if (lwsi_state(wsi) == LRS_WAITING_CONNECT) { + if (!lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL)) return LWS_HPI_RET_WSI_ALREADY_DIED; + + if (lws_raw_skt_connect(wsi) < 0) + goto fail; + } #endif + if (lwsi_state(wsi) == LRS_WAITING_SSL) + return LWS_HPI_RET_HANDLED; + /* one shot */ - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_notice("%s a\n", __func__); + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; - } /* clear back-to-back write detection */ wsi->could_have_pending = 0; diff --git a/minimal-examples-lowlevel/raw/minimal-raw-client/CMakeLists.txt b/minimal-examples-lowlevel/raw/minimal-raw-client/CMakeLists.txt new file mode 100644 index 000000000..4ff5c55ca --- /dev/null +++ b/minimal-examples-lowlevel/raw/minimal-raw-client/CMakeLists.txt @@ -0,0 +1,23 @@ +project(lws-minimal-raw-client C) +cmake_minimum_required(VERSION 2.8.12) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-minimal-raw-client) +set(SRCS main.c) + +set(requirements 1) +require_lws_config(LWS_WITH_CLIENT 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() +endif() diff --git a/minimal-examples-lowlevel/raw/minimal-raw-client/main.c b/minimal-examples-lowlevel/raw/minimal-raw-client/main.c new file mode 100644 index 000000000..db18a99eb --- /dev/null +++ b/minimal-examples-lowlevel/raw/minimal-raw-client/main.c @@ -0,0 +1,214 @@ +/* + * lws-minimal-raw-client + * + * Written in 2010-2022 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates connecting a "raw" client connection + */ + +#include +#include +#include +#if !defined(WIN32) +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#if !defined(WIN32) +#include +#endif +#include + +#include + +static struct lws *raw_wsi, *stdin_wsi; +static uint8_t buf[LWS_PRE + 4096]; +static int waiting, interrupted; +static struct lws_context *context; +static int us_wait_after_input_close = LWS_USEC_PER_SEC / 10; + +static const char *server = "libwebsockets.org", *port = "443"; + +static int +callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + const char *cp = (const char *)in; + + switch (reason) { + + /* callbacks related to file descriptor */ + + case LWS_CALLBACK_RAW_ADOPT_FILE: + lwsl_user("LWS_CALLBACK_RAW_ADOPT_FILE\n"); + break; + + case LWS_CALLBACK_RAW_CLOSE_FILE: + lwsl_user("LWS_CALLBACK_RAW_CLOSE_FILE\n"); + /* stdin close, wait 1s then close the raw skt */ + stdin_wsi = NULL; /* invalid now we close */ + if (raw_wsi) + lws_set_timer_usecs(raw_wsi, us_wait_after_input_close); + else { + interrupted = 1; + lws_cancel_service(context); + } + break; + + case LWS_CALLBACK_RAW_RX_FILE: + lwsl_user("LWS_CALLBACK_RAW_RX_FILE\n"); + waiting = (int)read(0, buf, sizeof(buf)); + lwsl_notice("raw file read %d\n", waiting); + if (waiting < 0) + return -1; + + if (raw_wsi) + lws_callback_on_writable(raw_wsi); + lws_rx_flow_control(wsi, 0); + break; + + + /* callbacks related to raw socket descriptor */ + + case LWS_CALLBACK_RAW_ADOPT: + lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_RAW_CONNECTED: + lwsl_user("LWS_CALLBACK_RAW_CONNECTED\n"); + break; + + case LWS_CALLBACK_RAW_CLOSE: + lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); + /* + * If the socket to the remote server closed, we must close + * and drop any remaining stdin + */ + interrupted = 1; + lws_cancel_service(context); + /* our pointer to this wsi is invalid now we close */ + raw_wsi = NULL; + break; + + case LWS_CALLBACK_RAW_RX: + lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); + while (len--) + putchar(*cp++); + fflush(stdout); + break; + + case LWS_CALLBACK_RAW_WRITEABLE: + lwsl_user("LWS_CALLBACK_RAW_WRITEABLE\n"); + // lwsl_hexdump_info(buf, waiting); + if (!waiting) + break; + if (stdin_wsi) + lws_rx_flow_control(stdin_wsi, 1); + if (lws_write(wsi, buf, (unsigned int)waiting, LWS_WRITE_RAW) != waiting) { + lwsl_notice("%s: raw skt write failed\n", __func__); + + return -1; + } + break; + + case LWS_CALLBACK_TIMER: + lwsl_user("LWS_CALLBACK_TIMER\n"); + interrupted = 1; + lws_cancel_service(context); + return -1; + + default: + break; + } + + return 0; +} + +static struct lws_protocols protocols[] = { + { "raw-test", callback_raw_test, 0, 0, 0, NULL, 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static int +system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, + int current, int target) +{ + struct lws_client_connect_info i; + + if (current != LWS_SYSTATE_OPERATIONAL || + target != LWS_SYSTATE_OPERATIONAL) + return 0; + + memset(&i, 0, sizeof i); + i.context = context; + i.method = "RAW"; + i.ssl_connection = LCCSCF_USE_SSL; + i.alpn = "http/1.1"; + i.address = server; + i.host = server; + i.port = atoi(port); + i.local_protocol_name = "raw-test"; + + waiting = lws_snprintf((char *)buf, sizeof(buf), "GET / HTTP/1.1\xaHost: libwebsockets.org\xa\xa"); + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("Client creation failed\n"); + interrupted = 1; + } + + return 0; +} + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + const char *p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + lws_state_notify_link_t notifier = { { NULL, NULL, NULL }, + system_notify_cb, "app" }; + lws_state_notify_link_t *na[] = { ¬ifier, NULL }; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal raw client\n"); + + memset(&info, 0, sizeof info); + + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.port = CONTEXT_PORT_NO_LISTEN_SERVER; + info.protocols = protocols; + info.register_notifier_list = na; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 0); + + lwsl_user("%s: destroying context\n", __func__); + + lws_context_destroy(context); + + return 0; +}