diff --git a/cmake/lws_config_private.h.in b/cmake/lws_config_private.h.in index 0aa6df70..267a3484 100644 --- a/cmake/lws_config_private.h.in +++ b/cmake/lws_config_private.h.in @@ -28,9 +28,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine LWS_HAVE_IN6ADDR_H -/* Define to 1 if you have the `ssl' library (-lssl). */ -//#cmakedefine LWS_HAVE_LIBSSL - /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #cmakedefine LWS_HAVE_MALLOC diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index e7a88651..13744b58 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -3295,7 +3295,9 @@ struct lws_client_connect_info { /**< UNUSED... provide in info.extensions at context creation time */ const char *method; /**< if non-NULL, do this http method instead of ws[s] upgrade. - * use "GET" to be a simple http client connection */ + * use "GET" to be a simple http client connection. "RAW" gets + * you a connected socket that lws itself will leave alone once + * connected. */ struct lws *parent_wsi; /**< if another wsi is responsible for this connection, give it here. * this is used to make sure if the parent closes so do any diff --git a/lib/server/peer-limits.c b/lib/server/peer-limits.c index 7ddb9a43..4e8b3aba 100644 --- a/lib/server/peer-limits.c +++ b/lib/server/peer-limits.c @@ -70,8 +70,10 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd) } #endif rlen = sizeof(addr); - if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) + if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) { + lwsl_notice("%s: getpeername failed\n", __func__); return NULL; + } if (af == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&addr; diff --git a/minimal-examples/raw/README.md b/minimal-examples/raw/README.md index 1c70f56a..18e3fb48 100644 --- a/minimal-examples/raw/README.md +++ b/minimal-examples/raw/README.md @@ -1,5 +1,6 @@ |name|demonstrates| ---|--- +minimal-raw-adopt-tcp|Shows how to have lws adopt an existing tcp socket something else had connected minimal-raw-file|Shows how to adopt a file descriptor (device node, fifo, file, etc) into the lws event loop and handle events minimal-raw-vhost|Shows how to set up a vhost that listens and accepts RAW socket connections diff --git a/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt b/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt new file mode 100644 index 00000000..6ee4fb84 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-adopt-tcp/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-raw-adopt-tcp) +set(SRCS minimal-raw-adopt-tcp.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/raw/minimal-raw-adopt-tcp/README.md b/minimal-examples/raw/minimal-raw-adopt-tcp/README.md new file mode 100644 index 00000000..6f548063 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-adopt-tcp/README.md @@ -0,0 +1,54 @@ +# lws minimal ws server raw adopt tcp + +This example is only meaningful if you are integrating lws in another +app which generates its own connected sockets. In some cases you may +want lws to "adopt" the socket. + +(If you simply want a connected client raw socket using lws alone, you +can just use lws_client_connect_via_info() with info.method = "RAW". +http-client/minimal-http-client shows how to do that, just set +info.method to "RAW".) + +This example demonstrates how to adopt a foreign, connected socket into lws +as a raw wsi, bound to a specific lws protocol. + +The example connects a socket itself to libwebsockets.org:80, and then +has lws adopt it as a raw wsi. The lws protocol writes "GET / HTTP/1.1" +to the socket and hexdumps what was sent back. + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-raw-adopt-tcp +[2018/03/23 09:03:57:1960] USER: LWS minimal raw adopt tcp +[2018/03/23 09:03:57:1961] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off +[2018/03/23 09:03:57:2079] USER: Starting connect... +[2018/03/23 09:03:57:4963] USER: Connected... +[2018/03/23 09:03:57:4963] USER: LWS_CALLBACK_RAW_ADOPT +[2018/03/23 09:03:57:7842] USER: LWS_CALLBACK_RAW_RX (186) +[2018/03/23 09:03:57:7842] NOTICE: +[2018/03/23 09:03:57:7842] NOTICE: 0000: 48 54 54 50 2F 31 2E 31 20 33 30 31 20 52 65 64 HTTP/1.1 301 Red +[2018/03/23 09:03:57:7842] NOTICE: 0010: 69 72 65 63 74 0D 0A 73 65 72 76 65 72 3A 20 6C irect..server: l +[2018/03/23 09:03:57:7842] NOTICE: 0020: 77 73 77 73 0D 0A 53 74 72 69 63 74 2D 54 72 61 wsws..Strict-Tra +[2018/03/23 09:03:57:7843] NOTICE: 0030: 6E 73 70 6F 72 74 2D 53 65 63 75 72 69 74 79 3A nsport-Security: +[2018/03/23 09:03:57:7843] NOTICE: 0040: 20 6D 61 78 2D 61 67 65 3D 31 35 37 36 38 30 30 max-age=1576800 +[2018/03/23 09:03:57:7843] NOTICE: 0050: 30 20 3B 20 69 6E 63 6C 75 64 65 53 75 62 44 6F 0 ; includeSubDo +[2018/03/23 09:03:57:7843] NOTICE: 0060: 6D 61 69 6E 73 0D 0A 6C 6F 63 61 74 69 6F 6E 3A mains..location: +[2018/03/23 09:03:57:7843] NOTICE: 0070: 20 68 74 74 70 73 3A 2F 2F 6C 69 62 77 65 62 73 https://libwebs +[2018/03/23 09:03:57:7843] NOTICE: 0080: 6F 63 6B 65 74 73 2E 6F 72 67 0D 0A 63 6F 6E 74 ockets.org..cont +[2018/03/23 09:03:57:7843] NOTICE: 0090: 65 6E 74 2D 74 79 70 65 3A 20 74 65 78 74 2F 68 ent-type: text/h +[2018/03/23 09:03:57:7843] NOTICE: 00A0: 74 6D 6C 0D 0A 63 6F 6E 74 65 6E 74 2D 6C 65 6E tml..content-len +[2018/03/23 09:03:57:7843] NOTICE: 00B0: 67 74 68 3A 20 30 0D 0A 0D 0A gth: 0.... +[2018/03/23 09:03:57:7843] NOTICE: +[2018/03/23 09:04:03:3627] USER: LWS_CALLBACK_RAW_CLOSE + +``` + +Note the example does everything itself, after 5s idle the remote server closes the connection +after which the example continues until you ^C it. diff --git a/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c b/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c new file mode 100644 index 00000000..86b82caf --- /dev/null +++ b/minimal-examples/raw/minimal-raw-adopt-tcp/minimal-raw-adopt-tcp.c @@ -0,0 +1,181 @@ +/* + * lws-minimal-raw-adopt-tcp + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates integrating somebody else's connected tcp + * socket into the lws event loop as a RAW wsi. It's interesting in + * the kind of situation where you already have a connected socket + * in your application, and you need to hand it over to lws to deal with. + * + * Lws supports "adopting" these foreign sockets. + * + * If you simply want a connected client raw socket using lws alone, you + * can just use lws_client_connect_via_info() with info.method = "RAW". + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + + switch (reason) { + + /* 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_CLOSE: + lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); + break; + + case LWS_CALLBACK_RAW_RX: + lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); + lwsl_hexdump_level(LLL_NOTICE, in, len); + break; + + case LWS_CALLBACK_RAW_WRITEABLE: + if (lws_write(wsi, + (uint8_t *)"GET / HTTP/1.1\xd\xa\xd\xa", 18, + LWS_WRITE_RAW) != 18) { + lwsl_notice("%s: raw write failed\n", __func__); + return 1; + } + break; + + default: + break; + } + + return 0; +} + +static struct lws_protocols protocols[] = { + { "raw-test", callback_raw_test, 0, 0 }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static int interrupted; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + lws_sock_file_fd_type sock; + struct addrinfo h, *r, *rp; + struct lws_vhost *vhost; + int n = 0; + + lws_set_log_level(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 */, NULL); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + lwsl_user("LWS minimal raw adopt tcp\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + info.port = 7681; + info.protocols = protocols; + + vhost = lws_create_vhost(context, &info); + if (!vhost) { + lwsl_err("lws vhost creation failed\n"); + goto bail; + } + + /* + * Connect our own "foreign" socket to libwebsockets.org:80 + * + * Normally you would do this with lws_client_connect_via_info() inside + * the lws event loop, hiding all this detail. But this example + * demonstrates how to integrate an externally-connected "foreign" + * socket, so we create one by hand. + */ + + memset(&h, 0, sizeof(h)); + h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + h.ai_socktype = SOCK_STREAM; + h.ai_protocol = IPPROTO_TCP; + + n = getaddrinfo("libwebsockets.org", "80", &h, &r); + if (n) { + lwsl_err("%s: problem resolving libwebsockets.org: %s\n", __func__, gai_strerror(n)); + return 1; + } + + for (rp = r; rp; rp = rp->ai_next) { + sock.sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock.sockfd >= 0) + break; + } + if (!rp) { + lwsl_err("%s: unable to create INET socket\n", __func__); + freeaddrinfo(r); + + return 1; + } + + lwsl_user("Starting connect...\n"); + if (connect(sock.sockfd, rp->ai_addr, sizeof(*rp->ai_addr)) < 0) { + lwsl_err("%s: unable to connect to libwebsockets.org:80\n", __func__); + freeaddrinfo(r); + return 1; + } + + freeaddrinfo(r); + signal(SIGINT, sigint_handler); + lwsl_user("Connected...\n"); + + /* our foreign socket is connected... adopt it into lws */ + + if (!lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_SOCKET, sock, + protocols[0].name, NULL)) { + lwsl_err("%s: foreign socket adoption failed\n", __func__); + goto bail; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + +bail: + lws_context_destroy(context); + + return 0; +}