diff options
Diffstat (limited to 'thirdparty/libwebsockets/lib/roles/ws')
-rw-r--r-- | thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c | 607 | ||||
-rw-r--r-- | thirdparty/libwebsockets/lib/roles/ws/client-ws.c | 634 | ||||
-rw-r--r-- | thirdparty/libwebsockets/lib/roles/ws/ops-ws.c | 2021 | ||||
-rw-r--r-- | thirdparty/libwebsockets/lib/roles/ws/private.h | 164 | ||||
-rw-r--r-- | thirdparty/libwebsockets/lib/roles/ws/server-ws.c | 874 |
5 files changed, 0 insertions, 4300 deletions
diff --git a/thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c b/thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c deleted file mode 100644 index f5aaa6dbb5..0000000000 --- a/thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c +++ /dev/null @@ -1,607 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "core/private.h" - -/* - * parsers.c: lws_ws_rx_sm() needs to be roughly kept in - * sync with changes here, esp related to ext draining - */ - -int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c) -{ - int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; - int handled, m; - unsigned short close_code; - struct lws_tokens ebuf; - unsigned char *pp; -#if !defined(LWS_WITHOUT_EXTENSIONS) - int rx_draining_ext = 0, n; -#endif - - ebuf.token = NULL; - ebuf.len = 0; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - assert(!c); - - lws_remove_wsi_from_draining_ext_list(wsi); - rx_draining_ext = 1; - lwsl_debug("%s: doing draining flow\n", __func__); - - goto drain_extension; - } -#endif - - if (wsi->socket_is_permanently_unusable) - return -1; - - switch (wsi->lws_rx_parse_state) { - case LWS_RXPS_NEW: - /* control frames (PING) may interrupt checkable sequences */ - wsi->ws->defeat_check_utf8 = 0; - - switch (wsi->ws->ietf_spec_revision) { - case 13: - wsi->ws->opcode = c & 0xf; - /* revisit if an extension wants them... */ - switch (wsi->ws->opcode) { - case LWSWSOPC_TEXT_FRAME: - wsi->ws->rsv_first_msg = (c & 0x70); - wsi->ws->continuation_possible = 1; - wsi->ws->check_utf8 = lws_check_opt( - wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8); - wsi->ws->utf8 = 0; - wsi->ws->first_fragment = 1; - break; - case LWSWSOPC_BINARY_FRAME: - wsi->ws->rsv_first_msg = (c & 0x70); - wsi->ws->check_utf8 = 0; - wsi->ws->continuation_possible = 1; - wsi->ws->first_fragment = 1; - break; - case LWSWSOPC_CONTINUATION: - if (!wsi->ws->continuation_possible) { - lwsl_info("disordered continuation\n"); - return -1; - } - wsi->ws->first_fragment = 0; - break; - case LWSWSOPC_CLOSE: - wsi->ws->check_utf8 = 0; - wsi->ws->utf8 = 0; - break; - case 3: - case 4: - case 5: - case 6: - case 7: - case 0xb: - case 0xc: - case 0xd: - case 0xe: - case 0xf: - lwsl_info("illegal opcode\n"); - return -1; - default: - wsi->ws->defeat_check_utf8 = 1; - break; - } - wsi->ws->rsv = (c & 0x70); - /* revisit if an extension wants them... */ - if ( -#if !defined(LWS_WITHOUT_EXTENSIONS) - !wsi->ws->count_act_ext && -#endif - wsi->ws->rsv) { - lwsl_info("illegal rsv bits set\n"); - return -1; - } - wsi->ws->final = !!((c >> 7) & 1); - lwsl_ext("%s: This RX frame Final %d\n", __func__, - wsi->ws->final); - - if (wsi->ws->owed_a_fin && - (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME || - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) { - lwsl_info("hey you owed us a FIN\n"); - return -1; - } - if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) { - wsi->ws->continuation_possible = 0; - wsi->ws->owed_a_fin = 0; - } - - if ((wsi->ws->opcode & 8) && !wsi->ws->final) { - lwsl_info("control msg can't be fragmented\n"); - return -1; - } - if (!wsi->ws->final) - wsi->ws->owed_a_fin = 1; - - switch (wsi->ws->opcode) { - case LWSWSOPC_TEXT_FRAME: - case LWSWSOPC_BINARY_FRAME: - wsi->ws->frame_is_binary = wsi->ws->opcode == - LWSWSOPC_BINARY_FRAME; - break; - } - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; - break; - - default: - lwsl_err("unknown spec version %02d\n", - wsi->ws->ietf_spec_revision); - break; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN: - - wsi->ws->this_frame_masked = !!(c & 0x80); - - switch (c & 0x7f) { - case 126: - /* control frames are not allowed to have big lengths */ - if (wsi->ws->opcode & 8) - goto illegal_ctl_length; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; - break; - case 127: - /* control frames are not allowed to have big lengths */ - if (wsi->ws->opcode & 8) - goto illegal_ctl_length; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; - break; - default: - wsi->ws->rx_packet_length = c & 0x7f; - if (wsi->ws->this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - if (wsi->ws->rx_packet_length) { - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - } else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - } - break; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN16_2: - wsi->ws->rx_packet_length = c << 8; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN16_1: - wsi->ws->rx_packet_length |= c; - if (wsi->ws->this_frame_masked) - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - if (wsi->ws->rx_packet_length) - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_8: - if (c & 0x80) { - lwsl_warn("b63 of length must be zero\n"); - /* kill the connection */ - return -1; - } -#if defined __LP64__ - wsi->ws->rx_packet_length = ((size_t)c) << 56; -#else - wsi->ws->rx_packet_length = 0; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_7: -#if defined __LP64__ - wsi->ws->rx_packet_length |= ((size_t)c) << 48; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_6: -#if defined __LP64__ - wsi->ws->rx_packet_length |= ((size_t)c) << 40; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_5: -#if defined __LP64__ - wsi->ws->rx_packet_length |= ((size_t)c) << 32; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_4: - wsi->ws->rx_packet_length |= ((size_t)c) << 24; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_3: - wsi->ws->rx_packet_length |= ((size_t)c) << 16; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_2: - wsi->ws->rx_packet_length |= ((size_t)c) << 8; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_1: - wsi->ws->rx_packet_length |= (size_t)c; - if (wsi->ws->this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - if (wsi->ws->rx_packet_length) - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - } - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_1: - wsi->ws->mask[0] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_2: - wsi->ws->mask[1] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_3: - wsi->ws->mask[2] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_4: - wsi->ws->mask[3] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - - if (wsi->ws->rx_packet_length) - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - break; - - case LWS_RXPS_WS_FRAME_PAYLOAD: - - assert(wsi->ws->rx_ubuf); -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) - goto drain_extension; -#endif - if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce) - c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3]; - - wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c; - - if (--wsi->ws->rx_packet_length == 0) { - /* spill because we have the whole frame */ - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - - /* - * if there's no protocol max frame size given, we are - * supposed to default to context->pt_serv_buf_size - */ - if (!wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size) - break; - - if (wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size) - break; - - /* spill because we filled our rx buffer */ -spill: - - handled = 0; - - /* - * is this frame a control packet we should take care of at this - * layer? If so service it and hide it from the user callback - */ - - switch (wsi->ws->opcode) { - case LWSWSOPC_CLOSE: - pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE]; - if (lws_check_opt(wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8) && - wsi->ws->rx_ubuf_head > 2 && - lws_check_utf8(&wsi->ws->utf8, pp + 2, - wsi->ws->rx_ubuf_head - 2)) - goto utf8_fail; - - /* is this an acknowledgment of our close? */ - if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) { - /* - * fine he has told us he is closing too, let's - * finish our close - */ - lwsl_parser("seen server's close ack\n"); - return -1; - } - - lwsl_parser("client sees server close len = %d\n", - wsi->ws->rx_ubuf_head); - if (wsi->ws->rx_ubuf_head >= 2) { - close_code = (pp[0] << 8) | pp[1]; - if (close_code < 1000 || - close_code == 1004 || - close_code == 1005 || - close_code == 1006 || - close_code == 1012 || - close_code == 1013 || - close_code == 1014 || - close_code == 1015 || - (close_code >= 1016 && close_code < 3000) - ) { - pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff; - pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff; - } - } - if (user_callback_handle_rxflow( - wsi->protocol->callback, wsi, - LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, - wsi->user_space, pp, - wsi->ws->rx_ubuf_head)) - return -1; - - memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp, - wsi->ws->rx_ubuf_head); - wsi->ws->close_in_ping_buffer_len = - wsi->ws->rx_ubuf_head; - - lwsl_info("%s: scheduling return close as ack\n", - __func__); - __lws_change_pollfd(wsi, LWS_POLLIN, 0); - lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3); - wsi->waiting_to_send_close_frame = 1; - wsi->close_needs_ack = 0; - lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE); - lws_callback_on_writable(wsi); - handled = 1; - break; - - case LWSWSOPC_PING: - lwsl_info("received %d byte ping, sending pong\n", - wsi->ws->rx_ubuf_head); - - /* he set a close reason on this guy, ignore PING */ - if (wsi->ws->close_in_ping_buffer_len) - goto ping_drop; - - if (wsi->ws->ping_pending_flag) { - /* - * there is already a pending ping payload - * we should just log and drop - */ - lwsl_parser("DROP PING since one pending\n"); - goto ping_drop; - } - - /* control packets can only be < 128 bytes long */ - if (wsi->ws->rx_ubuf_head > 128 - 3) { - lwsl_parser("DROP PING payload too large\n"); - goto ping_drop; - } - - /* stash the pong payload */ - memcpy(wsi->ws->ping_payload_buf + LWS_PRE, - &wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head; - wsi->ws->ping_pending_flag = 1; - - /* get it sent as soon as possible */ - lws_callback_on_writable(wsi); -ping_drop: - wsi->ws->rx_ubuf_head = 0; - handled = 1; - break; - - case LWSWSOPC_PONG: - lwsl_info("client received pong\n"); - lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - if (wsi->pending_timeout == - PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { - lwsl_info("%p: received expected PONG\n", wsi); - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - } - - /* issue it */ - callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG; - break; - - case LWSWSOPC_CONTINUATION: - case LWSWSOPC_TEXT_FRAME: - case LWSWSOPC_BINARY_FRAME: - break; - - default: - /* not handled or failed */ - lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode); - wsi->ws->rx_ubuf_head = 0; - - return -1; - } - - /* - * No it's real payload, pass it up to the user callback. - * It's nicely buffered with the pre-padding taken care of - * so it can be sent straight out again using lws_write - */ - if (handled) - goto already_done; - - ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE]; - ebuf.len = wsi->ws->rx_ubuf_head; - -#if !defined(LWS_WITHOUT_EXTENSIONS) -drain_extension: - lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len); - - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0); - lwsl_ext("Ext RX returned %d\n", n); - if (n < 0) { - wsi->socket_is_permanently_unusable = 1; - return -1; - } -#endif - lwsl_debug("post inflate ebuf len %d\n", ebuf.len); - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (rx_draining_ext && !ebuf.len) { - lwsl_debug(" --- ending drain on 0 read result\n"); - goto already_done; - } -#endif - - if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { - if (lws_check_utf8(&wsi->ws->utf8, - (unsigned char *)ebuf.token, - ebuf.len)) { - lws_close_reason(wsi, - LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"bad utf8", 8); - goto utf8_fail; - } - - /* we are ending partway through utf-8 character? */ - if (!wsi->ws->rx_packet_length && wsi->ws->final && - wsi->ws->utf8 -#if !defined(LWS_WITHOUT_EXTENSIONS) - && !n -#endif - ) { - lwsl_info("FINAL utf8 error\n"); - lws_close_reason(wsi, - LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"partial utf8", 12); -utf8_fail: - lwsl_info("utf8 error\n"); - lwsl_hexdump_info(ebuf.token, ebuf.len); - - return -1; - } - } - - if (ebuf.len < 0 && - callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) - goto already_done; - - if (!ebuf.token) - goto already_done; - - ebuf.token[ebuf.len] = '\0'; - - if (!wsi->protocol->callback) - goto already_done; - - if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) - lwsl_info("Client doing pong callback\n"); - - if ( - /* coverity says dead code otherwise */ -#if !defined(LWS_WITHOUT_EXTENSIONS) - n && -#endif - ebuf.len) - /* extension had more... main loop will come back - * we want callback to be done with this set, if so, - * because lws_is_final() hides it was final until the - * last chunk - */ - lws_add_wsi_to_draining_ext_list(wsi); - else - lws_remove_wsi_from_draining_ext_list(wsi); - - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE || - lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE || - lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) - goto already_done; - - m = wsi->protocol->callback(wsi, - (enum lws_callback_reasons)callback_action, - wsi->user_space, ebuf.token, ebuf.len); - - wsi->ws->first_fragment = 0; - - // lwsl_notice("%s: bulk ws rx: input used %d, output %d\n", - // __func__, wsi->ws->rx_ubuf_head, ebuf.len); - - /* if user code wants to close, let caller know */ - if (m) - return 1; - -already_done: - wsi->ws->rx_ubuf_head = 0; - break; - default: - lwsl_err("client rx illegal state\n"); - return 1; - } - - return 0; - -illegal_ctl_length: - lwsl_warn("Control frame asking for extended length is illegal\n"); - - /* kill the connection */ - return -1; -} - - diff --git a/thirdparty/libwebsockets/lib/roles/ws/client-ws.c b/thirdparty/libwebsockets/lib/roles/ws/client-ws.c deleted file mode 100644 index d88833f381..0000000000 --- a/thirdparty/libwebsockets/lib/roles/ws/client-ws.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include <core/private.h> - -/* - * In-place str to lower case - */ - -static void -strtolower(char *s) -{ - while (*s) { -#ifdef LWS_PLAT_OPTEE - int tolower_optee(int c); - *s = tolower_optee((int)*s); -#else - *s = tolower((int)*s); -#endif - s++; - } -} - -int -lws_create_client_ws_object(const struct lws_client_connect_info *i, - struct lws *wsi) -{ - int v = SPEC_LATEST_SUPPORTED; - - /* allocate the ws struct for the wsi */ - wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct"); - if (!wsi->ws) { - lwsl_notice("OOM\n"); - return 1; - } - - /* -1 means just use latest supported */ - if (i->ietf_version_or_minus_one != -1 && - i->ietf_version_or_minus_one) - v = i->ietf_version_or_minus_one; - - wsi->ws->ietf_spec_revision = v; - - return 0; -} - -#if !defined(LWS_NO_CLIENT) -int -lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len) -{ - if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) && - (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) && - (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) && - !lwsi_role_client(wsi)) - return 0; - - // lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len); - - while (len) { - /* - * we were accepting input but now we stopped doing so - */ - if (lws_is_flowcontrolled(wsi)) { - //lwsl_notice("%s: caching %ld\n", __func__, (long)len); - lws_rxflow_cache(wsi, *buf, 0, (int)len); - *buf += len; - return 0; - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - int m; - - //lwsl_notice("%s: draining ext\n", __func__); - if (lwsi_role_client(wsi)) - m = lws_ws_client_rx_sm(wsi, 0); - else - m = lws_ws_rx_sm(wsi, 0, 0); - if (m < 0) - return -1; - continue; - } -#endif - /* caller will account for buflist usage */ - - if (lws_ws_client_rx_sm(wsi, *(*buf)++)) { - lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n", - __func__, (int)len); - return -1; - } - len--; - } - // lwsl_notice("%s: finished with %ld\n", __func__, (long)len); - - return 0; -} -#endif - -char * -lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1) -{ - char buf[128], hash[20], key_b64[40]; - int n; -#if !defined(LWS_WITHOUT_EXTENSIONS) - const struct lws_extension *ext; - int ext_count = 0; -#endif - - /* - * create the random key - */ - n = lws_get_random(wsi->context, hash, 16); - if (n != 16) { - lwsl_err("Unable to read from random dev %s\n", - SYSTEM_RANDOM_FILEPATH); - return NULL; - } - - lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); - - p += sprintf(p, "Upgrade: websocket\x0d\x0a" - "Connection: %sUpgrade\x0d\x0a" - "Sec-WebSocket-Key: ", conn1); - strcpy(p, key_b64); - p += strlen(key_b64); - p += sprintf(p, "\x0d\x0a"); - if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) - p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); - - /* tell the server what extensions we could support */ - -#if !defined(LWS_WITHOUT_EXTENSIONS) - ext = wsi->vhost->ws.extensions; - while (ext && ext->callback) { - - n = wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, - wsi->user_space, (char *)ext->name, 0); - - /* - * zero return from callback means go ahead and allow - * the extension, it's what we get if the callback is - * unhandled - */ - - if (n) { - ext++; - continue; - } - - /* apply it */ - - if (ext_count) - *p++ = ','; - else - p += sprintf(p, "Sec-WebSocket-Extensions: "); - p += sprintf(p, "%s", ext->client_offer); - ext_count++; - - ext++; - } - if (ext_count) - p += sprintf(p, "\x0d\x0a"); -#endif - - if (wsi->ws->ietf_spec_revision) - p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", - wsi->ws->ietf_spec_revision); - - /* prepare the expected server accept response */ - - key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ - n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", - key_b64); - - lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); - - lws_b64_encode_string(hash, 20, - wsi->http.ah->initial_handshake_hash_base64, - sizeof(wsi->http.ah->initial_handshake_hash_base64)); - - return p; -} - -int -lws_client_ws_upgrade(struct lws *wsi, const char **cce) -{ - struct lws_context *context = wsi->context; - struct lws_tokenize ts; - int n, len, okay = 0; - lws_tokenize_elem e; - char *p, buf[64]; - const char *pc; -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char *sb = (char *)&pt->serv_buf[0]; - const struct lws_ext_options *opts; - const struct lws_extension *ext; - char ext_name[128]; - const char *c, *a; - int more = 1; - char ignore; -#endif - - if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */ - lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n", - __func__); - *cce = "HS: h2 / ws upgrade unsupported"; - goto bail3; - } - - if (wsi->http.ah->http_response == 401) { - lwsl_warn( - "lws_client_handshake: got bad HTTP response '%d'\n", - wsi->http.ah->http_response); - *cce = "HS: ws upgrade unauthorized"; - goto bail3; - } - - if (wsi->http.ah->http_response != 101) { - lwsl_warn( - "lws_client_handshake: got bad HTTP response '%d'\n", - wsi->http.ah->http_response); - *cce = "HS: ws upgrade response not 101"; - goto bail3; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { - lwsl_info("no ACCEPT\n"); - *cce = "HS: ACCEPT missing"; - goto bail3; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); - if (!p) { - lwsl_info("no UPGRADE\n"); - *cce = "HS: UPGRADE missing"; - goto bail3; - } - strtolower(p); - if (strcmp(p, "websocket")) { - lwsl_warn( - "lws_client_handshake: got bad Upgrade header '%s'\n", p); - *cce = "HS: Upgrade to something other than websocket"; - goto bail3; - } - - /* connection: must have "upgrade" */ - - lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST | - LWS_TOKENIZE_F_MINUS_NONTERM); - ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION); - if (ts.len <= 0) /* won't fit, or absent */ - goto bad_conn_format; - - do { - e = lws_tokenize(&ts); - switch (e) { - case LWS_TOKZE_TOKEN: - if (!strcasecmp(ts.token, "upgrade")) - e = LWS_TOKZE_ENDED; - break; - - case LWS_TOKZE_DELIMITER: - break; - - default: /* includes ENDED */ -bad_conn_format: - *cce = "HS: UPGRADE malformed"; - goto bail3; - } - } while (e > 0); - - pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); - if (!pc) { - lwsl_parser("lws_client_int_s_hs: no protocol list\n"); - } else - lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc); - - /* - * confirm the protocol the server wants to talk was in the list - * of protocols we offered - */ - - len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); - if (!len) { - lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__); - /* - * no protocol name to work from, - * default to first protocol - */ - n = 0; - wsi->protocol = &wsi->vhost->protocols[0]; - goto check_extensions; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); - len = (int)strlen(p); - - while (pc && *pc && !okay) { - if (!strncmp(pc, p, len) && - (pc[len] == ',' || pc[len] == '\0')) { - okay = 1; - continue; - } - while (*pc && *pc++ != ',') - ; - while (*pc == ' ') - pc++; - } - - if (!okay) { - lwsl_info("%s: got bad protocol %s\n", __func__, p); - *cce = "HS: PROTOCOL malformed"; - goto bail2; - } - - /* - * identify the selected protocol struct and set it - */ - n = 0; - /* keep client connection pre-bound protocol */ - if (!lwsi_role_client(wsi)) - wsi->protocol = NULL; - - while (wsi->vhost->protocols[n].callback) { - if (!wsi->protocol && - strcmp(p, wsi->vhost->protocols[n].name) == 0) { - wsi->protocol = &wsi->vhost->protocols[n]; - break; - } - n++; - } - - if (!wsi->vhost->protocols[n].callback) { /* no match */ - /* if server, that's already fatal */ - if (!lwsi_role_client(wsi)) { - lwsl_info("%s: fail protocol %s\n", __func__, p); - *cce = "HS: Cannot match protocol"; - goto bail2; - } - - /* for client, find the index of our pre-bound protocol */ - - n = 0; - while (wsi->vhost->protocols[n].callback) { - if (wsi->protocol && strcmp(wsi->protocol->name, - wsi->vhost->protocols[n].name) == 0) { - wsi->protocol = &wsi->vhost->protocols[n]; - break; - } - n++; - } - - if (!wsi->vhost->protocols[n].callback) { - if (wsi->protocol) - lwsl_err("Failed to match protocol %s\n", - wsi->protocol->name); - else - lwsl_err("No protocol on client\n"); - goto bail2; - } - } - - lwsl_debug("Selected protocol %s\n", wsi->protocol->name); - -check_extensions: - /* - * stitch protocol choice into the vh protocol linked list - * We always insert ourselves at the start of the list - * - * X <-> B - * X <-> pAn <-> pB - */ - - lws_same_vh_protocol_insert(wsi, n); - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* instantiate the accepted extensions */ - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { - lwsl_ext("no client extensions allowed by server\n"); - goto check_accept; - } - - /* - * break down the list of server accepted extensions - * and go through matching them or identifying bogons - */ - - if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, - WSI_TOKEN_EXTENSIONS) < 0) { - lwsl_warn("ext list from server failed to copy\n"); - *cce = "HS: EXT: list too big"; - goto bail2; - } - - c = sb; - n = 0; - ignore = 0; - a = NULL; - while (more) { - - if (*c && (*c != ',' && *c != '\t')) { - if (*c == ';') { - ignore = 1; - if (!a) - a = c + 1; - } - if (ignore || *c == ' ') { - c++; - continue; - } - - ext_name[n] = *c++; - if (n < (int)sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - ignore = 0; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - /* check we actually support it */ - - lwsl_notice("checking client ext %s\n", ext_name); - - n = 0; - ext = wsi->vhost->ws.extensions; - while (ext && ext->callback) { - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - n = 1; - lwsl_notice("instantiating client ext %s\n", ext_name); - - /* instantiate the extension on this conn */ - - wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext; - - /* allow him to construct his ext instance */ - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_CLIENT_CONSTRUCT, - (void *)&wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - (void *)&opts, 0)) { - lwsl_info(" ext %s failed construction\n", - ext_name); - ext++; - continue; - } - - /* - * allow the user code to override ext defaults if it - * wants to - */ - ext_name[0] = '\0'; - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, - (char *)ext->name, ext_name, - sizeof(ext_name))) { - *cce = "HS: EXT: failed setting defaults"; - goto bail2; - } - - if (ext_name[0] && - lws_ext_parse_options(ext, wsi, - wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - opts, ext_name, - (int)strlen(ext_name))) { - lwsl_err("%s: unable to parse user defaults '%s'", - __func__, ext_name); - *cce = "HS: EXT: failed parsing defaults"; - goto bail2; - } - - /* - * give the extension the server options - */ - if (a && lws_ext_parse_options(ext, wsi, - wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - opts, a, lws_ptr_diff(c, a))) { - lwsl_err("%s: unable to parse remote def '%s'", - __func__, a); - *cce = "HS: EXT: failed parsing options"; - goto bail2; - } - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_OPTION_CONFIRM, - wsi->ws->act_ext_user[wsi->ws->count_act_ext], - NULL, 0)) { - lwsl_err("%s: ext %s rejects server options %s", - __func__, ext->name, a); - *cce = "HS: EXT: Rejects server options"; - goto bail2; - } - - wsi->ws->count_act_ext++; - - ext++; - } - - if (n == 0) { - lwsl_warn("Unknown ext '%s'!\n", ext_name); - *cce = "HS: EXT: unknown ext"; - goto bail2; - } - - a = NULL; - n = 0; - } - -check_accept: -#endif - - /* - * Confirm his accept token is the one we precomputed - */ - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); - if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) { - lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p, - wsi->http.ah->initial_handshake_hash_base64); - *cce = "HS: Accept hash wrong"; - goto bail2; - } - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) { - lwsl_err("Problem allocating wsi user mem\n"); - *cce = "HS: OOM"; - goto bail2; - } - - /* - * we seem to be good to go, give client last chance to check - * headers and OK it - */ - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, - wsi->user_space, NULL, 0)) { - *cce = "HS: Rejected by filter cb"; - goto bail2; - } - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - /* free up his parsing allocations */ - lws_header_table_detach(wsi, 0); - - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, - &role_ops_ws); - lws_restart_ws_ping_pong_timer(wsi); - - wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* - * create the frame buffer for this connection according to the - * size mentioned in the protocol definition. If 0 there, then - * use a big default for compatibility - */ - n = (int)wsi->protocol->rx_buffer_size; - if (!n) - n = context->pt_serv_buf_size; - n += LWS_PRE; - wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, - "client frame buffer"); - if (!wsi->ws->rx_ubuf) { - lwsl_err("Out of Mem allocating rx buffer %d\n", n); - *cce = "HS: OOM"; - goto bail2; - } - wsi->ws->rx_ubuf_alloc = n; - lwsl_info("Allocating client RX buffer %d\n", n); - -#if !defined(LWS_WITH_ESP32) - if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, - (const char *)&n, sizeof n)) { - lwsl_warn("Failed to set SNDBUF to %d", n); - *cce = "HS: SO_SNDBUF failed"; - goto bail3; - } -#endif - - lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name); - - /* call him back to inform him he is up */ - - if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, - wsi->user_space, NULL, 0)) { - *cce = "HS: Rejected at CLIENT_ESTABLISHED"; - goto bail3; - } - - return 0; - -bail3: - return 3; - -bail2: - return 2; -} diff --git a/thirdparty/libwebsockets/lib/roles/ws/ops-ws.c b/thirdparty/libwebsockets/lib/roles/ws/ops-ws.c deleted file mode 100644 index 1cbc6ac6a6..0000000000 --- a/thirdparty/libwebsockets/lib/roles/ws/ops-ws.c +++ /dev/null @@ -1,2021 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include <core/private.h> - -#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } - -/* - * client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in - * sync with changes here, esp related to ext draining - */ - -int -lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c) -{ - int callback_action = LWS_CALLBACK_RECEIVE; - int ret = 0; - unsigned short close_code; - struct lws_tokens ebuf; - unsigned char *pp; - int n = 0; -#if !defined(LWS_WITHOUT_EXTENSIONS) - int rx_draining_ext = 0; - int lin; -#endif - - ebuf.token = NULL; - ebuf.len = 0; - if (wsi->socket_is_permanently_unusable) - return -1; - - switch (wsi->lws_rx_parse_state) { - case LWS_RXPS_NEW: -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - ebuf.token = NULL; - ebuf.len = 0; - lws_remove_wsi_from_draining_ext_list(wsi); - rx_draining_ext = 1; - lwsl_debug("%s: doing draining flow\n", __func__); - - goto drain_extension; - } -#endif - switch (wsi->ws->ietf_spec_revision) { - case 13: - /* - * no prepended frame key any more - */ - wsi->ws->all_zero_nonce = 1; - goto handle_first; - - default: - lwsl_warn("lws_ws_rx_sm: unknown spec version %d\n", - wsi->ws->ietf_spec_revision); - break; - } - break; - case LWS_RXPS_04_mask_1: - wsi->ws->mask[1] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2; - break; - case LWS_RXPS_04_mask_2: - wsi->ws->mask[2] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3; - break; - case LWS_RXPS_04_mask_3: - wsi->ws->mask[3] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - - /* - * start from the zero'th byte in the XOR key buffer since - * this is the start of a frame with a new key - */ - - wsi->ws->mask_idx = 0; - - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1; - break; - - /* - * 04 logical framing from the spec (all this is masked when incoming - * and has to be unmasked) - * - * We ignore the possibility of extension data because we don't - * negotiate any extensions at the moment. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-------+-+-------------+-------------------------------+ - * |F|R|R|R| opcode|R| Payload len | Extended payload length | - * |I|S|S|S| (4) |S| (7) | (16/63) | - * |N|V|V|V| |V| | (if payload len==126/127) | - * | |1|2|3| |4| | | - * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - * | Extended payload length continued, if payload len == 127 | - * + - - - - - - - - - - - - - - - +-------------------------------+ - * | | Extension data | - * +-------------------------------+ - - - - - - - - - - - - - - - + - * : : - * +---------------------------------------------------------------+ - * : Application data : - * +---------------------------------------------------------------+ - * - * We pass payload through to userland as soon as we get it, ignoring - * FIN. It's up to userland to buffer it up if it wants to see a - * whole unfragmented block of the original size (which may be up to - * 2^63 long!) - */ - - case LWS_RXPS_04_FRAME_HDR_1: -handle_first: - - wsi->ws->opcode = c & 0xf; - wsi->ws->rsv = c & 0x70; - wsi->ws->final = !!((c >> 7) & 1); - wsi->ws->defeat_check_utf8 = 0; - - if (((wsi->ws->opcode) & 8) && !wsi->ws->final) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"frag ctl", 8); - return -1; - } - - switch (wsi->ws->opcode) { - case LWSWSOPC_TEXT_FRAME: - wsi->ws->check_utf8 = lws_check_opt( - wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8); - /* fallthru */ - case LWSWSOPC_BINARY_FRAME: - if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME) - wsi->ws->check_utf8 = 0; - if (wsi->ws->continuation_possible) { - lws_close_reason(wsi, - LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad cont", 8); - return -1; - } - wsi->ws->rsv_first_msg = (c & 0x70); - wsi->ws->frame_is_binary = - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME; - wsi->ws->first_fragment = 1; - wsi->ws->continuation_possible = !wsi->ws->final; - break; - case LWSWSOPC_CONTINUATION: - if (!wsi->ws->continuation_possible) { - lws_close_reason(wsi, - LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad cont", 8); - return -1; - } - break; - case LWSWSOPC_CLOSE: - wsi->ws->check_utf8 = 0; - wsi->ws->utf8 = 0; - break; - case 3: - case 4: - case 5: - case 6: - case 7: - case 0xb: - case 0xc: - case 0xd: - case 0xe: - case 0xf: - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad opc", 7); - lwsl_info("illegal opcode\n"); - return -1; - } - - if (wsi->ws->owed_a_fin && - (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME || - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) { - lwsl_info("hey you owed us a FIN\n"); - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"bad fin", 7); - return -1; - } - if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) { - wsi->ws->continuation_possible = 0; - wsi->ws->owed_a_fin = 0; - } - - if (!wsi->ws->final) - wsi->ws->owed_a_fin = 1; - - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; - if (wsi->ws->rsv && - ( -#if !defined(LWS_WITHOUT_EXTENSIONS) - !wsi->ws->count_act_ext || -#endif - (wsi->ws->rsv & ~0x40))) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, - (uint8_t *)"rsv bits", 8); - return -1; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN: - - wsi->ws->this_frame_masked = !!(c & 0x80); - - switch (c & 0x7f) { - case 126: - /* control frames are not allowed to have big lengths */ - if (wsi->ws->opcode & 8) - goto illegal_ctl_length; - - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; - break; - case 127: - /* control frames are not allowed to have big lengths */ - if (wsi->ws->opcode & 8) - goto illegal_ctl_length; - - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; - break; - default: - wsi->ws->rx_packet_length = c & 0x7f; - - - if (wsi->ws->this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else - if (wsi->ws->rx_packet_length) { - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - } else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - break; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN16_2: - wsi->ws->rx_packet_length = c << 8; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN16_1: - wsi->ws->rx_packet_length |= c; - if (wsi->ws->this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - wsi->lws_rx_parse_state = - LWS_RXPS_WS_FRAME_PAYLOAD; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_8: - if (c & 0x80) { - lwsl_warn("b63 of length must be zero\n"); - /* kill the connection */ - return -1; - } -#if defined __LP64__ - wsi->ws->rx_packet_length = ((size_t)c) << 56; -#else - wsi->ws->rx_packet_length = 0; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_7: -#if defined __LP64__ - wsi->ws->rx_packet_length |= ((size_t)c) << 48; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_6: -#if defined __LP64__ - wsi->ws->rx_packet_length |= ((size_t)c) << 40; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_5: -#if defined __LP64__ - wsi->ws->rx_packet_length |= ((size_t)c) << 32; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_4: - wsi->ws->rx_packet_length |= ((size_t)c) << 24; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_3: - wsi->ws->rx_packet_length |= ((size_t)c) << 16; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_2: - wsi->ws->rx_packet_length |= ((size_t)c) << 8; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_1: - wsi->ws->rx_packet_length |= ((size_t)c); - if (wsi->ws->this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else - wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_1: - wsi->ws->mask[0] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_2: - wsi->ws->mask[1] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_3: - wsi->ws->mask[2] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_4: - wsi->ws->mask[3] = c; - if (c) - wsi->ws->all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD; - wsi->ws->mask_idx = 0; - if (wsi->ws->rx_packet_length == 0) { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - break; - - - case LWS_RXPS_WS_FRAME_PAYLOAD: - assert(wsi->ws->rx_ubuf); - - if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) { - lwsl_err("Attempted overflow \n"); - return -1; - } - if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) { - if (wsi->ws->all_zero_nonce) - wsi->ws->rx_ubuf[LWS_PRE + - (wsi->ws->rx_ubuf_head++)] = c; - else - wsi->ws->rx_ubuf[LWS_PRE + - (wsi->ws->rx_ubuf_head++)] = - c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3]; - - --wsi->ws->rx_packet_length; - } - - if (!wsi->ws->rx_packet_length) { - lwsl_debug("%s: ws fragment length exhausted\n", - __func__); - /* spill because we have the whole frame */ - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__); - goto drain_extension; - } -#endif - /* - * if there's no protocol max frame size given, we are - * supposed to default to context->pt_serv_buf_size - */ - if (!wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size) - break; - - if (wsi->protocol->rx_buffer_size && - wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size) - break; - - /* spill because we filled our rx buffer */ -spill: - /* - * is this frame a control packet we should take care of at this - * layer? If so service it and hide it from the user callback - */ - - lwsl_parser("spill on %s\n", wsi->protocol->name); - - switch (wsi->ws->opcode) { - case LWSWSOPC_CLOSE: - - if (wsi->ws->peer_has_sent_close) - break; - - wsi->ws->peer_has_sent_close = 1; - - pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE]; - if (lws_check_opt(wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8) && - wsi->ws->rx_ubuf_head > 2 && - lws_check_utf8(&wsi->ws->utf8, pp + 2, - wsi->ws->rx_ubuf_head - 2)) - goto utf8_fail; - - /* is this an acknowledgment of our close? */ - if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) { - /* - * fine he has told us he is closing too, let's - * finish our close - */ - lwsl_parser("seen client close ack\n"); - return -1; - } - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE) - /* if he sends us 2 CLOSE, kill him */ - return -1; - - if (lws_partial_buffered(wsi)) { - /* - * if we're in the middle of something, - * we can't do a normal close response and - * have to just close our end. - */ - wsi->socket_is_permanently_unusable = 1; - lwsl_parser("Closing on peer close " - "due to pending tx\n"); - return -1; - } - - if (wsi->ws->rx_ubuf_head >= 2) { - close_code = (pp[0] << 8) | pp[1]; - if (close_code < 1000 || - close_code == 1004 || - close_code == 1005 || - close_code == 1006 || - close_code == 1012 || - close_code == 1013 || - close_code == 1014 || - close_code == 1015 || - (close_code >= 1016 && close_code < 3000) - ) { - pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff; - pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff; - } - } - - if (user_callback_handle_rxflow( - wsi->protocol->callback, wsi, - LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, - wsi->user_space, - &wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head)) - return -1; - - lwsl_parser("server sees client close packet\n"); - lwsi_set_state(wsi, LRS_RETURNED_CLOSE); - /* deal with the close packet contents as a PONG */ - wsi->ws->payload_is_close = 1; - goto process_as_ping; - - case LWSWSOPC_PING: - lwsl_info("received %d byte ping, sending pong\n", - wsi->ws->rx_ubuf_head); - - if (wsi->ws->ping_pending_flag) { - /* - * there is already a pending ping payload - * we should just log and drop - */ - lwsl_parser("DROP PING since one pending\n"); - goto ping_drop; - } -process_as_ping: - /* control packets can only be < 128 bytes long */ - if (wsi->ws->rx_ubuf_head > 128 - 3) { - lwsl_parser("DROP PING payload too large\n"); - goto ping_drop; - } - - /* stash the pong payload */ - memcpy(wsi->ws->ping_payload_buf + LWS_PRE, - &wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head; - wsi->ws->ping_pending_flag = 1; - - /* get it sent as soon as possible */ - lws_callback_on_writable(wsi); -ping_drop: - wsi->ws->rx_ubuf_head = 0; - return 0; - - case LWSWSOPC_PONG: - lwsl_info("received pong\n"); - lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE], - wsi->ws->rx_ubuf_head); - - if (wsi->pending_timeout == - PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { - lwsl_info("received expected PONG on wsi %p\n", - wsi); - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - } - - /* issue it */ - callback_action = LWS_CALLBACK_RECEIVE_PONG; - break; - - case LWSWSOPC_TEXT_FRAME: - case LWSWSOPC_BINARY_FRAME: - case LWSWSOPC_CONTINUATION: - break; - - default: - lwsl_parser("unknown opc %x\n", wsi->ws->opcode); - - return -1; - } - - /* - * No it's real payload, pass it up to the user callback. - * It's nicely buffered with the pre-padding taken care of - * so it can be sent straight out again using lws_write - */ - - ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE]; - ebuf.len = wsi->ws->rx_ubuf_head; - - if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len) - goto already_done; -#if !defined(LWS_WITHOUT_EXTENSIONS) -drain_extension: -#endif - // lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len); - - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE || - lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) - goto already_done; -#if !defined(LWS_WITHOUT_EXTENSIONS) - lin = ebuf.len; - //if (lin) - // lwsl_hexdump_notice(ebuf.token, ebuf.len); - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0); - lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__, - n, ebuf.len); - if (wsi->ws->rx_draining_ext) - already_processed &= ~ALREADY_PROCESSED_NO_CB; -#endif - /* - * ebuf may be pointing somewhere completely different now, - * it's the output - */ -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (n < 0) { - /* - * we may rely on this to get RX, just drop connection - */ - wsi->socket_is_permanently_unusable = 1; - return -1; - } -#endif - if ( -#if !defined(LWS_WITHOUT_EXTENSIONS) - rx_draining_ext && -#endif - ebuf.len == 0) - goto already_done; - - if ( -#if !defined(LWS_WITHOUT_EXTENSIONS) - n && -#endif - ebuf.len) - /* extension had more... main loop will come back */ - lws_add_wsi_to_draining_ext_list(wsi); - else - lws_remove_wsi_from_draining_ext_list(wsi); - - if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { - if (lws_check_utf8(&wsi->ws->utf8, - (unsigned char *)ebuf.token, - ebuf.len)) { - lws_close_reason(wsi, - LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"bad utf8", 8); - goto utf8_fail; - } - - /* we are ending partway through utf-8 character? */ - if (!wsi->ws->rx_packet_length && wsi->ws->final && - wsi->ws->utf8 && !n) { - lwsl_info("FINAL utf8 error\n"); - lws_close_reason(wsi, - LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"partial utf8", 12); -utf8_fail: - lwsl_notice("utf8 error\n"); - lwsl_hexdump_notice(ebuf.token, ebuf.len); - - return -1; - } - } - - if (!wsi->wsistate_pre_close && (ebuf.len >= 0 || - callback_action == LWS_CALLBACK_RECEIVE_PONG)) { - if (ebuf.len) - ebuf.token[ebuf.len] = '\0'; - - if (wsi->protocol->callback && - !(already_processed & ALREADY_PROCESSED_NO_CB)) { - if (callback_action == LWS_CALLBACK_RECEIVE_PONG) - lwsl_info("Doing pong callback\n"); - - ret = user_callback_handle_rxflow( - wsi->protocol->callback, - wsi, (enum lws_callback_reasons) - callback_action, - wsi->user_space, - ebuf.token, - ebuf.len); - } - wsi->ws->first_fragment = 0; - } - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (!lin) - break; -#endif - -already_done: - wsi->ws->rx_ubuf_head = 0; - break; - } - - return ret; - -illegal_ctl_length: - - lwsl_warn("Control frame with xtended length is illegal\n"); - /* kill the connection */ - return -1; -} - - -LWS_VISIBLE size_t -lws_remaining_packet_payload(struct lws *wsi) -{ - return wsi->ws->rx_packet_length; -} - -LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi) -{ - return wsi->ws->frame_is_binary; -} - -void -lws_add_wsi_to_draining_ext_list(struct lws *wsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (wsi->ws->rx_draining_ext) - return; - - lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__); - - wsi->ws->rx_draining_ext = 1; - wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list; - pt->ws.rx_draining_ext_list = wsi; -#endif -} - -void -lws_remove_wsi_from_draining_ext_list(struct lws *wsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws **w = &pt->ws.rx_draining_ext_list; - - if (!wsi->ws->rx_draining_ext) - return; - - lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__); - - wsi->ws->rx_draining_ext = 0; - - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - /* if us, point it instead to who we were pointing to */ - *w = wsi->ws->rx_draining_ext_list; - break; - } - w = &((*w)->ws->rx_draining_ext_list); - } - wsi->ws->rx_draining_ext_list = NULL; -#endif -} - -LWS_EXTERN void -lws_restart_ws_ping_pong_timer(struct lws *wsi) -{ - if (!wsi->context->ws_ping_pong_interval || - !lwsi_role_ws(wsi)) - return; - - wsi->ws->time_next_ping_check = (time_t)lws_now_secs(); -} - -static int -lws_0405_frame_mask_generate(struct lws *wsi) -{ - int n; - /* fetch the per-frame nonce */ - - n = lws_get_random(lws_get_context(wsi), wsi->ws->mask, 4); - if (n != 4) { - lwsl_parser("Unable to read from random device %s %d\n", - SYSTEM_RANDOM_FILEPATH, n); - return 1; - } - - /* start masking from first byte of masking key buffer */ - wsi->ws->mask_idx = 0; - - return 0; -} - -int -lws_server_init_wsi_for_ws(struct lws *wsi) -{ - int n; - - lwsi_set_state(wsi, LRS_ESTABLISHED); - lws_restart_ws_ping_pong_timer(wsi); - - /* - * create the frame buffer for this connection according to the - * size mentioned in the protocol definition. If 0 there, use - * a big default for compatibility - */ - - n = (int)wsi->protocol->rx_buffer_size; - if (!n) - n = wsi->context->pt_serv_buf_size; - n += LWS_PRE; - wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf"); - if (!wsi->ws->rx_ubuf) { - lwsl_err("Out of Mem allocating rx buffer %d\n", n); - return 1; - } - wsi->ws->rx_ubuf_alloc = n; - lwsl_debug("Allocating RX buffer %d\n", n); - -#if !defined(LWS_WITH_ESP32) - if (!wsi->h2_stream_carries_ws) - if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, - (const char *)&n, sizeof n)) { - lwsl_warn("Failed to set SNDBUF to %d", n); - return 1; - } -#endif - - /* notify user code that we're ready to roll */ - - if (wsi->protocol->callback) - if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, - wsi->user_space, -#ifdef LWS_WITH_TLS - wsi->tls.ssl, -#else - NULL, -#endif - wsi->h2_stream_carries_ws)) - return 1; - - lwsl_debug("ws established\n"); - - return 0; -} - - - -LWS_VISIBLE int -lws_is_final_fragment(struct lws *wsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__, - wsi->ws->final, (long)wsi->ws->rx_packet_length, - (long)wsi->ws->rx_draining_ext); - return wsi->ws->final && !wsi->ws->rx_packet_length && - !wsi->ws->rx_draining_ext; -#else - return wsi->ws->final && !wsi->ws->rx_packet_length; -#endif -} - -LWS_VISIBLE int -lws_is_first_fragment(struct lws *wsi) -{ - return wsi->ws->first_fragment; -} - -LWS_VISIBLE unsigned char -lws_get_reserved_bits(struct lws *wsi) -{ - return wsi->ws->rsv; -} - -LWS_VISIBLE LWS_EXTERN int -lws_get_close_length(struct lws *wsi) -{ - return wsi->ws->close_in_ping_buffer_len; -} - -LWS_VISIBLE LWS_EXTERN unsigned char * -lws_get_close_payload(struct lws *wsi) -{ - return &wsi->ws->ping_payload_buf[LWS_PRE]; -} - -LWS_VISIBLE LWS_EXTERN void -lws_close_reason(struct lws *wsi, enum lws_close_status status, - unsigned char *buf, size_t len) -{ - unsigned char *p, *start; - int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE; - - assert(lwsi_role_ws(wsi)); - - start = p = &wsi->ws->ping_payload_buf[LWS_PRE]; - - *p++ = (((int)status) >> 8) & 0xff; - *p++ = ((int)status) & 0xff; - - if (buf) - while (len-- && p < start + budget) - *p++ = *buf++; - - wsi->ws->close_in_ping_buffer_len = lws_ptr_diff(p, start); -} - -static int -lws_is_ws_with_ext(struct lws *wsi) -{ -#if defined(LWS_WITHOUT_EXTENSIONS) - return 0; -#else - return lwsi_role_ws(wsi) && !!wsi->ws->count_act_ext; -#endif -} - -static int -rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - unsigned int pending = 0; - struct lws_tokens ebuf; - char buffered = 0; - int n = 0, m; -#if defined(LWS_WITH_HTTP2) - struct lws *wsi1; -#endif - - if (!wsi->ws) { - lwsl_err("ws role wsi with no ws\n"); - return 1; - } - - // lwsl_notice("%s: %s\n", __func__, wsi->protocol->name); - - //lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__, - // wsi->wsistate, pollfd->revents & LWS_POLLOUT); - - /* - * something went wrong with parsing the handshake, and - * we ended up back in the event loop without completing it - */ - if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) { - wsi->socket_is_permanently_unusable = 1; - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - ebuf.token = NULL; - ebuf.len = 0; - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT) { -#if !defined(LWS_NO_CLIENT) - if ((pollfd->revents & LWS_POLLOUT) && - lws_handle_POLLOUT_event(wsi, pollfd)) { - lwsl_debug("POLLOUT event closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - n = lws_client_socket_service(wsi, pollfd, NULL); - if (n) - return LWS_HPI_RET_WSI_ALREADY_DIED; -#endif - return LWS_HPI_RET_HANDLED; - } - - //lwsl_notice("%s: wsi->ws->tx_draining_ext %d revents 0x%x 0x%x %d\n", - //__func__, wsi->ws->tx_draining_ext, pollfd->revents, wsi->wsistate, - //lwsi_state_can_handle_POLLOUT(wsi)); - - /* 1: something requested a callback when it was OK to write */ - - if ((pollfd->revents & LWS_POLLOUT) && - lwsi_state_can_handle_POLLOUT(wsi) && - lws_handle_POLLOUT_event(wsi, pollfd)) { - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE) - lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE); - - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE || - lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) { - /* - * we stopped caring about anything except control - * packets. Force flow control off, defeat tx - * draining. - */ - lws_rx_flow_control(wsi, 1); -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws) - wsi->ws->tx_draining_ext = 0; -#endif - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->tx_draining_ext) - /* - * We cannot deal with new RX until the TX ext path has - * been drained. It's because new rx will, eg, crap on - * the wsi rx buf that may be needed to retain state. - * - * TX ext drain path MUST go through event loop to avoid - * blocking. - */ - return LWS_HPI_RET_HANDLED; -#endif - if (lws_is_flowcontrolled(wsi)) { - /* We cannot deal with any kind of new RX because we are - * RX-flowcontrolled. - */ - lwsl_info("flowcontrolled\n"); - return LWS_HPI_RET_HANDLED; - } - -#if defined(LWS_WITH_HTTP2) - if (wsi->http2_substream || wsi->upgraded_to_http2) { - wsi1 = lws_get_network_wsi(wsi); - if (wsi1 && lws_has_buffered_out(wsi1)) - /* We cannot deal with any kind of new RX - * because we are dealing with a partial send - * (new RX may trigger new http_action() that - * expect to be able to send) - */ - return LWS_HPI_RET_HANDLED; - } -#endif - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* 2: RX Extension needs to be drained - */ - - if (wsi->ws->rx_draining_ext) { - - lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__); -#ifndef LWS_NO_CLIENT - if (lwsi_role_client(wsi)) { - n = lws_ws_client_rx_sm(wsi, 0); - if (n < 0) - /* we closed wsi */ - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } else -#endif - n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0); - - return LWS_HPI_RET_HANDLED; - } - - if (wsi->ws->rx_draining_ext) - /* - * We have RX EXT content to drain, but can't do it - * right now. That means we cannot do anything lower - * priority either. - */ - return LWS_HPI_RET_HANDLED; -#endif - - /* 3: buflist needs to be drained - */ -read: - //lws_buflist_describe(&wsi->buflist, wsi); - ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist, - (uint8_t **)&ebuf.token); - if (ebuf.len) { - lwsl_info("draining buflist (len %d)\n", ebuf.len); - buffered = 1; - goto drain; - } - - if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->http.ah) - return LWS_HPI_RET_HANDLED; - - if (lws_is_flowcontrolled(wsi)) { - lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n", - __func__, wsi, wsi->rxflow_bitmap); - return LWS_HPI_RET_HANDLED; - } - - if (!(lwsi_role_client(wsi) && - (lwsi_state(wsi) != LRS_ESTABLISHED && - lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK && - lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) { - /* - * In case we are going to react to this rx by scheduling - * writes, we need to restrict the amount of rx to the size - * the protocol reported for rx buffer. - * - * Otherwise we get a situation we have to absorb possibly a - * lot of reads before we get a chance to drain them by writing - * them, eg, with echo type tests in autobahn. - */ - - buffered = 0; - ebuf.token = (char *)pt->serv_buf; - if (lwsi_role_ws(wsi)) - ebuf.len = wsi->ws->rx_ubuf_alloc; - else - ebuf.len = wsi->context->pt_serv_buf_size; - - if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size) - ebuf.len = wsi->context->pt_serv_buf_size; - - if ((int)pending > ebuf.len) - pending = ebuf.len; - - ebuf.len = lws_ssl_capable_read(wsi, (uint8_t *)ebuf.token, - pending ? (int)pending : - ebuf.len); - switch (ebuf.len) { - case 0: - lwsl_info("%s: zero length read\n", - __func__); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - case LWS_SSL_CAPABLE_MORE_SERVICE: - lwsl_info("SSL Capable more service\n"); - return LWS_HPI_RET_HANDLED; - case LWS_SSL_CAPABLE_ERROR: - lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n", - __func__); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - // lwsl_notice("Actual RX %d\n", ebuf.len); - - lws_restart_ws_ping_pong_timer(wsi); - - /* - * coverity thinks ssl_capable_read() may read over - * 2GB. Dissuade it... - */ - ebuf.len &= 0x7fffffff; - } - -drain: - - /* - * give any active extensions a chance to munge the buffer - * before parse. We pass in a pointer to an lws_tokens struct - * prepared with the default buffer and content length that's in - * there. Rather than rewrite the default buffer, extensions - * that expect to grow the buffer can adapt .token to - * point to their own per-connection buffer in the extension - * user allocation. By default with no extensions or no - * extension callback handling, just the normal input buffer is - * used then so it is efficient. - */ - m = 0; - do { - - /* service incoming data */ - //lws_buflist_describe(&wsi->buflist, wsi); - if (ebuf.len) { -#if defined(LWS_ROLE_H2) - if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY) - n = lws_read_h2(wsi, (unsigned char *)ebuf.token, - ebuf.len); - else -#endif - n = lws_read_h1(wsi, (unsigned char *)ebuf.token, - ebuf.len); - - if (n < 0) { - /* we closed wsi */ - n = 0; - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - //lws_buflist_describe(&wsi->buflist, wsi); - //lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len); - if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered)) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } - - ebuf.token = NULL; - ebuf.len = 0; - } while (m); - - if (wsi->http.ah -#if !defined(LWS_NO_CLIENT) - && !wsi->client_h2_alpn -#endif - ) { - lwsl_info("%s: %p: detaching ah\n", __func__, wsi); - lws_header_table_detach(wsi, 0); - } - - pending = lws_ssl_pending(wsi); - if (pending) { - if (lws_is_ws_with_ext(wsi)) - pending = pending > wsi->ws->rx_ubuf_alloc ? - wsi->ws->rx_ubuf_alloc : pending; - else - pending = pending > wsi->context->pt_serv_buf_size ? - wsi->context->pt_serv_buf_size : pending; - goto read; - } - - if (buffered && /* were draining, now nothing left */ - !lws_buflist_next_segment_len(&wsi->buflist, NULL)) { - lwsl_info("%s: %p flow buf: drained\n", __func__, wsi); - /* having drained the rxflow buffer, can rearm POLLIN */ -#ifdef LWS_NO_SERVER - n = -#endif - __lws_rx_flow_control(wsi); - /* n ignored, needed for NO_SERVER case */ - } - - /* n = 0 */ - return LWS_HPI_RET_HANDLED; -} - - -int rops_handle_POLLOUT_ws(struct lws *wsi) -{ - int write_type = LWS_WRITE_PONG; -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_tokens ebuf; - int ret, m; -#endif - int n; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__, - wsi->protocol->name, wsi->ws->tx_draining_ext); -#endif - - /* Priority 3: pending control packets (pong or close) - * - * 3a: close notification packet requested from close api - */ - - if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) { - lwsl_debug("sending close packet\n"); - lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE], - wsi->ws->close_in_ping_buffer_len); - wsi->waiting_to_send_close_frame = 0; - n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - wsi->ws->close_in_ping_buffer_len, - LWS_WRITE_CLOSE); - if (n >= 0) { - if (wsi->close_needs_ack) { - lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK); - lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, - 5); - lwsl_debug("sent close, await ack\n"); - - return LWS_HP_RET_BAIL_OK; - } - wsi->close_needs_ack = 0; - lwsi_set_state(wsi, LRS_RETURNED_CLOSE); - } - - return LWS_HP_RET_BAIL_DIE; - } - - /* else, the send failed and we should just hang up */ - - if ((lwsi_role_ws(wsi) && wsi->ws->ping_pending_flag) || - (lwsi_state(wsi) == LRS_RETURNED_CLOSE && - wsi->ws->payload_is_close)) { - - if (wsi->ws->payload_is_close) - write_type = LWS_WRITE_CLOSE; - else { - if (wsi->wsistate_pre_close) { - /* we started close flow, forget pong */ - wsi->ws->ping_pending_flag = 0; - return LWS_HP_RET_BAIL_OK; - } - lwsl_info("issuing pong %d on wsi %p\n", - wsi->ws->ping_payload_len, wsi); - } - - n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - wsi->ws->ping_payload_len, write_type); - if (n < 0) - return LWS_HP_RET_BAIL_DIE; - - /* well he is sent, mark him done */ - wsi->ws->ping_pending_flag = 0; - if (wsi->ws->payload_is_close) { - // assert(0); - /* oh... a close frame was it... then we are done */ - return LWS_HP_RET_BAIL_DIE; - } - - /* otherwise for PING, leave POLLOUT active either way */ - return LWS_HP_RET_BAIL_OK; - } - - if (!wsi->socket_is_permanently_unusable && wsi->ws->send_check_ping) { - - lwsl_info("issuing ping on wsi %p\n", wsi); - wsi->ws->send_check_ping = 0; - n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - 0, LWS_WRITE_PING); - if (n < 0) - return LWS_HP_RET_BAIL_DIE; - - /* - * we apparently were able to send the PING in a reasonable time - * now reset the clock on our peer to be able to send the - * PONG in a reasonable time. - */ - - lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG, - wsi->context->timeout_secs); - - return LWS_HP_RET_BAIL_OK; - } - - /* Priority 4: if we are closing, not allowed to send more data frags - * which means user callback or tx ext flush banned now - */ - if (lwsi_state(wsi) == LRS_RETURNED_CLOSE) - return LWS_HP_RET_USER_SERVICE; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* Priority 5: Tx path extension with more to send - * - * These are handled as new fragments each time around - * So while we must block new writeable callback to enforce - * payload ordering, but since they are always complete - * fragments control packets can interleave OK. - */ - if (wsi->ws->tx_draining_ext) { - lwsl_ext("SERVICING TX EXT DRAINING\n"); - if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0) - return LWS_HP_RET_BAIL_DIE; - /* leave POLLOUT active */ - return LWS_HP_RET_BAIL_OK; - } - - /* Priority 6: extensions - */ - if (!wsi->ws->extension_data_pending && !wsi->ws->tx_draining_ext) { - lwsl_ext("%s: !wsi->ws->extension_data_pending\n", __func__); - return LWS_HP_RET_USER_SERVICE; - } - - /* - * check in on the active extensions, see if they - * had pending stuff to spill... they need to get the - * first look-in otherwise sequence will be disordered - * - * NULL, zero-length ebuf means just spill pending - */ - - ret = 1; - if (wsi->role_ops == &role_ops_raw_skt || - wsi->role_ops == &role_ops_raw_file) - ret = 0; - - while (ret == 1) { - - /* default to nobody has more to spill */ - - ret = 0; - ebuf.token = NULL; - ebuf.len = 0; - - /* give every extension a chance to spill */ - - m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND, - &ebuf, 0); - if (m < 0) { - lwsl_err("ext reports fatal error\n"); - return LWS_HP_RET_BAIL_DIE; - } - if (m) - /* - * at least one extension told us he has more - * to spill, so we will go around again after - */ - ret = 1; - - /* assuming they gave us something to send, send it */ - - if (ebuf.len) { - n = lws_issue_raw(wsi, (unsigned char *)ebuf.token, - ebuf.len); - if (n < 0) { - lwsl_info("closing from POLLOUT spill\n"); - return LWS_HP_RET_BAIL_DIE; - } - /* - * Keep amount spilled small to minimize chance of this - */ - if (n != ebuf.len) { - lwsl_err("Unable to spill ext %d vs %d\n", - ebuf.len, n); - return LWS_HP_RET_BAIL_DIE; - } - } else - continue; - - /* no extension has more to spill */ - - if (!ret) - continue; - - /* - * There's more to spill from an extension, but we just sent - * something... did that leave the pipe choked? - */ - - if (!lws_send_pipe_choked(wsi)) - /* no we could add more */ - continue; - - lwsl_info("choked in POLLOUT service\n"); - - /* - * Yes, he's choked. Leave the POLLOUT masked on so we will - * come back here when he is unchoked. Don't call the user - * callback to enforce ordering of spilling, he'll get called - * when we come back here and there's nothing more to spill. - */ - - return LWS_HP_RET_BAIL_OK; - } - - wsi->ws->extension_data_pending = 0; -#endif - - return LWS_HP_RET_USER_SERVICE; -} - -static int -rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now) -{ - struct lws_vhost *vh; - - if (!context->ws_ping_pong_interval || - context->last_ws_ping_pong_check_s >= now + 10) - return 0; - - vh = context->vhost_list; - context->last_ws_ping_pong_check_s = now; - - while (vh) { - int n; - - lws_vhost_lock(vh); - - for (n = 0; n < vh->count_protocols; n++) { - - lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, - vh->same_vh_protocol_heads[n].next) { - struct lws *wsi = lws_container_of(d, - struct lws, same_vh_protocol); - - if (lwsi_role_ws(wsi) && - !wsi->socket_is_permanently_unusable && - !wsi->ws->send_check_ping && - wsi->ws->time_next_ping_check && - lws_compare_time_t(context, now, - wsi->ws->time_next_ping_check) > - context->ws_ping_pong_interval) { - - lwsl_info("req pp on wsi %p\n", wsi); - wsi->ws->send_check_ping = 1; - lws_set_timeout(wsi, - PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING, - context->timeout_secs); - lws_callback_on_writable(wsi); - wsi->ws->time_next_ping_check = now; - } - - } lws_end_foreach_dll_safe(d, d1); - } - - lws_vhost_unlock(vh); - vh = vh->vhost_next; - } - - return 0; -} - -static int -rops_service_flag_pending_ws(struct lws_context *context, int tsi) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws *wsi; - int forced = 0; - - /* POLLIN faking (the pt lock is taken by the parent) */ - - /* - * 1) For all guys with already-available ext data to drain, if they are - * not flowcontrolled, fake their POLLIN status - */ - wsi = pt->ws.rx_draining_ext_list; - while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) { - pt->fds[wsi->position_in_fds_table].revents |= - pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; - if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) - forced = 1; - - wsi = wsi->ws->rx_draining_ext_list; - } - - return forced; -#else - return 0; -#endif -} - -static int -rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason) -{ - if (!wsi->ws) - return 0; - - if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */ - (reason == LWS_CLOSE_STATUS_NOSTATUS || - reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)) - return 0; - - lwsl_debug("%s: sending close indication...\n", __func__); - - /* if no prepared close reason, use 1000 and no aux data */ - - if (!wsi->ws->close_in_ping_buffer_len) { - wsi->ws->close_in_ping_buffer_len = 2; - wsi->ws->ping_payload_buf[LWS_PRE] = (reason >> 8) & 0xff; - wsi->ws->ping_payload_buf[LWS_PRE + 1] = reason & 0xff; - } - - wsi->waiting_to_send_close_frame = 1; - wsi->close_needs_ack = 1; - lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE); - __lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5); - - lws_callback_on_writable(wsi); - - return 1; -} - -static int -rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi) -{ - if (!wsi->ws) - return 0; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - - if (wsi->ws->rx_draining_ext) { - struct lws **w = &pt->ws.rx_draining_ext_list; - - wsi->ws->rx_draining_ext = 0; - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - *w = wsi->ws->rx_draining_ext_list; - break; - } - w = &((*w)->ws->rx_draining_ext_list); - } - wsi->ws->rx_draining_ext_list = NULL; - } - - if (wsi->ws->tx_draining_ext) { - struct lws **w = &pt->ws.tx_draining_ext_list; - lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__); - wsi->ws->tx_draining_ext = 0; - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - *w = wsi->ws->tx_draining_ext_list; - break; - } - w = &((*w)->ws->tx_draining_ext_list); - } - wsi->ws->tx_draining_ext_list = NULL; - } -#endif - lws_free_set_NULL(wsi->ws->rx_ubuf); - - wsi->ws->ping_payload_len = 0; - wsi->ws->ping_pending_flag = 0; - - /* deallocate any active extension contexts */ - - if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0) - lwsl_warn("extension destruction failed\n"); - - return 0; -} - -static int -rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol *wp) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - enum lws_write_protocol wpt; -#endif - int masked7 = lwsi_role_client(wsi); - unsigned char is_masked_bit = 0; - unsigned char *dropmask = NULL; - struct lws_tokens ebuf; - size_t orig_len = len; - int pre = 0, n = 0; - - // lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len); -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->tx_draining_ext) { - /* remove us from the list */ - struct lws **w = &pt->ws.tx_draining_ext_list; - - lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__); - wsi->ws->tx_draining_ext = 0; - /* remove us from context draining ext list */ - while (*w) { - if (*w == wsi) { - *w = wsi->ws->tx_draining_ext_list; - break; - } - w = &((*w)->ws->tx_draining_ext_list); - } - wsi->ws->tx_draining_ext_list = NULL; - - wpt = *wp; - *wp = (wsi->ws->tx_draining_stashed_wp & 0xc0)| - LWS_WRITE_CONTINUATION; - - /* - * When we are just flushing (len == 0), we can trust the - * stashed wp info completely. Otherwise adjust it to the - * FIN status of the incoming packet. - */ - - if (!(wpt & LWS_WRITE_NO_FIN) && len) - *wp &= ~LWS_WRITE_NO_FIN; - - lwsl_ext("FORCED draining wp to 0x%02X " - "(stashed 0x%02X, incoming 0x%02X)\n", *wp, - wsi->ws->tx_draining_stashed_wp, wpt); - // assert(0); - } -#endif - lws_restart_ws_ping_pong_timer(wsi); - - if (((*wp) & 0x1f) == LWS_WRITE_HTTP || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS) - goto send_raw; - - - - /* if we are continuing a frame that already had its header done */ - - if (wsi->ws->inside_frame) { - lwsl_debug("INSIDE FRAME\n"); - goto do_more_inside_frame; - } - - wsi->ws->clean_buffer = 1; - - /* - * give a chance to the extensions to modify payload - * the extension may decide to produce unlimited payload erratically - * (eg, compression extension), so we require only that if he produces - * something, it will be a complete fragment of the length known at - * the time (just the fragment length known), and if he has - * more we will come back next time he is writeable and allow him to - * produce more fragments until he's drained. - * - * This allows what is sent each time it is writeable to be limited to - * a size that can be sent without partial sends or blocking, allows - * interleaving of control frames and other connection service. - */ - ebuf.token = (char *)buf; - ebuf.len = (int)len; - - switch ((int)*wp) { - case LWS_WRITE_PING: - case LWS_WRITE_PONG: - case LWS_WRITE_CLOSE: - break; - default: -#if !defined(LWS_WITHOUT_EXTENSIONS) - // lwsl_notice("LWS_EXT_CB_PAYLOAD_TX\n"); - // m = (int)ebuf.len; - /* returns 0 if no more tx pending, 1 if more pending */ - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &ebuf, *wp); - if (n < 0) - return -1; - // lwsl_notice("ext processed %d plaintext into %d compressed" - // " (wp 0x%x)\n", m, (int)ebuf.len, *wp); - - if (n && ebuf.len) { - lwsl_ext("write drain len %d (wp 0x%x) SETTING " - "tx_draining_ext\n", (int)ebuf.len, *wp); - /* extension requires further draining */ - wsi->ws->tx_draining_ext = 1; - wsi->ws->tx_draining_ext_list = - pt->ws.tx_draining_ext_list; - pt->ws.tx_draining_ext_list = wsi; - /* we must come back to do more */ - lws_callback_on_writable(wsi); - /* - * keep a copy of the write type for the overall - * action that has provoked generation of these - * fragments, so the last guy can use its FIN state. - */ - wsi->ws->tx_draining_stashed_wp = *wp; - /* this is definitely not actually the last fragment - * because the extension asserted he has more coming - * So make sure this intermediate one doesn't go out - * with a FIN. - */ - *wp |= LWS_WRITE_NO_FIN; - } -#endif - if (ebuf.len && wsi->ws->stashed_write_pending) { - wsi->ws->stashed_write_pending = 0; - *wp = ((*wp) & 0xc0) | (int)wsi->ws->stashed_write_type; - } - } - - /* - * an extension did something we need to keep... for example, if - * compression extension, it has already updated its state according - * to this being issued - */ - if ((char *)buf != ebuf.token) { - /* - * ext might eat it, but not have anything to issue yet. - * In that case we have to follow his lead, but stash and - * replace the write type that was lost here the first time. - */ - if (len && !ebuf.len) { - if (!wsi->ws->stashed_write_pending) - wsi->ws->stashed_write_type = - (char)(*wp) & 0x3f; - wsi->ws->stashed_write_pending = 1; - return (int)len; - } - /* - * extension recreated it: - * need to buffer this if not all sent - */ - wsi->ws->clean_buffer = 0; - } - - buf = (unsigned char *)ebuf.token; - len = ebuf.len; - - if (!buf) { - lwsl_err("null buf (%d)\n", (int)len); - return -1; - } - - switch (wsi->ws->ietf_spec_revision) { - case 13: - if (masked7) { - pre += 4; - dropmask = &buf[0 - pre]; - is_masked_bit = 0x80; - } - - switch ((*wp) & 0xf) { - case LWS_WRITE_TEXT: - n = LWSWSOPC_TEXT_FRAME; - break; - case LWS_WRITE_BINARY: - n = LWSWSOPC_BINARY_FRAME; - break; - case LWS_WRITE_CONTINUATION: - n = LWSWSOPC_CONTINUATION; - break; - - case LWS_WRITE_CLOSE: - n = LWSWSOPC_CLOSE; - break; - case LWS_WRITE_PING: - n = LWSWSOPC_PING; - break; - case LWS_WRITE_PONG: - n = LWSWSOPC_PONG; - break; - default: - lwsl_warn("lws_write: unknown write opc / wp\n"); - return -1; - } - - if (!((*wp) & LWS_WRITE_NO_FIN)) - n |= 1 << 7; - - if (len < 126) { - pre += 2; - buf[-pre] = n; - buf[-pre + 1] = (unsigned char)(len | is_masked_bit); - } else { - if (len < 65536) { - pre += 4; - buf[-pre] = n; - buf[-pre + 1] = 126 | is_masked_bit; - buf[-pre + 2] = (unsigned char)(len >> 8); - buf[-pre + 3] = (unsigned char)len; - } else { - pre += 10; - buf[-pre] = n; - buf[-pre + 1] = 127 | is_masked_bit; -#if defined __LP64__ - buf[-pre + 2] = (len >> 56) & 0x7f; - buf[-pre + 3] = len >> 48; - buf[-pre + 4] = len >> 40; - buf[-pre + 5] = len >> 32; -#else - buf[-pre + 2] = 0; - buf[-pre + 3] = 0; - buf[-pre + 4] = 0; - buf[-pre + 5] = 0; -#endif - buf[-pre + 6] = (unsigned char)(len >> 24); - buf[-pre + 7] = (unsigned char)(len >> 16); - buf[-pre + 8] = (unsigned char)(len >> 8); - buf[-pre + 9] = (unsigned char)len; - } - } - break; - } - -do_more_inside_frame: - - /* - * Deal with masking if we are in client -> server direction and - * the wp demands it - */ - - if (masked7) { - if (!wsi->ws->inside_frame) - if (lws_0405_frame_mask_generate(wsi)) { - lwsl_err("frame mask generation failed\n"); - return -1; - } - - /* - * in v7, just mask the payload - */ - if (dropmask) { /* never set if already inside frame */ - for (n = 4; n < (int)len + 4; n++) - dropmask[n] = dropmask[n] ^ wsi->ws->mask[ - (wsi->ws->mask_idx++) & 3]; - - /* copy the frame nonce into place */ - memcpy(dropmask, wsi->ws->mask, 4); - } - } - - if (lwsi_role_h2_ENCAPSULATION(wsi)) { - struct lws *encap = lws_get_network_wsi(wsi); - - assert(encap != wsi); - return encap->role_ops->write_role_protocol(wsi, buf - pre, - len + pre, wp); - } - - switch ((*wp) & 0x1f) { - case LWS_WRITE_TEXT: - case LWS_WRITE_BINARY: - case LWS_WRITE_CONTINUATION: - if (!wsi->h2_stream_carries_ws) { - - /* - * give any active extensions a chance to munge the - * buffer before send. We pass in a pointer to an - * lws_tokens struct prepared with the default buffer - * and content length that's in there. Rather than - * rewrite the default buffer, extensions that expect - * to grow the buffer can adapt .token to point to their - * own per-connection buffer in the extension user - * allocation. By default with no extensions or no - * extension callback handling, just the normal input - * buffer is used then so it is efficient. - * - * callback returns 1 in case it wants to spill more - * buffers - * - * This takes care of holding the buffer if send is - * incomplete, ie, if wsi->ws->clean_buffer is 0 - * (meaning an extension meddled with the buffer). If - * wsi->ws->clean_buffer is 1, it will instead return - * to the user code how much OF THE USER BUFFER was - * consumed. - */ - - n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre); - wsi->ws->inside_frame = 1; - if (n <= 0) - return n; - - if (n == (int)len + pre) { - /* everything in the buffer was handled - * (or rebuffered...) */ - wsi->ws->inside_frame = 0; - return (int)orig_len; - } - - /* - * it is how many bytes of user buffer got sent... may - * be < orig_len in which case callback when writable - * has already been arranged and user code can call - * lws_write() again with the rest later. - */ - - return n - pre; - } - break; - default: - break; - } - -send_raw: - return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre); -} - -static int -rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason) -{ - /* deal with ws encapsulation in h2 */ -#if defined(LWS_WITH_HTTP2) - if (wsi->http2_substream && wsi->h2_stream_carries_ws) - return role_ops_h2.close_kill_connection(wsi, reason); - - return 0; -#else - return 0; -#endif -} - -static int -rops_callback_on_writable_ws(struct lws *wsi) -{ -#if defined(LWS_WITH_HTTP2) - if (lwsi_role_h2_ENCAPSULATION(wsi)) { - /* we know then that it has an h2 parent */ - struct lws *enc = role_ops_h2.encapsulation_parent(wsi); - - assert(enc); - if (enc->role_ops->callback_on_writable(wsi)) - return 1; - } -#endif - return 0; -} - -static int -rops_init_vhost_ws(struct lws_vhost *vh, - const struct lws_context_creation_info *info) -{ -#if !defined(LWS_WITHOUT_EXTENSIONS) -#ifdef LWS_WITH_PLUGINS - struct lws_plugin *plugin = vh->context->plugin_list; - int m; - - if (vh->context->plugin_extension_count) { - - m = 0; - while (info->extensions && info->extensions[m].callback) - m++; - - /* - * give the vhost a unified list of extensions including the - * ones that came from plugins - */ - vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) * - (m + vh->context->plugin_extension_count + 1), - "extensions"); - if (!vh->ws.extensions) - return 1; - - memcpy((struct lws_extension *)vh->ws.extensions, info->extensions, - sizeof(struct lws_extension) * m); - plugin = vh->context->plugin_list; - while (plugin) { - memcpy((struct lws_extension *)&vh->ws.extensions[m], - plugin->caps.extensions, - sizeof(struct lws_extension) * - plugin->caps.count_extensions); - m += plugin->caps.count_extensions; - plugin = plugin->list; - } - } else -#endif - vh->ws.extensions = info->extensions; -#endif - - return 0; -} - -static int -rops_destroy_vhost_ws(struct lws_vhost *vh) -{ -#ifdef LWS_WITH_PLUGINS -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (vh->context->plugin_extension_count) - lws_free((void *)vh->ws.extensions); -#endif -#endif - - return 0; -} - -static int -rops_destroy_role_ws(struct lws *wsi) -{ - lws_free_set_NULL(wsi->ws); - - return 0; -} - -struct lws_role_ops role_ops_ws = { - /* role name */ "ws", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* init_context */ NULL, - /* init_vhost */ rops_init_vhost_ws, - /* destroy_vhost */ rops_destroy_vhost_ws, - /* periodic_checks */ rops_periodic_checks_ws, - /* service_flag_pending */ rops_service_flag_pending_ws, - /* handle_POLLIN */ rops_handle_POLLIN_ws, - /* handle_POLLOUT */ rops_handle_POLLOUT_ws, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ rops_callback_on_writable_ws, - /* tx_credit */ NULL, - /* write_role_protocol */ rops_write_role_protocol_ws, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ rops_close_via_role_protocol_ws, - /* close_role */ rops_close_role_ws, - /* close_kill_connection */ rops_close_kill_connection_ws, - /* destroy_role */ rops_destroy_role_ws, - /* adoption_bind */ NULL, - /* client_bind */ NULL, - /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE, - LWS_CALLBACK_SERVER_WRITEABLE }, - /* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED, - LWS_CALLBACK_CLOSED }, - /* protocol_bind cb c, srv */ { LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL, - LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL }, - /* protocol_unbind cb c, srv */ { LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL, - LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL }, - /* file handles */ 0 -}; diff --git a/thirdparty/libwebsockets/lib/roles/ws/private.h b/thirdparty/libwebsockets/lib/roles/ws/private.h deleted file mode 100644 index 71ffcaea96..0000000000 --- a/thirdparty/libwebsockets/lib/roles/ws/private.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - * - * This is included from core/private.h if LWS_ROLE_WS - */ - -extern struct lws_role_ops role_ops_ws; - -#define lwsi_role_ws(wsi) (wsi->role_ops == &role_ops_ws) - -enum lws_rx_parse_state { - LWS_RXPS_NEW, - - LWS_RXPS_04_mask_1, - LWS_RXPS_04_mask_2, - LWS_RXPS_04_mask_3, - - LWS_RXPS_04_FRAME_HDR_1, - LWS_RXPS_04_FRAME_HDR_LEN, - LWS_RXPS_04_FRAME_HDR_LEN16_2, - LWS_RXPS_04_FRAME_HDR_LEN16_1, - LWS_RXPS_04_FRAME_HDR_LEN64_8, - LWS_RXPS_04_FRAME_HDR_LEN64_7, - LWS_RXPS_04_FRAME_HDR_LEN64_6, - LWS_RXPS_04_FRAME_HDR_LEN64_5, - LWS_RXPS_04_FRAME_HDR_LEN64_4, - LWS_RXPS_04_FRAME_HDR_LEN64_3, - LWS_RXPS_04_FRAME_HDR_LEN64_2, - LWS_RXPS_04_FRAME_HDR_LEN64_1, - - LWS_RXPS_07_COLLECT_FRAME_KEY_1, - LWS_RXPS_07_COLLECT_FRAME_KEY_2, - LWS_RXPS_07_COLLECT_FRAME_KEY_3, - LWS_RXPS_07_COLLECT_FRAME_KEY_4, - - LWS_RXPS_WS_FRAME_PAYLOAD -}; - -enum lws_websocket_opcodes_07 { - LWSWSOPC_CONTINUATION = 0, - LWSWSOPC_TEXT_FRAME = 1, - LWSWSOPC_BINARY_FRAME = 2, - - LWSWSOPC_NOSPEC__MUX = 7, - - /* control extensions 8+ */ - - LWSWSOPC_CLOSE = 8, - LWSWSOPC_PING = 9, - LWSWSOPC_PONG = 0xa, -}; - -/* this is not usable directly by user code any more, lws_close_reason() */ -#define LWS_WRITE_CLOSE 4 - -#define ALREADY_PROCESSED_IGNORE_CHAR 1 -#define ALREADY_PROCESSED_NO_CB 2 - -#if !defined(LWS_WITHOUT_EXTENSIONS) -struct lws_vhost_role_ws { - const struct lws_extension *extensions; -}; - -struct lws_pt_role_ws { - struct lws *rx_draining_ext_list; - struct lws *tx_draining_ext_list; -}; -#endif - -struct _lws_websocket_related { - char *rx_ubuf; -#if !defined(LWS_WITHOUT_EXTENSIONS) - const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE]; - void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE]; - struct lws *rx_draining_ext_list; - struct lws *tx_draining_ext_list; -#endif - /* Also used for close content... control opcode == < 128 */ - uint8_t ping_payload_buf[128 - 3 + LWS_PRE]; - uint8_t mask[4]; - - time_t time_next_ping_check; - size_t rx_packet_length; - uint32_t rx_ubuf_head; - uint32_t rx_ubuf_alloc; - - uint8_t ping_payload_len; - uint8_t mask_idx; - uint8_t opcode; - uint8_t rsv; - uint8_t rsv_first_msg; - /* zero if no info, or length including 2-byte close code */ - uint8_t close_in_ping_buffer_len; - uint8_t utf8; - uint8_t stashed_write_type; - uint8_t tx_draining_stashed_wp; - uint8_t ietf_spec_revision; - - unsigned int final:1; - unsigned int frame_is_binary:1; - unsigned int all_zero_nonce:1; - unsigned int this_frame_masked:1; - unsigned int inside_frame:1; /* next write will be more of frame */ - unsigned int clean_buffer:1; /* buffer not rewritten by extension */ - unsigned int payload_is_close:1; /* process as PONG, but it is close */ - unsigned int ping_pending_flag:1; - unsigned int continuation_possible:1; - unsigned int owed_a_fin:1; - unsigned int check_utf8:1; - unsigned int defeat_check_utf8:1; - unsigned int stashed_write_pending:1; - unsigned int send_check_ping:1; - unsigned int first_fragment:1; - unsigned int peer_has_sent_close:1; -#if !defined(LWS_WITHOUT_EXTENSIONS) - unsigned int extension_data_pending:1; - unsigned int rx_draining_ext:1; - unsigned int tx_draining_ext:1; - - uint8_t count_act_ext; -#endif -}; - -int -lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len); - -#if !defined(LWS_WITHOUT_EXTENSIONS) -LWS_VISIBLE void -lws_context_init_extensions(const struct lws_context_creation_info *info, - struct lws_context *context); -LWS_EXTERN int -lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r, - void *v, size_t len); - -LWS_EXTERN int -lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len); -LWS_EXTERN int -lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason, - void *arg, int len); -#endif - -int -handshake_0405(struct lws_context *context, struct lws *wsi); -int -lws_process_ws_upgrade(struct lws *wsi); -int -lws_server_init_wsi_for_ws(struct lws *wsi); diff --git a/thirdparty/libwebsockets/lib/roles/ws/server-ws.c b/thirdparty/libwebsockets/lib/roles/ws/server-ws.c deleted file mode 100644 index bdc45367d4..0000000000 --- a/thirdparty/libwebsockets/lib/roles/ws/server-ws.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include <core/private.h> - -#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } - -#if !defined(LWS_WITHOUT_EXTENSIONS) -static int -lws_extension_server_handshake(struct lws *wsi, char **p, int budget) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char ext_name[64], *args, *end = (*p) + budget - 1; - const struct lws_ext_options *opts, *po; - const struct lws_extension *ext; - struct lws_ext_option_arg oa; - int n, m, more = 1; - int ext_count = 0; - char ignore; - char *c; - - /* - * Figure out which extensions the client has that we want to - * enable on this connection, and give him back the list - */ - if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) - return 0; - - /* - * break down the list of client extensions - * and go through them - */ - - if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size, - WSI_TOKEN_EXTENSIONS) < 0) - return 1; - - c = (char *)pt->serv_buf; - lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); - wsi->ws->count_act_ext = 0; - ignore = 0; - n = 0; - args = NULL; - - /* - * We may get a simple request - * - * Sec-WebSocket-Extensions: permessage-deflate - * - * or an elaborated one with requested options - * - * Sec-WebSocket-Extensions: permessage-deflate; \ - * server_no_context_takeover; \ - * client_no_context_takeover - */ - - while (more) { - - if (c >= (char *)pt->serv_buf + 255) - return -1; - - if (*c && (*c != ',' && *c != '\t')) { - if (*c == ';') { - ignore = 1; - if (!args) - args = c + 1; - } - if (ignore || *c == ' ') { - c++; - continue; - } - ext_name[n] = *c++; - if (n < (int)sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - - ignore = 0; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - while (args && *args == ' ') - args++; - - /* check a client's extension against our support */ - - ext = wsi->vhost->ws.extensions; - - while (ext && ext->callback) { - - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - /* - * oh, we do support this one he asked for... but let's - * confirm he only gave it once - */ - for (m = 0; m < wsi->ws->count_act_ext; m++) - if (wsi->ws->active_extensions[m] == ext) { - lwsl_info("ext mentioned twice\n"); - return 1; /* shenanigans */ - } - - /* - * ask user code if it's OK to apply it on this - * particular connection + protocol - */ - m = (wsi->protocol->callback)(wsi, - LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, - wsi->user_space, ext_name, 0); - - /* - * zero return from callback means go ahead and allow - * the extension, it's what we get if the callback is - * unhandled - */ - if (m) { - ext++; - continue; - } - - /* apply it */ - - ext_count++; - - /* instantiate the extension on this conn */ - - wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext; - - /* allow him to construct his context */ - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_CONSTRUCT, - (void *)&wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - (void *)&opts, 0)) { - lwsl_info("ext %s failed construction\n", - ext_name); - ext_count--; - ext++; - - continue; - } - - if (ext_count > 1) - *(*p)++ = ','; - else - LWS_CPYAPP(*p, - "\x0d\x0aSec-WebSocket-Extensions: "); - *p += lws_snprintf(*p, (end - *p), "%s", ext_name); - - /* - * The client may send a bunch of different option - * sets for the same extension, we are supposed to - * pick one we like the look of. The option sets are - * separated by comma. - * - * Actually we just either accept the first one or - * nothing. - * - * Go through the options trying to apply the - * recognized ones - */ - - lwsl_info("ext args %s\n", args); - - while (args && *args && *args != ',') { - while (*args == ' ') - args++; - po = opts; - while (po->name) { - /* only support arg-less options... */ - if (po->type != EXTARG_NONE || - strncmp(args, po->name, - strlen(po->name))) { - po++; - continue; - } - oa.option_name = NULL; - oa.option_index = (int)(po - opts); - oa.start = NULL; - oa.len = 0; - lwsl_info("setting '%s'\n", po->name); - if (!ext->callback(lws_get_context(wsi), - ext, wsi, - LWS_EXT_CB_OPTION_SET, - wsi->ws->act_ext_user[ - wsi->ws->count_act_ext], - &oa, (end - *p))) { - - *p += lws_snprintf(*p, - (end - *p), - "; %s", po->name); - lwsl_debug("adding option %s\n", - po->name); - } - po++; - } - while (*args && *args != ',' && *args != ';') - args++; - - if (*args == ';') - args++; - } - - wsi->ws->count_act_ext++; - lwsl_parser("cnt_act_ext <- %d\n", - wsi->ws->count_act_ext); - - if (args && *args == ',') - more = 0; - - ext++; - } - - n = 0; - args = NULL; - } - - return 0; -} -#endif - -int -lws_process_ws_upgrade(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - const struct lws_protocols *pcol = NULL; - char buf[128], name[64]; - struct lws_tokenize ts; - lws_tokenize_elem e; - - if (!wsi->protocol) - lwsl_err("NULL protocol at lws_read\n"); - - /* - * We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined - * header considerations about keeping the ah around no longer apply. - * - * However it's common for the first ws protocol data to have been - * coalesced with the browser upgrade request and to already be in the - * ah rx buffer. - */ - - lws_pt_lock(pt, __func__); - - if (!wsi->h2_stream_carries_ws) - lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, - &role_ops_ws); - - lws_pt_unlock(pt); - - /* - * It's either websocket or h2->websocket - * - * If we are on h1, confirm we got the required "connection: upgrade" - * header. h2 / ws-over-h2 does not have this. - */ - -#if defined(LWS_WITH_HTTP2) - if (wsi->http2_substream) - goto check_protocol; -#endif - - lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST | - LWS_TOKENIZE_F_MINUS_NONTERM); - ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION); - if (ts.len <= 0) - goto bad_conn_format; - - do { - e = lws_tokenize(&ts); - switch (e) { - case LWS_TOKZE_TOKEN: - if (!strcasecmp(ts.token, "upgrade")) - e = LWS_TOKZE_ENDED; - break; - - case LWS_TOKZE_DELIMITER: - break; - - default: /* includes ENDED */ -bad_conn_format: - lwsl_err("%s: malformed or absent connection hdr\n", - __func__); - - return 1; - } - } while (e > 0); - -#if defined(LWS_WITH_HTTP2) -check_protocol: -#endif - - /* - * Select the first protocol we support from the list - * the client sent us. - */ - - lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST | - LWS_TOKENIZE_F_MINUS_NONTERM | - LWS_TOKENIZE_F_RFC7230_DELIMS); - ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_PROTOCOL); - if (ts.len < 0) { - lwsl_err("%s: protocol list too long\n", __func__); - return 1; - } - if (!ts.len) { - int n = wsi->vhost->default_protocol_index; - /* - * some clients only have one protocol and do not send the - * protocol list header... allow it and match to the vhost's - * default protocol (which itself defaults to zero) - */ - lwsl_info("%s: defaulting to prot handler %d\n", __func__, n); - - lws_bind_protocol(wsi, &wsi->vhost->protocols[n], - "ws upgrade default pcol"); - - goto alloc_ws; - } - - /* otherwise go through the user-provided protocol list */ - - do { - e = lws_tokenize(&ts); - switch (e) { - case LWS_TOKZE_TOKEN: - - if (lws_tokenize_cstr(&ts, name, sizeof(name))) { - lwsl_err("%s: pcol name too long\n", __func__); - - return 1; - } - lwsl_debug("checking %s\n", name); - pcol = lws_vhost_name_to_protocol(wsi->vhost, name); - if (pcol) { - /* if we know it, bind to it and stop looking */ - lws_bind_protocol(wsi, pcol, "ws upg pcol"); - e = LWS_TOKZE_ENDED; - } - break; - - case LWS_TOKZE_DELIMITER: - case LWS_TOKZE_ENDED: - break; - - default: - lwsl_err("%s: malformatted protocol list", __func__); - - return 1; - } - } while (e > 0); - - /* we didn't find a protocol he wanted? */ - - if (!pcol) { - lwsl_notice("No supported protocol \"%s\"\n", buf); - - return 1; - } - -alloc_ws: - - /* allocate the ws struct for the wsi */ - - wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct"); - if (!wsi->ws) { - lwsl_notice("OOM\n"); - return 1; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION)) - wsi->ws->ietf_spec_revision = - atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION)); - - /* allocate wsi->user storage */ - if (lws_ensure_user_space(wsi)) { - lwsl_notice("problem with user space\n"); - return 1; - } - - /* - * Give the user code a chance to study the request and - * have the opportunity to deny it - */ - if ((wsi->protocol->callback)(wsi, - LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, - wsi->user_space, - lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { - lwsl_warn("User code denied connection\n"); - return 1; - } - - /* - * Perform the handshake according to the protocol version the - * client announced - */ - - switch (wsi->ws->ietf_spec_revision) { - default: - lwsl_notice("Unknown client spec version %d\n", - wsi->ws->ietf_spec_revision); - wsi->ws->ietf_spec_revision = 13; - //return 1; - /* fallthru */ - case 13: -#if defined(LWS_WITH_HTTP2) - if (wsi->h2_stream_carries_ws) { - if (lws_h2_ws_handshake(wsi)) { - lwsl_notice("h2 ws handshake failed\n"); - return 1; - } - lws_role_transition(wsi, - LWSIFR_SERVER | LWSIFR_P_ENCAP_H2, - LRS_ESTABLISHED, &role_ops_ws); - } else -#endif - { - lwsl_parser("lws_parse calling handshake_04\n"); - if (handshake_0405(wsi->context, wsi)) { - lwsl_notice("hs0405 has failed the connection\n"); - return 1; - } - } - break; - } - - lws_server_init_wsi_for_ws(wsi); - lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision); - - lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi); - lws_header_table_detach(wsi, 1); - - return 0; -} - -int -handshake_0405(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_process_html_args args; - unsigned char hash[20]; - int n, accept_len; - char *response; - char *p; - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || - !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { - lwsl_info("handshake_04 missing pieces\n"); - /* completed header processing, but missing some bits */ - goto bail; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= - MAX_WEBSOCKET_04_KEY_LEN) { - lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN); - goto bail; - } - - /* - * since key length is restricted above (currently 128), cannot - * overflow - */ - n = sprintf((char *)pt->serv_buf, - "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); - - lws_SHA1(pt->serv_buf, n, hash); - - accept_len = lws_b64_encode_string((char *)hash, 20, - (char *)pt->serv_buf, context->pt_serv_buf_size); - if (accept_len < 0) { - lwsl_warn("Base64 encoded hash too long\n"); - goto bail; - } - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) - goto bail; - - /* create the response packet */ - - /* make a buffer big enough for everything */ - - response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + - 256 + LWS_PRE; - p = response; - LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a" - "Upgrade: WebSocket\x0d\x0a" - "Connection: Upgrade\x0d\x0a" - "Sec-WebSocket-Accept: "); - strcpy(p, (char *)pt->serv_buf); - p += accept_len; - - /* we can only return the protocol header if: - * - one came in, and ... */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) && - /* - it is not an empty string */ - wsi->protocol->name && - wsi->protocol->name[0]) { - LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: "); - p += lws_snprintf(p, 128, "%s", wsi->protocol->name); - } - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* - * Figure out which extensions the client has that we want to - * enable on this connection, and give him back the list. - * - * Give him a limited write bugdet - */ - if (lws_extension_server_handshake(wsi, &p, 192)) - goto bail; -#endif - LWS_CPYAPP(p, "\x0d\x0a"); - - args.p = p; - args.max_len = lws_ptr_diff((char *)pt->serv_buf + - context->pt_serv_buf_size, p); - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_ADD_HEADERS, - wsi->user_space, &args, 0)) - goto bail; - - p = args.p; - - /* end of response packet */ - - LWS_CPYAPP(p, "\x0d\x0a"); - - /* okay send the handshake response accepting the connection */ - - lwsl_parser("issuing resp pkt %d len\n", - lws_ptr_diff(p, response)); -#if defined(DEBUG) - fwrite(response, 1, p - response, stderr); -#endif - n = lws_write(wsi, (unsigned char *)response, p - response, - LWS_WRITE_HTTP_HEADERS); - if (n != (p - response)) { - lwsl_info("%s: ERROR writing to socket %d\n", __func__, n); - goto bail; - } - - /* alright clean up and set ourselves into established state */ - - lwsi_set_state(wsi, LRS_ESTABLISHED); - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - - { - const char * uri_ptr = - lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); - int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); - const struct lws_http_mount *hit = - lws_find_mount(wsi, uri_ptr, uri_len); - if (hit && hit->cgienv && - wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO, - wsi->user_space, (void *)hit->cgienv, 0)) - return 1; - } - - return 0; - -bail: - /* caller will free up his parsing allocations */ - return -1; -} - - - -/* - * Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much - * to expect in that state and can deal with it in bulk more efficiently. - */ - -static int -lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len) -{ - unsigned int avail = (unsigned int)len; - uint8_t *buffer = *buf, mask[4]; - struct lws_tokens ebuf; -#if !defined(LWS_WITHOUT_EXTENSIONS) - unsigned int old_packet_length = (int)wsi->ws->rx_packet_length; -#endif - int n = 0; - - /* - * With zlib, we can give it as much input as we like. The pmd - * extension will draw it down in chunks (default 1024). - * - * If we try to restrict how much we give it, because we must go - * back to the event loop each time, we will drop the remainder... - */ - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (!wsi->ws->count_act_ext) -#endif - { - if (wsi->protocol->rx_buffer_size) - avail = (int)wsi->protocol->rx_buffer_size; - else - avail = wsi->context->pt_serv_buf_size; - } - - /* do not consume more than we should */ - if (avail > wsi->ws->rx_packet_length) - avail = (unsigned int)wsi->ws->rx_packet_length; - - /* do not consume more than what is in the buffer */ - if (avail > len) - avail = (unsigned int)len; - - if (!avail) - return 0; - - ebuf.token = (char *)buffer; - ebuf.len = avail; - - //lwsl_hexdump_notice(ebuf.token, ebuf.len); - - if (!wsi->ws->all_zero_nonce) { - - for (n = 0; n < 4; n++) - mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3]; - - /* deal with 4-byte chunks using unwrapped loop */ - n = avail >> 2; - while (n--) { - *(buffer) = *(buffer) ^ mask[0]; - buffer++; - *(buffer) = *(buffer) ^ mask[1]; - buffer++; - *(buffer) = *(buffer) ^ mask[2]; - buffer++; - *(buffer) = *(buffer) ^ mask[3]; - buffer++; - } - /* and the remaining bytes bytewise */ - for (n = 0; n < (int)(avail & 3); n++) { - *(buffer) = *(buffer) ^ mask[n]; - buffer++; - } - - wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3; - } - - lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__, - avail, (int)len); - - (*buf) += avail; - len -= avail; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0); - lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len); -#endif - /* - * ebuf may be pointing somewhere completely different now, - * it's the output - */ - -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (n < 0) { - /* - * we may rely on this to get RX, just drop connection - */ - lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__); - wsi->socket_is_permanently_unusable = 1; - return -1; - } -#endif - - wsi->ws->rx_packet_length -= avail; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - /* - * if we had an rx fragment right at the last compressed byte of the - * message, we can get a zero length inflated output, where no prior - * rx inflated output marked themselves with FIN, since there was - * raw ws payload still to drain at that time. - * - * Then we need to generate a zero length ws rx that can be understood - * as the message completion. - */ - - if (!ebuf.len && /* zero-length inflation output */ - !n && /* nothing left to drain from the inflator */ - wsi->ws->count_act_ext && /* we are using pmd */ - old_packet_length && /* we gave the inflator new input */ - !wsi->ws->rx_packet_length && /* raw ws packet payload all gone */ - wsi->ws->final && /* the raw ws packet is a FIN guy */ - wsi->protocol->callback && - !wsi->wsistate_pre_close) { - - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_RECEIVE, - wsi->user_space, NULL, 0)) - return -1; - - return avail; - } -#endif - - if (!ebuf.len) - return avail; - - if ( -#if !defined(LWS_WITHOUT_EXTENSIONS) - n && -#endif - ebuf.len) - /* extension had more... main loop will come back */ - lws_add_wsi_to_draining_ext_list(wsi); - else - lws_remove_wsi_from_draining_ext_list(wsi); - - if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { - if (lws_check_utf8(&wsi->ws->utf8, - (unsigned char *)ebuf.token, ebuf.len)) { - lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"bad utf8", 8); - goto utf8_fail; - } - - /* we are ending partway through utf-8 character? */ - if (!wsi->ws->rx_packet_length && wsi->ws->final && - wsi->ws->utf8 && !n) { - lwsl_info("FINAL utf8 error\n"); - lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD, - (uint8_t *)"partial utf8", 12); - -utf8_fail: - lwsl_info("utf8 error\n"); - lwsl_hexdump_info(ebuf.token, ebuf.len); - - return -1; - } - } - - if (wsi->protocol->callback && !wsi->wsistate_pre_close) - if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, - LWS_CALLBACK_RECEIVE, - wsi->user_space, - ebuf.token, ebuf.len)) - return -1; - - wsi->ws->first_fragment = 0; - -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n", - __func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext); -#endif - - return avail; /* how much we used from the input */ -} - - -int -lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len) -{ - int m, bulk = 0; - - lwsl_debug("%s: received %d byte packet\n", __func__, (int)len); - - //lwsl_hexdump_notice(*buf, len); - - /* let the rx protocol state machine have as much as it needs */ - - while (len) { - /* - * we were accepting input but now we stopped doing so - */ - if (wsi->rxflow_bitmap) { - lwsl_info("%s: doing rxflow\n", __func__); - lws_rxflow_cache(wsi, *buf, 0, (int)len); - lwsl_parser("%s: cached %ld\n", __func__, (long)len); - *buf += len; /* stashing it is taking care of it */ - return 1; - } -#if !defined(LWS_WITHOUT_EXTENSIONS) - if (wsi->ws->rx_draining_ext) { - lwsl_debug("%s: draining rx ext\n", __func__); - m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0); - if (m < 0) - return -1; - continue; - } -#endif - - /* consume payload bytes efficiently */ - while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD && - (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME || - wsi->ws->opcode == LWSWSOPC_BINARY_FRAME || - wsi->ws->opcode == LWSWSOPC_CONTINUATION) && - len) { - uint8_t *bin = *buf; - - bulk = 1; - m = lws_ws_frame_rest_is_payload(wsi, buf, len); - assert((int)lws_ptr_diff(*buf, bin) <= (int)len); - len -= lws_ptr_diff(*buf, bin); - - if (!m) { - - break; - } - if (m < 0) { - lwsl_info("%s: rest_is_payload bailed\n", - __func__); - return -1; - } - } - - if (!bulk) { - /* process the byte */ - m = lws_ws_rx_sm(wsi, 0, *(*buf)++); - len--; - } else { - /* - * We already handled this byte in bulk, just deal - * with the ramifications - */ -#if !defined(LWS_WITHOUT_EXTENSIONS) - lwsl_debug("%s: coming out of bulk with len %d, " - "wsi->ws->rx_draining_ext %d\n", - __func__, (int)len, - wsi->ws->rx_draining_ext); -#endif - m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR | - ALREADY_PROCESSED_NO_CB, 0); - } - - if (m < 0) { - lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__, - bulk); - - return -1; - } - - bulk = 0; - } - - lwsl_debug("%s: exit with %d unused\n", __func__, (int)len); - - return 0; -} |