diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/CMakeLists.txt b/minimal-examples/ws-server/minimal-ws-raw-proxy/CMakeLists.txt new file mode 100644 index 000000000..373021246 --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/CMakeLists.txt @@ -0,0 +1,25 @@ +project(lws-minimal-ws-raw-proxy 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-ws-raw-proxy) +set(SRCS minimal-ws-raw-proxy.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_WS 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) +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() \ No newline at end of file diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/README.md b/minimal-examples/ws-server/minimal-ws-raw-proxy/README.md new file mode 100644 index 000000000..687162ebe --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/README.md @@ -0,0 +1,54 @@ +# lws minimal ws - raw proxy + +This demonstrates how to use a proxy connection object to bind together two or +more connections in a proxy. This particular example has a ws server that +creates an onward "raw" client connection to 127.0.0.1:1234. + +You can make a suitable "raw server" with + +``` +$ nc -l 127.0.0.1 1234 +``` + +## build + +``` + $ cmake . && make +``` + +## Commandline Options + +Option|Meaning +---|--- +-d|Set logging verbosity + + +## usage + +``` + $ ./lws-minimal-ws-raw-proxy +[2021/03/04 21:14:45:0540] U: LWS minimal ws-raw proxy | visit http://localhost:7681 (-s = use TLS / https) +[2021/03/04 21:14:45:0898] N: LWS: 4.1.99-v4.1.0-294-g2776b4ce65, loglevel 1031 +[2021/03/04 21:14:45:0902] N: NET CLI SRV H1 H2 WS SS-JSON-POL SSPROX IPV6-on +[2021/03/04 21:14:45:1146] N: ++ [3224086|wsi|0|pipe] (1) +[2021/03/04 21:14:45:1203] N: ++ [3224086|vh|0|netlink] (1) +[2021/03/04 21:14:45:1284] N: ++ [3224086|vh|1|localhost||7681] (2) +[2021/03/04 21:14:45:1401] N: lws_socket_bind: nowsi: source ads :: +[2021/03/04 21:14:45:1425] N: ++ [3224086|wsi|1|listen|localhost||7681] (2) +[2021/03/04 21:14:46:1164] N: ++ [3224086|wsisrv|0|adopted] (1) +[2021/03/04 21:14:46:2771] N: ++ [3224086|wsisrv|1|adopted] (2) +[2021/03/04 21:14:46:3159] N: ++ [3224086|wsicli|0|RAW/raw-skt/127.0.0.1] (1) +[2021/03/04 21:14:46:3451] N: ++ [3224086|wsisrv|2|adopted] (3) + +``` + +Visit http://localhost:7681 in a browser... it loads JS that opens a ws +connection to the proxy's ws server side. That causes the proxy to open a +raw client connection to 127.0.0.1:1234, and forward anything you type in the +browser to the raw server, and anything typed in the raw server (you must +press enter on netcat to get it sent) is proxied back to the browser. + +The proxy can handle many ws connections each with their individual onward +raw client connections, so you could open multiple browser windows. But you +will need a better "raw server" than netcat, which is restricted to just the +one peer at a time. \ No newline at end of file diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/localhost-100y.cert b/minimal-examples/ws-server/minimal-ws-raw-proxy/localhost-100y.cert new file mode 100644 index 000000000..6f372db40 --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/localhost-100y.cert @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +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 +e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= +-----END CERTIFICATE----- diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/localhost-100y.key b/minimal-examples/ws-server/minimal-ws-raw-proxy/localhost-100y.key new file mode 100644 index 000000000..148f8598e --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/localhost-100y.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fBrpEY1IATtPq1taBZZogRqI3rOkkPk= +-----END PRIVATE KEY----- diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/minimal-ws-raw-proxy.c b/minimal-examples/ws-server/minimal-ws-raw-proxy/minimal-ws-raw-proxy.c new file mode 100644 index 000000000..4fd5cb8ed --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/minimal-ws-raw-proxy.c @@ -0,0 +1,459 @@ +/* + * lws-minimal-ws-raw-proxy + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates a ws (server) -> raw (client) proxy, it's a ws server + * that accepts connections, creates an onward client connection to some other + * no-protocol server, eg, nc -l 127.0.0.1 1234 + * + * The idea is to show the general approach for making async proxies using lws + * that are robust and valgrind-clean. + * + * There's no vhd or pss on either side. Instead when the ws server gets an + * incoming connection and negotiates the ws link, he creates an object + * representing the proxied connection, it is not destroyed automatically when + * any particular wsi is closed, instead the last wsi that is part of the + * proxied connection destroys it when he is closed. + */ + +#include +#include +#include +#include + +/* one of these created for each pending message that is to be forwarded */ + +typedef struct proxy_msg { + lws_dll2_t list; + size_t len; + /* + * the packet content is overallocated here, if p is a pointer to + * this struct, you can get a pointer to the message contents by + * ((uint8_t)&p[1]) + LWS_PRE. + * + * Notice we additionally take care to overallocate LWS_PRE before the + * actual message data, so we can simplify sending it. + */ +} proxy_msg_t; + +/* + * One of these is created when a inbound ws connection joins, it represents + * the proxy action provoked by that. + */ + +typedef struct proxy_conn { + struct lws *wsi_ws; /* wsi for the inbound ws conn */ + struct lws *wsi_raw; /* wsi for the outbound raw conn */ + + lws_dll2_owner_t pending_msg_to_ws; + lws_dll2_owner_t pending_msg_to_raw; +} proxy_conn_t; + + +static int +proxy_ws_raw_msg_destroy(struct lws_dll2 *d, void *user) +{ + proxy_msg_t *msg = lws_container_of(d, proxy_msg_t, list); + + lws_dll2_remove(d); + free(msg); + + return 0; +} + +/* + * First the ws server side + */ + +static int +callback_proxy_ws_server(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + proxy_conn_t *pc = (proxy_conn_t *)lws_get_opaque_user_data(wsi); + struct lws_client_connect_info i; + proxy_msg_t *msg; + uint8_t *data; + int m, a; + + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + /* so let's create the proxy connection object */ + pc = malloc(sizeof(*pc)); + memset(pc, 0, sizeof(*pc)); + + /* mark this accepted ws connection with the proxy conn obj */ + lws_set_opaque_user_data(wsi, pc); + /* tell the proxy conn object that we are the ws side of it */ + pc->wsi_ws = wsi; + + /* + * For this example proxy, our job is to create a new, onward, + * raw client connection to proxy stuff on to + */ + + memset(&i, 0, sizeof(i)); + + i.method = "RAW"; + i.context = lws_get_context(wsi); + i.port = 1234; + i.address = "127.0.0.1"; + i.ssl_connection = 0; + i.local_protocol_name = "lws-ws-raw-raw"; + + /* also mark the onward, raw client conn with the proxy_conn */ + i.opaque_user_data = pc; + /* if it succeeds, set the wsi into the proxy_conn */ + i.pwsi = &pc->wsi_raw; + + if (!lws_client_connect_via_info(&i)) { + lwsl_warn("%s: onward connection failed\n", __func__); + return -1; /* hang up on the ws client, triggering + * _CLOSE flow */ + } + + break; + + case LWS_CALLBACK_CLOSED: + /* + * Clean up any pending messages to us that are never going + * to get delivered now, we are in the middle of closing + */ + lws_dll2_foreach_safe(&pc->pending_msg_to_ws, NULL, + proxy_ws_raw_msg_destroy); + + /* + * Remove our pointer from the proxy_conn... we are about to + * be destroyed. + */ + pc->wsi_ws = NULL; + lws_set_opaque_user_data(wsi, NULL); + + if (!pc->wsi_raw) { + /* + * The onward raw conn either never got started or is + * already closed... then we are the last guy still + * holding on to the proxy_conn... and we're going away + * so let's destroy it + */ + + free(pc); + break; + } + + /* + * Onward conn still alive... + * does he have stuff left to deliver? + */ + if (pc->pending_msg_to_raw.count) { + /* + * Yes, let him get on with trying to send + * the remaining pieces... but put a time limit + * on how hard he will try now the ws part is + * disappearing... give him 3s + */ + lws_set_timeout(pc->wsi_raw, + PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE, 3); + break; + } + /* + * Onward raw client conn doesn't have anything left + * to do, let's close him right after this, he will take care to + * destroy the proxy_conn when he goes down after he sees we + * have already been closed + */ + + lws_wsi_close(pc->wsi_raw, LWS_TO_KILL_ASYNC); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + if (!pc || !pc->pending_msg_to_ws.count) + break; + + msg = lws_container_of(pc->pending_msg_to_ws.head, + proxy_msg_t, list); + data = (uint8_t *)&msg[1] + LWS_PRE; + + /* notice we allowed for LWS_PRE in the payload already */ + m = lws_write(wsi, data, msg->len, LWS_WRITE_TEXT); + a = (int)msg->len; + lws_dll2_remove(&msg->list); + free(msg); + + if (m < a) { + lwsl_err("ERROR %d writing to ws\n", m); + return -1; + } + + /* + * If more to do... + */ + if (pc->pending_msg_to_ws.count) + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_RECEIVE: + if (!pc || !pc->wsi_raw) + break; + + /* notice we over-allocate by LWS_PRE + rx len */ + msg = (proxy_msg_t *)malloc(sizeof(*msg) + LWS_PRE + len); + data = (uint8_t *)&msg[1] + LWS_PRE; + + if (!msg) { + lwsl_user("OOM: dropping\n"); + break; + } + + memset(msg, 0, sizeof(*msg)); + msg->len = len; + memcpy(data, in, len); + + /* add us on to the list of packets to send to the onward conn */ + lws_dll2_add_tail(&msg->list, &pc->pending_msg_to_raw); + + /* ask to send on the onward proxy client conn */ + lws_callback_on_writable(pc->wsi_raw); + break; + + default: + break; + } + + return 0; +} + +/* + * Then the onward, raw client side + */ + +static int +callback_proxy_raw_client(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + proxy_conn_t *pc = (proxy_conn_t *)lws_get_opaque_user_data(wsi); + proxy_msg_t *msg; + uint8_t *data; + int m, a; + + switch (reason) { + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_warn("%s: onward raw connection failed\n", __func__); + pc->wsi_raw = NULL; + break; + + case LWS_CALLBACK_RAW_ADOPT: + lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); + pc->wsi_raw = wsi; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_RAW_CLOSE: + lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); + /* + * Clean up any pending messages to us that are never going + * to get delivered now, we are in the middle of closing + */ + lws_dll2_foreach_safe(&pc->pending_msg_to_raw, NULL, + proxy_ws_raw_msg_destroy); + + /* + * Remove our pointer from the proxy_conn... we are about to + * be destroyed. + */ + pc->wsi_raw = NULL; + lws_set_opaque_user_data(wsi, NULL); + + if (!pc->wsi_ws) { + /* + * The original ws conn is already closed... then we are + * the last guy still holding on to the proxy_conn... + * and we're going away, so let's destroy it + */ + + free(pc); + break; + } + + /* + * Original ws conn still alive... + * does he have stuff left to deliver? + */ + if (pc->pending_msg_to_ws.count) { + /* + * Yes, let him get on with trying to send + * the remaining pieces... but put a time limit + * on how hard he will try now the raw part is + * disappearing... give him 3s + */ + lws_set_timeout(pc->wsi_ws, + PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE, 3); + break; + } + /* + * Original ws client conn doesn't have anything left + * to do, let's close him right after this, he will take care to + * destroy the proxy_conn when he goes down after he sees we + * have already been closed + */ + + lws_wsi_close(pc->wsi_ws, LWS_TO_KILL_ASYNC); + break; + + case LWS_CALLBACK_RAW_RX: + lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); + if (!pc || !pc->wsi_ws) + break; + + /* notice we over-allocate by LWS_PRE + rx len */ + msg = (proxy_msg_t *)malloc(sizeof(*msg) + LWS_PRE + len); + data = (uint8_t *)&msg[1] + LWS_PRE; + + if (!msg) { + lwsl_user("OOM: dropping\n"); + break; + } + + memset(msg, 0, sizeof(*msg)); + msg->len = len; + memcpy(data, in, len); + + /* add us on to the list of packets to send to the onward conn */ + lws_dll2_add_tail(&msg->list, &pc->pending_msg_to_ws); + + /* ask to send on the onward proxy client conn */ + lws_callback_on_writable(pc->wsi_ws); + break; + + case LWS_CALLBACK_RAW_WRITEABLE: + lwsl_user("LWS_CALLBACK_RAW_WRITEABLE\n"); + if (!pc || !pc->pending_msg_to_raw.count) + break; + + msg = lws_container_of(pc->pending_msg_to_raw.head, + proxy_msg_t, list); + data = (uint8_t *)&msg[1] + LWS_PRE; + + /* notice we allowed for LWS_PRE in the payload already */ + m = lws_write(wsi, data, msg->len, LWS_WRITE_TEXT); + a = (int)msg->len; + lws_dll2_remove(&msg->list); + free(msg); + + if (m < a) { + lwsl_err("ERROR %d writing to raw\n", m); + return -1; + } + + /* + * If more to do... + */ + if (pc->pending_msg_to_raw.count) + lws_callback_on_writable(wsi); + break; + default: + break; + } + + return 0; +} + +static struct lws_protocols protocols[] = { + { "http", lws_callback_http_dummy, 0, 0 }, + { "lws-ws-raw-ws", callback_proxy_ws_server, 0, 1024 }, + { "lws-ws-raw-raw", callback_proxy_raw_client, 0, 1024 }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static const lws_retry_bo_t retry = { + .secs_since_valid_ping = 3, + .secs_since_valid_hangup = 10, +}; + +static int interrupted; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + const char *p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, + * lws must have been configured and built with + * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */; + + 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 ws-raw proxy | visit http://localhost:7681 (-s = use TLS / https)\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.mounts = &mount; + info.protocols = protocols; + info.vhost_name = "localhost"; + info.options = + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + +#if defined(LWS_WITH_TLS) + if (lws_cmdline_option(argc, argv, "-s")) { + lwsl_user("Server using TLS\n"); + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + } +#endif + + if (lws_cmdline_option(argc, argv, "-h")) + info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; + + if (lws_cmdline_option(argc, argv, "-v")) + info.retry_and_idle_policy = &retry; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 0); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/example.js b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/example.js new file mode 100644 index 000000000..d350dee95 --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/example.js @@ -0,0 +1,66 @@ + +function get_appropriate_ws_url(extra_url) +{ + var pcol; + var u = document.URL; + + /* + * We open the websocket encrypted if this page came on an + * https:// url itself, otherwise unencrypted + */ + + if (u.substring(0, 5) === "https") { + pcol = "wss://"; + u = u.substr(8); + } else { + pcol = "ws://"; + if (u.substring(0, 4) === "http") + u = u.substr(7); + } + + u = u.split("/"); + + /* + "/xxx" bit is for IE10 workaround */ + + return pcol + u[0] + "/" + extra_url; +} + +function new_ws(urlpath, protocol) +{ + return new WebSocket(urlpath, protocol); +} + +document.addEventListener("DOMContentLoaded", function() { + + var ws = new_ws(get_appropriate_ws_url(""), "lws-ws-raw-ws"); + try { + ws.onopen = function() { + document.getElementById("m").disabled = 0; + document.getElementById("b").disabled = 0; + }; + + ws.onmessage =function got_packet(msg) { + document.getElementById("r").value = + document.getElementById("r").value + msg.data + "\n"; + document.getElementById("r").scrollTop = + document.getElementById("r").scrollHeight; + }; + + ws.onclose = function(){ + document.getElementById("m").disabled = 1; + document.getElementById("b").disabled = 1; + }; + } catch(exception) { + alert("

Error " + exception); + } + + function sendmsg() + { + ws.send(document.getElementById("m").value); + document.getElementById("m").value = ""; + } + + document.getElementById("b").addEventListener("click", sendmsg); + +}, false); + diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/favicon.ico b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/favicon.ico new file mode 100644 index 000000000..c0cc2e3df Binary files /dev/null and b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/favicon.ico differ diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/index.html b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/index.html new file mode 100644 index 000000000..9c1dc9a49 --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/index.html @@ -0,0 +1,19 @@ + + + + + + + +
+ + LWS chat minimal ws server example.
+ Chat is sent to all browsers open on this page. +
+
+
+ + + + + diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/libwebsockets.org-logo.svg b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/libwebsockets.org-logo.svg new file mode 100644 index 000000000..ef241b37c --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/libwebsockets.org-logo.svg @@ -0,0 +1,66 @@ + + + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/strict-csp.svg b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/strict-csp.svg new file mode 100644 index 000000000..cd128f1d2 --- /dev/null +++ b/minimal-examples/ws-server/minimal-ws-raw-proxy/mount-origin/strict-csp.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +