| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 | /* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010-2018 Andy Green <[email protected]> * *  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;}
 |