diff --git a/lib/context.c b/lib/context.c index 6b12cd19..ccc13c00 100644 --- a/lib/context.c +++ b/lib/context.c @@ -897,6 +897,9 @@ lws_cancel_service(struct lws_context *context) struct lws_context_per_thread *pt = &context->pt[0]; short m = context->count_threads; + if (context->being_destroyed1) + return; + lwsl_info("%s\n", __func__); while (m--) { diff --git a/lib/event-libs/poll/poll.c b/lib/event-libs/poll/poll.c index cfbec684..ad6b29b0 100644 --- a/lib/event-libs/poll/poll.c +++ b/lib/event-libs/poll/poll.c @@ -23,16 +23,10 @@ #include -static int -elops_destroy_context1_poll(struct lws_context *context) -{ - return 1; -} - struct lws_event_loop_ops event_loop_ops_poll = { /* name */ "poll", /* init_context */ NULL, - /* destroy_context1 */ elops_destroy_context1_poll, + /* destroy_context1 */ NULL, /* destroy_context2 */ NULL, /* init_vhost_listen_wsi */ NULL, /* init_pt */ NULL, diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index af4ca2f6..81787db1 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -91,15 +91,8 @@ signed char char_to_hex(const char c) void __lws_free_wsi(struct lws *wsi) { -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_context_per_thread *pt; -#endif - if (!wsi) return; -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - pt = &wsi->context->pt[(int)wsi->tsi]; -#endif /* * Protocol user data may be allocated either internally by lws @@ -116,35 +109,16 @@ __lws_free_wsi(struct lws *wsi) if (wsi->vhost && wsi->vhost->lserv_wsi == wsi) wsi->vhost->lserv_wsi = NULL; -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + // lws_peer_dump_from_wsi(wsi); - /* we may not have an ah, but may be on the waiting list... */ - lwsl_info("ah det due to close\n"); - __lws_header_table_detach(wsi, 0); - - { - struct allocated_headers *ah = pt->http.ah_list; - while (ah) { - if (ah->in_use && ah->wsi == wsi) { - lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); - ah->in_use = 0; - ah->wsi = NULL; - pt->http.ah_count_in_use--; - break; - } - ah = ah->next; - } - } -#endif + if (wsi->role_ops->destroy_role) + wsi->role_ops->destroy_role(wsi); #if defined(LWS_WITH_PEER_LIMITS) lws_peer_track_wsi_close(wsi->context, wsi->peer); wsi->peer = NULL; #endif - if (wsi->role_ops->destroy_role) - wsi->role_ops->destroy_role(wsi); - /* since we will destroy the wsi, make absolutely sure now */ #if defined(LWS_WITH_OPENSSL) @@ -847,8 +821,7 @@ just_kill_connection: if (!wsi->protocol) pro = &wsi->vhost->protocols[0]; - //lwsl_notice("%s: est %d told %d cbin %d %s\n", __func__, lwsi_state_est_PRE_CLOSE(wsi), !wsi->told_user_closed, - // wsi->role_ops->close_cb[lwsi_role_server(wsi)], pro->name); + pro->callback(wsi, wsi->role_ops->close_cb[lwsi_role_server(wsi)], wsi->user_space, NULL, 0); diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 3ccb4a72..1fb04d4e 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1527,7 +1527,10 @@ enum lws_callback_reasons { /**< RAW mode file was adopted (equivalent to 'wsi created') */ LWS_CALLBACK_RAW_RX_FILE = 64, - /**< RAW mode file has something to read */ + /**< This is the indication the RAW mode file has something to read. + * This doesn't actually do the read of the file and len is always + * 0... your code should do the read having been informed there is + * something to read now. */ LWS_CALLBACK_RAW_WRITEABLE_FILE = 65, /**< RAW mode file is writeable */ diff --git a/lib/misc/peer-limits.c b/lib/misc/peer-limits.c index 741505ce..373ec54c 100644 --- a/lib/misc/peer-limits.c +++ b/lib/misc/peer-limits.c @@ -199,6 +199,26 @@ lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, lws_context_unlock(context); /* ====================================> */ } +void +lws_peer_dump_from_wsi(struct lws *wsi) +{ + struct lws_peer *peer; + + if (!wsi || !wsi->peer) + return; + + peer = wsi->peer; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d, ah %d/%d\n", __func__, + wsi, (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi, + peer->http.count_ah, peer->http.total_ah); +#else + lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d\n", __func__, + wsi, (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi); +#endif +} + void lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer) { diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index d731c183..9f2859d0 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1715,6 +1715,8 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); void lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, struct lws *wsi); +void +lws_peer_dump_from_wsi(struct lws *wsi); #endif diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 0f14264b..d4aad661 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -469,6 +469,7 @@ try_pollout: fail: +lwsl_notice("%s: fail: closing\n", __func__); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "server socket svc fail"); return LWS_HPI_RET_WSI_ALREADY_DIED; @@ -616,6 +617,32 @@ rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn) return 0; } +static int +rops_destroy_role_h1(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct allocated_headers *ah; + + /* we may not have an ah, but may be on the waiting list... */ + lwsl_info("%s: ah det due to close\n", __func__); + __lws_header_table_detach(wsi, 0); + + ah = pt->http.ah_list; + + while (ah) { + if (ah->in_use && ah->wsi == wsi) { + lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); + ah->in_use = 0; + ah->wsi = NULL; + pt->http.ah_count_in_use--; + break; + } + ah = ah->next; + } + + return 0; +} + struct lws_role_ops role_ops_h1 = { /* role name */ "h1", /* alpn id */ "http/1.1", @@ -636,7 +663,7 @@ struct lws_role_ops role_ops_h1 = { /* close_via_role_protocol */ NULL, /* close_role */ NULL, /* close_kill_connection */ NULL, - /* destroy_role */ NULL, + /* destroy_role */ rops_destroy_role_h1, /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 208342f4..abd7e125 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -503,6 +503,26 @@ rops_tx_credit_h2(struct lws *wsi) static int rops_destroy_role_h2(struct lws *wsi) { + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct allocated_headers *ah; + + /* we may not have an ah, but may be on the waiting list... */ + lwsl_info("%s: ah det due to close\n", __func__); + __lws_header_table_detach(wsi, 0); + + ah = pt->http.ah_list; + + while (ah) { + if (ah->in_use && ah->wsi == wsi) { + lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); + ah->in_use = 0; + ah->wsi = NULL; + pt->http.ah_count_in_use--; + break; + } + ah = ah->next; + } + if (wsi->upgraded_to_http2 || wsi->http2_substream) { lws_hpack_destroy_dynamic_header(wsi); diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 8616b237..001c6c09 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1729,7 +1729,7 @@ lws_http_transaction_completed(struct lws *wsi) return 1; if (wsi->http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { - lwsl_info("%s: %p: close connection\n", __func__, wsi); + lwsl_notice("%s: %p: close connection\n", __func__, wsi); return 1; } @@ -1961,10 +1961,10 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, /* non-SSL */ if (!(type & LWS_ADOPT_HTTP)) { if (!(type & LWS_ADOPT_SOCKET)) - lws_role_transition(new_wsi, 0, LRS_UNCONNECTED, + lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, &role_ops_raw_file); else - lws_role_transition(new_wsi, 0, LRS_UNCONNECTED, + lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt); } #if defined(LWS_ROLE_H1) diff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw/ops-raw.c index f59bdab6..e94af046 100644 --- a/lib/roles/raw/ops-raw.c +++ b/lib/roles/raw/ops-raw.c @@ -54,12 +54,17 @@ rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi, buffered = lws_buflist_aware_read(pt, wsi, &ebuf); switch (ebuf.len) { case 0: - lwsl_info("%s: read 0 len a\n", - __func__); + lwsl_info("%s: read 0 len\n", __func__); wsi->seen_zero_length_recv = 1; lws_change_pollfd(wsi, LWS_POLLIN, 0); - goto try_pollout; - //goto fail; + + /* + * we need to go to fail here, since it's the only + * chance we get to understand that the socket has + * closed + */ + // goto try_pollout; + goto fail; case LWS_SSL_CAPABLE_ERROR: goto fail; @@ -127,7 +132,7 @@ try_pollout: fail: lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; + return LWS_HPI_RET_WSI_ALREADY_DIED; } diff --git a/minimal-examples/raw/README.md b/minimal-examples/raw/README.md index ccc91643..f6ce5ae0 100644 --- a/minimal-examples/raw/README.md +++ b/minimal-examples/raw/README.md @@ -3,5 +3,6 @@ minimal-raw-adopt-tcp|Shows how to have lws adopt an existing tcp socket something else had connected minimal-raw-adopt-udp|Shows how to create a udp socket and read and write on it 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-netcat|Writes stdin to a remote server and prints results on stdout 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-netcat/CMakeLists.txt b/minimal-examples/raw/minimal-raw-netcat/CMakeLists.txt new file mode 100644 index 00000000..ba3997d0 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-netcat/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-raw-netcat) +set(SRCS minimal-raw-netcat.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-netcat/README.md b/minimal-examples/raw/minimal-raw-netcat/README.md new file mode 100644 index 00000000..b50483cc --- /dev/null +++ b/minimal-examples/raw/minimal-raw-netcat/README.md @@ -0,0 +1,38 @@ +# lws minimal raw netcat + +This example shows to to create a "netcat" that copies its stdin to +a remote socket and prints what is returned in stdout. + +It has some advantage over the real netcat, it will wait 1s after stdin closes +to print results that are in flight. + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ echo -e -n "GET / http/1.1\r\n\r\n"| ./lws-minimal-raw-netcat +[2018/05/02 08:53:53:2665] USER: LWS minimal raw netcat [--server ip] [--port port] +[2018/05/02 08:53:53:2667] NOTICE: Creating Vhost 'default' (no listener), 1 protocols, IPv6 off +[2018/05/02 08:53:53:2703] USER: Starting connect... +[2018/05/02 08:53:53:5644] USER: Connected to libwebsockets.org:80... +[2018/05/02 08:53:53:5645] USER: LWS_CALLBACK_RAW_ADOPT +[2018/05/02 08:53:53:5645] USER: LWS_CALLBACK_RAW_ADOPT_FILE +[2018/05/02 08:53:53:5646] USER: LWS_CALLBACK_RAW_RX_FILE +[2018/05/02 08:53:53:5646] USER: LWS_CALLBACK_RAW_CLOSE_FILE +[2018/05/02 08:53:53:8600] USER: LWS_CALLBACK_RAW_RX (186) +HTTP/1.1 301 Redirect +server: lwsws +Strict-Transport-Security: max-age=15768000 ; includeSubDomains +location: https://libwebsockets.org +content-type: text/html +content-length: 0 + +``` + +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-netcat/minimal-raw-netcat.c b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c new file mode 100644 index 00000000..faffaa56 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c @@ -0,0 +1,254 @@ +/* + * lws-minimal-raw-netcat + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates sending stdin to a remote socket and printing + * what is returned to stdout. + * + * All the logging is on stderr, so you can tune it out with 2>log + * or whatever. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + + 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, LWS_USEC_PER_SEC / 10); + else { + interrupted = 1; + lws_cancel_service(context); + } + break; + + case LWS_CALLBACK_RAW_RX_FILE: + lwsl_user("LWS_CALLBACK_RAW_RX_FILE\n"); + waiting = 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_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(*((const char *)in++)); + fflush(stdout); + break; + + case LWS_CALLBACK_RAW_WRITEABLE: + lwsl_user("LWS_CALLBACK_RAW_WRITEABLE\n"); + // lwsl_hexdump_info(buf, waiting); + if (stdin_wsi) + lws_rx_flow_control(stdin_wsi, 1); + if (lws_write(wsi, buf, 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 }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + const char *server = "libwebsockets.org", *port = "80"; + struct lws_context_creation_info info; + lws_sock_file_fd_type sock; + struct addrinfo h, *r, *rp; + struct lws_vhost *vhost; + 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 raw netcat [--server ip] [--port port]\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + info.port = CONTEXT_PORT_NO_LISTEN_SERVER; + 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; + + if ((p = lws_cmdline_option(argc, argv, "--port"))) + port = p; + + if ((p = lws_cmdline_option(argc, argv, "--server"))) + server = p; + + n = getaddrinfo(server, port, &h, &r); + if (n) { + lwsl_err("%s: problem resolving %s: %s\n", __func__, + server, 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 to %s:%s...\n", server, port); + if (connect(sock.sockfd, rp->ai_addr, sizeof(*rp->ai_addr)) < 0) { + lwsl_err("%s: unable to connect\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 */ + + raw_wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_SOCKET, sock, + protocols[0].name, NULL); + if (!raw_wsi) { + lwsl_err("%s: foreign socket adoption failed\n", __func__); + goto bail; + } + + sock.filefd = 0; + stdin_wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_FILE_DESC, + sock, protocols[0].name, NULL); + if (!stdin_wsi) { + lwsl_err("%s: stdin adoption failed\n", __func__); + goto bail; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + +bail: + + lwsl_user("%s: destroying context\n", __func__); + + lws_context_destroy(context); + + return 0; +} diff --git a/scripts/attack.sh b/scripts/attack.sh index 66691fcf..6fe21df6 100755 --- a/scripts/attack.sh +++ b/scripts/attack.sh @@ -2,7 +2,11 @@ # # attack the test server and try to make it fall over # -# Requires the library to have been built with cmake .. -DCMAKE_BUILD_TYPE=DEBUG +# Requires the library to have been built with +# +# cmake .. -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_MINIMAL_EXAMPLES=1 +# +# run it from the build dir echo echo "----------------------------------------------" @@ -19,6 +23,7 @@ INSTALLED=`dirname $A` SHAREDIR=$INSTALLED/../share/libwebsockets-test-server CORPUS=$SHAREDIR/test.html +LWS_NC=./bin/lws-minimal-raw-netcat CPID= LEN=0 @@ -139,7 +144,7 @@ check echo echo "---- /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F" rm -f /tmp/lwscap -echo -n -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap cat /tmp/lwscap check 1 "UPDATE_SETTINGS=1" check 2 "Root_Channels_1_Channel_name_http_post=?" @@ -149,14 +154,14 @@ check echo echo "---- ? processing (/cgi-bin/settings.js?key1=value1)" rm -f /tmp/lwscap -echo -n -e "GET /cgi-bin/settings.js?key1=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET /cgi-bin/settings.js?key1=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check 1 "key1=value1" check echo echo "---- ? processing (/t%3dest?key1%3d2=value1)" rm -f /tmp/lwscap -echo -n -e "GET /t%3dest?key1%3d2=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET /t%3dest?key1%3d2=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check 0 "/t=est" check 1 "key1_2=value1" check @@ -164,46 +169,46 @@ check echo echo "---- ? processing (%2f%2e%2e%2f%2e./xxtest.html?arg=1)" rm -f /tmp/lwscap -echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check 1 "arg=1" check echo echo "---- ? processing (%2f%2e%2e%2f%2e./xxtest.html?arg=/../.)" rm -f /tmp/lwscap -echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check 1 "arg=/../." check echo echo "---- spam enough crap to not be GET" -echo "not GET" | nc $SERVER $PORT +echo "not GET" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null > /tmp/lwscap check echo echo "---- spam more than the name buffer of crap" -dd if=/dev/urandom bs=1 count=80 2>/dev/null | nc -i1 $SERVER $PORT +dd if=/dev/urandom bs=1 count=80 2>/dev/null | $LWS_NC --server $SERVER --port $PORT 2>/dev/null > /tmp/lwscap check echo echo "---- spam 10MB of crap" -dd if=/dev/urandom bs=1 count=655360 | nc -i1 $SERVER $PORT +dd if=/dev/urandom bs=1 count=655360 | $LWS_NC --server $SERVER --port $PORT 2>/dev/null > /tmp/lwscap check echo echo "---- malformed URI" echo "GET nonsense................................................................................................................" \ - | nc -i1 $SERVER $PORT + | $LWS_NC --server $SERVER --port $PORT 2>/dev/null > /tmp/lwscap check echo echo "---- missing URI" -echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1 $SERVER $PORT >/tmp/lwscap +echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null >/tmp/lwscap check echo echo "---- repeated method" -echo -n -e "GET blah HTTP/1.0\x0d\x0aGET blah HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT >/tmp/lwscap +echo -n -e "GET blah HTTP/1.0\x0d\x0aGET blah HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null >/tmp/lwscap check echo @@ -225,7 +230,7 @@ echo -n -e "GET blah HTTP/1.0\x0d\x0a........................................... "......................................................................................................................." \ "......................................................................................................................." \ "......................................................................................................................." \ - | nc -i1 $SERVER $PORT + | $LWS_NC --server $SERVER --port $PORT 2>/dev/null check echo @@ -247,20 +252,20 @@ echo -n -e "GET ................................................................ "......................................................................................................................." \ "......................................................................................................................." \ "......................................................................................................................." \ - | nc -i1 $SERVER $PORT + | $LWS_NC --server $SERVER --port $PORT 2>/dev/null check echo echo "---- good request but http payload coming too (test.html served then forbidden)" echo -n -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \ - | cat - /dev/zero | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap + | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check defaultplusforbidden check echo echo "---- nonexistent file" rm -f /tmp/lwscap -echo -n -e "GET /nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET /nope HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap cat /tmp/lwscap check notfound check @@ -268,63 +273,63 @@ check echo echo "---- relative uri path" rm -f /tmp/lwscap -echo -n -e "GET nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET nope HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check forbidden check echo echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)" rm -f /tmp/lwscap -echo -n -e "GET /../../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET /../../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check notfound check echo echo "---- directory attack 2 (/../ should be /)" rm -f /tmp/lwscap -echo -e -n "GET /../ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET /../ HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check default check echo echo "---- directory attack 3 (/./ should be /)" rm -f /tmp/lwscap -echo -e -n "GET /./ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET /./ HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check default check echo echo "---- directory attack 4 (/blah/.. should be /)" rm -f /tmp/lwscap -echo -e -n "GET /blah/.. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET /blah/.. HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check default check echo echo "---- directory attack 5 (/blah/../ should be /)" rm -f /tmp/lwscap -echo -e -n "GET /blah/../ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET /blah/../ HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check default check echo echo "---- directory attack 6 (/blah/../. should be /)" rm -f /tmp/lwscap -echo -e -n "GET /blah/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET /blah/../. HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check default check echo echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)" rm -f /tmp/lwscap -echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check notfound check echo echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)" rm -f /tmp/lwscap -echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null | sed '1,/^\r$/d'> /tmp/lwscap check notfound check @@ -550,8 +555,9 @@ for i in \ /path/to/dir/../other/dir \ ; do -R=`rm -f /tmp/lwscap ; echo -n -e "GET $i HTTP/1.0\r\n\r\n" | nc localhost 7681 2>/dev/null >/tmp/lwscap; head -n1 /tmp/lwscap| cut -d' ' -f2` +R=`rm -f /tmp/lwscap ; echo -n -e "GET $i HTTP/1.0\r\n\r\n" | $LWS_NC --server $SERVER --port $PORT 2>/dev/null > /tmp/lwscap; head -n1 /tmp/lwscap| cut -d' ' -f2` +#cat $LOG #echo ==== $R