| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to |
| * deal in the Software without restriction, including without limitation the |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| * sell copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "private-lib-core.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; |
| struct lws_ext_pm_deflate_rx_ebufs pmdrx; |
| unsigned short close_code; |
| unsigned char *pp; |
| int handled, m, n; |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| int rx_draining_ext = 0; |
| #endif |
| |
| pmdrx.eb_in.token = NULL; |
| pmdrx.eb_in.len = 0; |
| pmdrx.eb_out.token = NULL; |
| pmdrx.eb_out.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 |
| |
| /* |
| * Peer may have closed, but that doesn't necessarily mean we should not |
| * parse any rx we have from them. |
| * |
| 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); |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| /* |
| * set the expectation that we will have to |
| * fake up the zlib trailer to the inflator for |
| * this frame |
| */ |
| wsi->ws->pmd_trailer_application = !!(c & 0x40); |
| #endif |
| wsi->ws->continuation_possible = 1; |
| wsi->ws->check_utf8 = lws_check_opt( |
| wsi->a.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); |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| /* |
| * set the expectation that we will have to |
| * fake up the zlib trailer to the inflator for |
| * this frame |
| */ |
| wsi->ws->pmd_trailer_application = !!(c & 0x40); |
| #endif |
| 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 = (size_t)((unsigned int)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]; |
| |
| /* |
| * unmask and collect the payload body in |
| * rx_ubuf_head + LWS_PRE |
| */ |
| |
| 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; |
| lwsl_debug("%s: spilling as we have the whole frame\n", |
| __func__); |
| goto spill; |
| } |
| |
| /* |
| * if there's no protocol max frame size given, we are |
| * supposed to default to context->pt_serv_buf_size |
| */ |
| if (!wsi->a.protocol->rx_buffer_size && |
| wsi->ws->rx_ubuf_head != wsi->a.context->pt_serv_buf_size) |
| break; |
| |
| if (wsi->a.protocol->rx_buffer_size && |
| wsi->ws->rx_ubuf_head != wsi->a.protocol->rx_buffer_size) |
| break; |
| |
| /* spill because we filled our rx buffer */ |
| |
| lwsl_debug("%s: spilling as we filled our rx buffer\n", |
| __func__); |
| 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 = &wsi->ws->rx_ubuf[LWS_PRE]; |
| if (lws_check_opt(wsi->a.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", |
| (int)wsi->ws->rx_ubuf_head); |
| if (wsi->ws->rx_ubuf_head >= 2) { |
| close_code = (unsigned short)((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->a.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 = |
| (uint8_t)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", |
| (int)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 = (uint8_t)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("%s: %s received pong\n", __func__, lws_wsi_tag(wsi)); |
| lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE], |
| wsi->ws->rx_ubuf_head); |
| |
| lws_validity_confirmed(wsi); |
| /* 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. |
| * |
| * We have been statefully collecting it in the |
| * LWS_RXPS_WS_FRAME_PAYLOAD clause above. |
| * |
| * It's nicely buffered with the pre-padding taken care of |
| * so it can be sent straight out again using lws_write. |
| * |
| * However, now we have a chunk of it, we want to deal with it |
| * all here. Since this may be input to permessage-deflate and |
| * there are block limits on that for input and output, we may |
| * need to iterate. |
| */ |
| if (handled) |
| goto already_done; |
| |
| pmdrx.eb_in.token = &wsi->ws->rx_ubuf[LWS_PRE]; |
| pmdrx.eb_in.len = (int)wsi->ws->rx_ubuf_head; |
| |
| /* for the non-pm-deflate case */ |
| |
| pmdrx.eb_out = pmdrx.eb_in; |
| |
| lwsl_debug("%s: starting disbursal of %d deframed rx\n", |
| __func__, (int)wsi->ws->rx_ubuf_head); |
| |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| drain_extension: |
| #endif |
| do { |
| |
| // lwsl_notice("%s: pmdrx.eb_in.len: %d\n", __func__, |
| // (int)pmdrx.eb_in.len); |
| |
| n = PMDR_DID_NOTHING; |
| |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| lwsl_ext("%s: +++ passing %d %p to ext\n", __func__, |
| pmdrx.eb_in.len, pmdrx.eb_in.token); |
| |
| n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, |
| &pmdrx, 0); |
| lwsl_ext("Ext RX returned %d\n", n); |
| if (n < 0) { |
| wsi->socket_is_permanently_unusable = 1; |
| return -1; |
| } |
| if (n == PMDR_DID_NOTHING) |
| /* ie, not PMDR_NOTHING_WE_SHOULD_DO */ |
| break; |
| #endif |
| lwsl_ext("%s: post inflate ebuf in len %d / out len %d\n", |
| __func__, pmdrx.eb_in.len, pmdrx.eb_out.len); |
| |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| if (rx_draining_ext && !pmdrx.eb_out.len) { |
| lwsl_debug(" --- ending drain on 0 read result\n"); |
| goto already_done; |
| } |
| |
| if (n == PMDR_HAS_PENDING) { /* 1 means stuff to drain */ |
| /* extension had more... main loop will come back */ |
| lwsl_ext("%s: adding to draining ext list\n", |
| __func__); |
| lws_add_wsi_to_draining_ext_list(wsi); |
| } else { |
| lwsl_ext("%s: removing from draining ext list\n", |
| __func__); |
| lws_remove_wsi_from_draining_ext_list(wsi); |
| } |
| rx_draining_ext = wsi->ws->rx_draining_ext; |
| #endif |
| |
| if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { |
| |
| if (lws_check_utf8(&wsi->ws->utf8, |
| pmdrx.eb_out.token, |
| (unsigned int)pmdrx.eb_out.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) |
| /* if ext not negotiated, going to be UNKNOWN */ |
| && (n == PMDR_EMPTY_FINAL || n == PMDR_UNKNOWN) |
| #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(pmdrx.eb_out.token, |
| (unsigned int)pmdrx.eb_out.len); |
| |
| return -1; |
| } |
| } |
| |
| if (pmdrx.eb_out.len < 0 && |
| callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) |
| goto already_done; |
| |
| if (!pmdrx.eb_out.token) |
| goto already_done; |
| |
| pmdrx.eb_out.token[pmdrx.eb_out.len] = '\0'; |
| |
| if (!wsi->a.protocol->callback) |
| goto already_done; |
| |
| if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) |
| lwsl_info("Client doing pong callback\n"); |
| |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| if (n == PMDR_HAS_PENDING) |
| /* 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); |
| #endif |
| |
| 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; |
| |
| /* if pmd not enabled, in == out */ |
| |
| if (n == PMDR_DID_NOTHING |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| || n == PMDR_UNKNOWN |
| #endif |
| ) |
| pmdrx.eb_in.len -= pmdrx.eb_out.len; |
| |
| m = wsi->a.protocol->callback(wsi, |
| (enum lws_callback_reasons)callback_action, |
| wsi->user_space, pmdrx.eb_out.token, |
| (unsigned int)pmdrx.eb_out.len); |
| |
| wsi->ws->first_fragment = 0; |
| |
| lwsl_debug("%s: bulk ws rx: inp used %d, output %d\n", |
| __func__, (int)wsi->ws->rx_ubuf_head, |
| (int)pmdrx.eb_out.len); |
| |
| /* if user code wants to close, let caller know */ |
| if (m) |
| return 1; |
| |
| } while (pmdrx.eb_in.len |
| #if !defined(LWS_WITHOUT_EXTENSIONS) |
| || rx_draining_ext |
| #endif |
| ); |
| |
| 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; |
| } |
| |
| |