mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
remove broken experimental code
This commit is contained in:
@@ -9,11 +9,6 @@ SUBDIRS += testzzuf
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Finally (last!) also build experimental lib...
|
|
||||||
if HAVE_EXPERIMENTAL
|
|
||||||
SUBDIRS += microhttpd_ws
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_EXAMPLES
|
if BUILD_EXAMPLES
|
||||||
SUBDIRS += examples
|
SUBDIRS += examples
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
# This Makefile.am is in the public domain
|
|
||||||
AM_CPPFLAGS = \
|
|
||||||
-I$(top_srcdir)/src/include \
|
|
||||||
-I$(top_srcdir)/src/microhttpd
|
|
||||||
|
|
||||||
AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
|
|
||||||
|
|
||||||
$(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/Makefile
|
|
||||||
@echo ' cd $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la'; \
|
|
||||||
$(am__cd) $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la
|
|
||||||
|
|
||||||
noinst_DATA =
|
|
||||||
MOSTLYCLEANFILES =
|
|
||||||
|
|
||||||
SUBDIRS = .
|
|
||||||
|
|
||||||
lib_LTLIBRARIES = \
|
|
||||||
libmicrohttpd_ws.la
|
|
||||||
libmicrohttpd_ws_la_SOURCES = \
|
|
||||||
sha1.c sha1.h \
|
|
||||||
mhd_websocket.c
|
|
||||||
libmicrohttpd_ws_la_CPPFLAGS = \
|
|
||||||
$(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) \
|
|
||||||
-DBUILDING_MHD_LIB=1
|
|
||||||
libmicrohttpd_ws_la_CFLAGS = \
|
|
||||||
$(AM_CFLAGS) $(MHD_LIB_CFLAGS)
|
|
||||||
libmicrohttpd_ws_la_LDFLAGS = \
|
|
||||||
$(MHD_LIB_LDFLAGS) \
|
|
||||||
$(W32_MHD_LIB_LDFLAGS) \
|
|
||||||
-version-info 0:0:0
|
|
||||||
libmicrohttpd_ws_la_LIBADD = \
|
|
||||||
$(MHD_LIBDEPS)
|
|
||||||
|
|
||||||
TESTS = $(check_PROGRAMS)
|
|
||||||
|
|
||||||
check_PROGRAMS = \
|
|
||||||
test_websocket
|
|
||||||
|
|
||||||
test_websocket_SOURCES = \
|
|
||||||
test_websocket.c
|
|
||||||
test_websocket_LDADD = \
|
|
||||||
$(top_builddir)/src/microhttpd_ws/libmicrohttpd_ws.la \
|
|
||||||
$(top_builddir)/src/microhttpd/libmicrohttpd.la
|
|
||||||
@@ -1,2443 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of libmicrohttpd
|
|
||||||
Copyright (C) 2021 David Gausmann
|
|
||||||
|
|
||||||
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; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file microhttpd_ws/mhd_websocket.c
|
|
||||||
* @brief Support for the websocket protocol
|
|
||||||
* @author David Gausmann
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include "microhttpd.h"
|
|
||||||
#include "microhttpd_ws.h"
|
|
||||||
#include "sha1.h"
|
|
||||||
|
|
||||||
struct MHD_WebSocketStream
|
|
||||||
{
|
|
||||||
/* The function pointer to malloc for payload (can be used to use different memory management) */
|
|
||||||
MHD_WebSocketMallocCallback malloc;
|
|
||||||
/* The function pointer to realloc for payload (can be used to use different memory management) */
|
|
||||||
MHD_WebSocketReallocCallback realloc;
|
|
||||||
/* The function pointer to free for payload (can be used to use different memory management) */
|
|
||||||
MHD_WebSocketFreeCallback free;
|
|
||||||
/* A closure for the random number generator (only used for client mode; usually not required) */
|
|
||||||
void *cls_rng;
|
|
||||||
/* The random number generator (only used for client mode; usually not required) */
|
|
||||||
MHD_WebSocketRandomNumberGenerator rng;
|
|
||||||
/* The flags specified upon initialization. It may alter the behavior of decoding/encoding */
|
|
||||||
int flags;
|
|
||||||
/* The current step for the decoder. 0 means start of a frame. */
|
|
||||||
char decode_step;
|
|
||||||
/* Specifies whether the stream is valid (1) or not (0),
|
|
||||||
if a close frame has been received this is (-1) to indicate that no data frames are allowed anymore */
|
|
||||||
char validity;
|
|
||||||
/* The current step of the UTF-8 encoding check in the data payload */
|
|
||||||
char data_utf8_step;
|
|
||||||
/* The current step of the UTF-8 encoding check in the control payload */
|
|
||||||
char control_utf8_step;
|
|
||||||
/* if != 0 means that we expect a CONTINUATION frame */
|
|
||||||
char data_type;
|
|
||||||
/* The start of the current frame (may differ from data_payload for CONTINUATION frames) */
|
|
||||||
char *data_payload_start;
|
|
||||||
/* The buffer for the data frame */
|
|
||||||
char *data_payload;
|
|
||||||
/* The buffer for the control frame */
|
|
||||||
char *control_payload;
|
|
||||||
/* Configuration for the maximum allowed buffer size for payload data */
|
|
||||||
size_t max_payload_size;
|
|
||||||
/* The current frame header size */
|
|
||||||
size_t frame_header_size;
|
|
||||||
/* The current data payload size (can be greater than payload_size for fragmented frames) */
|
|
||||||
size_t data_payload_size;
|
|
||||||
/* The size of the payload of the current frame (control or data) */
|
|
||||||
size_t payload_size;
|
|
||||||
/* The processing offset to the start of the payload of the current frame (control or data) */
|
|
||||||
size_t payload_index;
|
|
||||||
/* The frame header of the current frame (control or data) */
|
|
||||||
char frame_header[32];
|
|
||||||
/* The mask key of the current frame (control or data); this is 0 if no masking used */
|
|
||||||
char mask_key[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT MHD_WEBSOCKET_FLAG_CLIENT
|
|
||||||
#define MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION \
|
|
||||||
MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
|
|
||||||
#define MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES \
|
|
||||||
MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
|
|
||||||
#define MHD_WEBSOCKET_FLAG_MASK_ALL \
|
|
||||||
(MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT \
|
|
||||||
| MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION \
|
|
||||||
| MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES)
|
|
||||||
|
|
||||||
enum MHD_WebSocket_Opcode
|
|
||||||
{
|
|
||||||
MHD_WebSocket_Opcode_Continuation = 0x0,
|
|
||||||
MHD_WebSocket_Opcode_Text = 0x1,
|
|
||||||
MHD_WebSocket_Opcode_Binary = 0x2,
|
|
||||||
MHD_WebSocket_Opcode_Close = 0x8,
|
|
||||||
MHD_WebSocket_Opcode_Ping = 0x9,
|
|
||||||
MHD_WebSocket_Opcode_Pong = 0xA
|
|
||||||
};
|
|
||||||
|
|
||||||
enum MHD_WebSocket_DecodeStep
|
|
||||||
{
|
|
||||||
MHD_WebSocket_DecodeStep_Start = 0,
|
|
||||||
MHD_WebSocket_DecodeStep_Length1ofX = 1,
|
|
||||||
MHD_WebSocket_DecodeStep_Length1of2 = 2,
|
|
||||||
MHD_WebSocket_DecodeStep_Length2of2 = 3,
|
|
||||||
MHD_WebSocket_DecodeStep_Length1of8 = 4,
|
|
||||||
MHD_WebSocket_DecodeStep_Length2of8 = 5,
|
|
||||||
MHD_WebSocket_DecodeStep_Length3of8 = 6,
|
|
||||||
MHD_WebSocket_DecodeStep_Length4of8 = 7,
|
|
||||||
MHD_WebSocket_DecodeStep_Length5of8 = 8,
|
|
||||||
MHD_WebSocket_DecodeStep_Length6of8 = 9,
|
|
||||||
MHD_WebSocket_DecodeStep_Length7of8 = 10,
|
|
||||||
MHD_WebSocket_DecodeStep_Length8of8 = 11,
|
|
||||||
MHD_WebSocket_DecodeStep_Mask1Of4 = 12,
|
|
||||||
MHD_WebSocket_DecodeStep_Mask2Of4 = 13,
|
|
||||||
MHD_WebSocket_DecodeStep_Mask3Of4 = 14,
|
|
||||||
MHD_WebSocket_DecodeStep_Mask4Of4 = 15,
|
|
||||||
MHD_WebSocket_DecodeStep_HeaderCompleted = 16,
|
|
||||||
MHD_WebSocket_DecodeStep_PayloadOfDataFrame = 17,
|
|
||||||
MHD_WebSocket_DecodeStep_PayloadOfControlFrame = 18,
|
|
||||||
MHD_WebSocket_DecodeStep_BrokenStream = 99
|
|
||||||
};
|
|
||||||
|
|
||||||
enum MHD_WebSocket_UTF8Result
|
|
||||||
{
|
|
||||||
MHD_WebSocket_UTF8Result_Invalid = 0,
|
|
||||||
MHD_WebSocket_UTF8Result_Valid = 1,
|
|
||||||
MHD_WebSocket_UTF8Result_Incomplete = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
MHD_websocket_copy_payload (char *dst,
|
|
||||||
const char *src,
|
|
||||||
size_t len,
|
|
||||||
uint32_t mask,
|
|
||||||
unsigned long mask_offset);
|
|
||||||
|
|
||||||
static int
|
|
||||||
MHD_websocket_check_utf8 (const char *buf,
|
|
||||||
size_t buf_len,
|
|
||||||
int *utf8_step,
|
|
||||||
size_t *buf_offset);
|
|
||||||
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_decode_header_complete (struct MHD_WebSocketStream *ws,
|
|
||||||
char **payload,
|
|
||||||
size_t *payload_len);
|
|
||||||
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream *ws,
|
|
||||||
char **payload,
|
|
||||||
size_t *payload_len);
|
|
||||||
|
|
||||||
static char
|
|
||||||
MHD_websocket_encode_is_masked (struct MHD_WebSocketStream *ws);
|
|
||||||
static char
|
|
||||||
MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws,
|
|
||||||
size_t payload_len);
|
|
||||||
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_data (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
int fragmentation,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len,
|
|
||||||
char opcode);
|
|
||||||
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len,
|
|
||||||
char opcode);
|
|
||||||
|
|
||||||
static uint32_t
|
|
||||||
MHD_websocket_generate_mask (struct MHD_WebSocketStream *ws);
|
|
||||||
|
|
||||||
static uint16_t
|
|
||||||
MHD_htons (uint16_t value);
|
|
||||||
|
|
||||||
static uint64_t
|
|
||||||
MHD_htonll (uint64_t value);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the HTTP version is 1.1 or above.
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_check_http_version (const char *http_version)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == http_version)
|
|
||||||
{
|
|
||||||
/* Like with the other check routines, */
|
|
||||||
/* NULL is threated as "value not given" and not as parameter error */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether the version has a valid format */
|
|
||||||
/* RFC 1945 3.1: The format must be "HTTP/x.x" where x is */
|
|
||||||
/* any digit and must appear at least once */
|
|
||||||
if (('H' != http_version[0]) ||
|
|
||||||
('T' != http_version[1]) ||
|
|
||||||
('T' != http_version[2]) ||
|
|
||||||
('P' != http_version[3]) ||
|
|
||||||
('/' != http_version[4]))
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the major and minor part of the version */
|
|
||||||
/* RFC 1945 3.1: Both numbers must be threated as separate integers. */
|
|
||||||
/* Leading zeros must be ignored and both integers may have multiple digits */
|
|
||||||
const char *major = NULL;
|
|
||||||
const char *dot = NULL;
|
|
||||||
size_t i = 5;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
char c = http_version[i];
|
|
||||||
if (('0' <= c) && ('9' >= c))
|
|
||||||
{
|
|
||||||
if ((NULL == major) ||
|
|
||||||
((http_version + i == major + 1) && ('0' == *major)) )
|
|
||||||
{
|
|
||||||
major = http_version + i;
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
else if ('.' == http_version[i])
|
|
||||||
{
|
|
||||||
dot = http_version + i;
|
|
||||||
++i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const char *minor = NULL;
|
|
||||||
const char *end = NULL;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
char c = http_version[i];
|
|
||||||
if (('0' <= c) && ('9' >= c))
|
|
||||||
{
|
|
||||||
if ((NULL == minor) ||
|
|
||||||
((http_version + i == minor + 1) && ('0' == *minor)) )
|
|
||||||
{
|
|
||||||
minor = http_version + i;
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
else if (0 == c)
|
|
||||||
{
|
|
||||||
end = http_version + i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((NULL == major) || (NULL == dot) || (NULL == minor) || (NULL == end))
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
if ((2 <= dot - major) || ('2' <= *major) ||
|
|
||||||
(('1' == *major) && ((2 <= end - minor) || ('1' <= *minor))) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the "Connection" request header has the 'Upgrade' token.
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_check_connection_header (const char *connection_header)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == connection_header)
|
|
||||||
{
|
|
||||||
/* To be compatible with the return value */
|
|
||||||
/* of MHD_lookup_connection_value, */
|
|
||||||
/* NULL is threated as "value not given" and not as parameter error */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether the Connection includes an Upgrade token */
|
|
||||||
/* RFC 7230 6.1: Multiple tokens may appear. */
|
|
||||||
/* RFC 7230 3.2.6: Tokens are comma separated */
|
|
||||||
const char *token_start = NULL;
|
|
||||||
const char *token_end = NULL;
|
|
||||||
for (size_t i = 0; ; ++i)
|
|
||||||
{
|
|
||||||
char c = connection_header[i];
|
|
||||||
|
|
||||||
/* RFC 7230 3.2.6: The list of allowed characters is a token is: */
|
|
||||||
/* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
|
|
||||||
/* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
|
|
||||||
/* DIGIT / ALPHA */
|
|
||||||
if (('!' == c) || ('#' == c) || ('$' == c) || ('%' == c) ||
|
|
||||||
('&' == c) || ('\'' == c) || ('*' == c) ||
|
|
||||||
('+' == c) || ('-' == c) || ('.' == c) || ('^' == c) ||
|
|
||||||
('_' == c) || ('`' == c) || ('|' == c) || ('~' == c) ||
|
|
||||||
(('0' <= c) && ('9' >= c)) ||
|
|
||||||
(('A' <= c) && ('Z' >= c)) || (('a' <= c) && ('z' >= c)) )
|
|
||||||
{
|
|
||||||
/* This is a valid token character */
|
|
||||||
if (NULL == token_start)
|
|
||||||
{
|
|
||||||
token_start = connection_header + i;
|
|
||||||
}
|
|
||||||
token_end = connection_header + i + 1;
|
|
||||||
}
|
|
||||||
else if ((' ' == c) || ('\t' == c))
|
|
||||||
{
|
|
||||||
/* White-spaces around tokens will be ignored */
|
|
||||||
}
|
|
||||||
else if ((',' == c) || (0 == c))
|
|
||||||
{
|
|
||||||
/* Check the token (case-insensitive) */
|
|
||||||
if (NULL != token_start)
|
|
||||||
{
|
|
||||||
if (7 == (token_end - token_start) )
|
|
||||||
{
|
|
||||||
if ( (('U' == token_start[0]) || ('u' == token_start[0])) &&
|
|
||||||
(('P' == token_start[1]) || ('p' == token_start[1])) &&
|
|
||||||
(('G' == token_start[2]) || ('g' == token_start[2])) &&
|
|
||||||
(('R' == token_start[3]) || ('r' == token_start[3])) &&
|
|
||||||
(('A' == token_start[4]) || ('a' == token_start[4])) &&
|
|
||||||
(('D' == token_start[5]) || ('d' == token_start[5])) &&
|
|
||||||
(('E' == token_start[6]) || ('e' == token_start[6])) )
|
|
||||||
{
|
|
||||||
/* The token equals to "Upgrade" */
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (0 == c)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
token_start = NULL;
|
|
||||||
token_end = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 7230 3.2.6: Other characters are not allowed */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the "Upgrade" request header has the "websocket" keyword.
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_check_upgrade_header (const char *upgrade_header)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == upgrade_header)
|
|
||||||
{
|
|
||||||
/* To be compatible with the return value */
|
|
||||||
/* of MHD_lookup_connection_value, */
|
|
||||||
/* NULL is threated as "value not given" and not as parameter error */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether the Connection includes an Upgrade token */
|
|
||||||
/* RFC 7230 6.1: Multiple tokens may appear. */
|
|
||||||
/* RFC 7230 3.2.6: Tokens are comma separated */
|
|
||||||
const char *keyword_start = NULL;
|
|
||||||
const char *keyword_end = NULL;
|
|
||||||
for (size_t i = 0; ; ++i)
|
|
||||||
{
|
|
||||||
char c = upgrade_header[i];
|
|
||||||
|
|
||||||
/* RFC 7230 3.2.6: The list of allowed characters is a token is: */
|
|
||||||
/* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
|
|
||||||
/* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
|
|
||||||
/* DIGIT / ALPHA */
|
|
||||||
/* We also allow "/" here as the sub-delimiter for the protocol version */
|
|
||||||
if (('!' == c) || ('#' == c) || ('$' == c) || ('%' == c) ||
|
|
||||||
('&' == c) || ('\'' == c) || ('*' == c) ||
|
|
||||||
('+' == c) || ('-' == c) || ('.' == c) || ('^' == c) ||
|
|
||||||
('_' == c) || ('`' == c) || ('|' == c) || ('~' == c) ||
|
|
||||||
('/' == c) ||
|
|
||||||
(('0' <= c) && ('9' >= c)) ||
|
|
||||||
(('A' <= c) && ('Z' >= c)) || (('a' <= c) && ('z' >= c)) )
|
|
||||||
{
|
|
||||||
/* This is a valid token character */
|
|
||||||
if (NULL == keyword_start)
|
|
||||||
{
|
|
||||||
keyword_start = upgrade_header + i;
|
|
||||||
}
|
|
||||||
keyword_end = upgrade_header + i + 1;
|
|
||||||
}
|
|
||||||
else if ((' ' == c) || ('\t' == c))
|
|
||||||
{
|
|
||||||
/* White-spaces around tokens will be ignored */
|
|
||||||
}
|
|
||||||
else if ((',' == c) || (0 == c))
|
|
||||||
{
|
|
||||||
/* Check the token (case-insensitive) */
|
|
||||||
if (NULL != keyword_start)
|
|
||||||
{
|
|
||||||
if (9 == (keyword_end - keyword_start) )
|
|
||||||
{
|
|
||||||
if ( (('W' == keyword_start[0]) || ('w' == keyword_start[0])) &&
|
|
||||||
(('E' == keyword_start[1]) || ('e' == keyword_start[1])) &&
|
|
||||||
(('B' == keyword_start[2]) || ('b' == keyword_start[2])) &&
|
|
||||||
(('S' == keyword_start[3]) || ('s' == keyword_start[3])) &&
|
|
||||||
(('O' == keyword_start[4]) || ('o' == keyword_start[4])) &&
|
|
||||||
(('C' == keyword_start[5]) || ('c' == keyword_start[5])) &&
|
|
||||||
(('K' == keyword_start[6]) || ('k' == keyword_start[6])) &&
|
|
||||||
(('E' == keyword_start[7]) || ('e' == keyword_start[7])) &&
|
|
||||||
(('T' == keyword_start[8]) || ('t' == keyword_start[8])) )
|
|
||||||
{
|
|
||||||
/* The keyword equals to "websocket" */
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (0 == c)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
keyword_start = NULL;
|
|
||||||
keyword_end = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 7230 3.2.6: Other characters are not allowed */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the "Sec-WebSocket-Version" request header
|
|
||||||
* equals to "13"
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_check_version_header (const char *version_header)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == version_header)
|
|
||||||
{
|
|
||||||
/* To be compatible with the return value */
|
|
||||||
/* of MHD_lookup_connection_value, */
|
|
||||||
/* NULL is threated as "value not given" and not as parameter error */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (('1' == version_header[0]) &&
|
|
||||||
('3' == version_header[1]) &&
|
|
||||||
(0 == version_header[2]))
|
|
||||||
{
|
|
||||||
/* The version equals to "13" */
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the response for the Sec-WebSocket-Accept header
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_create_accept_header (const char *sec_websocket_key,
|
|
||||||
char *sec_websocket_accept)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != sec_websocket_accept)
|
|
||||||
*sec_websocket_accept = 0;
|
|
||||||
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == sec_websocket_accept)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
if (NULL == sec_websocket_key)
|
|
||||||
{
|
|
||||||
/* NULL is not a parameter error, */
|
|
||||||
/* because MHD_lookup_connection_value returns NULL */
|
|
||||||
/* if the header wasn't found */
|
|
||||||
return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* build SHA1 hash of the given key and the UUID appended */
|
|
||||||
char sha1[20];
|
|
||||||
const char *suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
||||||
int length = (int) strlen (sec_websocket_key);
|
|
||||||
struct sha1_ctx ctx;
|
|
||||||
MHD_SHA1_init (&ctx);
|
|
||||||
MHD_SHA1_update (&ctx, (const uint8_t *) sec_websocket_key, length);
|
|
||||||
MHD_SHA1_update (&ctx, (const uint8_t *) suffix, 36);
|
|
||||||
MHD_SHA1_finish (&ctx, (uint8_t *) sha1);
|
|
||||||
|
|
||||||
/* base64 encode that SHA1 hash */
|
|
||||||
/* (simple algorithm here; SHA1 has always 20 bytes, */
|
|
||||||
/* which will always result in a 28 bytes base64 hash) */
|
|
||||||
const char *base64_encoding_table =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
for (int i = 0, j = 0; i < 20;)
|
|
||||||
{
|
|
||||||
uint32_t octet_a = i < 20 ? (unsigned char) sha1[i++] : 0;
|
|
||||||
uint32_t octet_b = i < 20 ? (unsigned char) sha1[i++] : 0;
|
|
||||||
uint32_t octet_c = i < 20 ? (unsigned char) sha1[i++] : 0;
|
|
||||||
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
|
|
||||||
|
|
||||||
sec_websocket_accept[j++] = base64_encoding_table[(triple >> 3 * 6) & 0x3F];
|
|
||||||
sec_websocket_accept[j++] = base64_encoding_table[(triple >> 2 * 6) & 0x3F];
|
|
||||||
sec_websocket_accept[j++] = base64_encoding_table[(triple >> 1 * 6) & 0x3F];
|
|
||||||
sec_websocket_accept[j++] = base64_encoding_table[(triple >> 0 * 6) & 0x3F];
|
|
||||||
|
|
||||||
}
|
|
||||||
sec_websocket_accept[27] = '=';
|
|
||||||
sec_websocket_accept[28] = 0;
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new websocket stream
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_stream_init (struct MHD_WebSocketStream **ws,
|
|
||||||
int flags,
|
|
||||||
size_t max_payload_size)
|
|
||||||
{
|
|
||||||
return MHD_websocket_stream_init2 (ws,
|
|
||||||
flags,
|
|
||||||
max_payload_size,
|
|
||||||
malloc,
|
|
||||||
realloc,
|
|
||||||
free,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new websocket stream with
|
|
||||||
* additional parameters for allocation functions
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_stream_init2 (struct MHD_WebSocketStream **ws,
|
|
||||||
int flags,
|
|
||||||
size_t max_payload_size,
|
|
||||||
MHD_WebSocketMallocCallback callback_malloc,
|
|
||||||
MHD_WebSocketReallocCallback callback_realloc,
|
|
||||||
MHD_WebSocketFreeCallback callback_free,
|
|
||||||
void *cls_rng,
|
|
||||||
MHD_WebSocketRandomNumberGenerator callback_rng)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != ws)
|
|
||||||
*ws = NULL;
|
|
||||||
|
|
||||||
/* validate parameters */
|
|
||||||
if ((NULL == ws) ||
|
|
||||||
(0 != (flags & ~MHD_WEBSOCKET_FLAG_MASK_ALL)) ||
|
|
||||||
((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) ||
|
|
||||||
(NULL == callback_malloc) ||
|
|
||||||
(NULL == callback_realloc) ||
|
|
||||||
(NULL == callback_free) ||
|
|
||||||
((0 != (flags & MHD_WEBSOCKET_FLAG_CLIENT)) &&
|
|
||||||
(NULL == callback_rng)))
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate stream */
|
|
||||||
struct MHD_WebSocketStream *ws_ = (struct MHD_WebSocketStream *) malloc (
|
|
||||||
sizeof (struct MHD_WebSocketStream));
|
|
||||||
if (NULL == ws_)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
|
|
||||||
/* initialize stream */
|
|
||||||
memset (ws_, 0, sizeof (struct MHD_WebSocketStream));
|
|
||||||
ws_->flags = flags;
|
|
||||||
ws_->max_payload_size = max_payload_size;
|
|
||||||
ws_->malloc = callback_malloc;
|
|
||||||
ws_->realloc = callback_realloc;
|
|
||||||
ws_->free = callback_free;
|
|
||||||
ws_->cls_rng = cls_rng;
|
|
||||||
ws_->rng = callback_rng;
|
|
||||||
ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID;
|
|
||||||
|
|
||||||
/* return stream */
|
|
||||||
*ws = ws_;
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees a previously allocated websocket stream
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_stream_free (struct MHD_WebSocketStream *ws)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == ws)
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
|
|
||||||
/* free allocated payload data */
|
|
||||||
if (ws->data_payload)
|
|
||||||
ws->free (ws->data_payload);
|
|
||||||
if (ws->control_payload)
|
|
||||||
ws->free (ws->control_payload);
|
|
||||||
|
|
||||||
/* free the stream */
|
|
||||||
free (ws);
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates a websocket stream (no more decoding possible)
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_stream_invalidate (struct MHD_WebSocketStream *ws)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == ws)
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
|
|
||||||
/* invalidate stream */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a websocket stream is valid
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_VALIDITY
|
|
||||||
MHD_websocket_stream_is_valid (struct MHD_WebSocketStream *ws)
|
|
||||||
{
|
|
||||||
/* validate parameters */
|
|
||||||
if (NULL == ws)
|
|
||||||
return MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
|
|
||||||
return ws->validity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes incoming data to a websocket frame
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_decode (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *streambuf,
|
|
||||||
size_t streambuf_len,
|
|
||||||
size_t *streambuf_read_len,
|
|
||||||
char **payload,
|
|
||||||
size_t *payload_len)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != streambuf_read_len)
|
|
||||||
*streambuf_read_len = 0;
|
|
||||||
if (NULL != payload)
|
|
||||||
*payload = NULL;
|
|
||||||
if (NULL != payload_len)
|
|
||||||
*payload_len = 0;
|
|
||||||
|
|
||||||
/* validate parameters */
|
|
||||||
if ((NULL == ws) ||
|
|
||||||
((NULL == streambuf) && (0 != streambuf_len)) ||
|
|
||||||
(NULL == streambuf_read_len) ||
|
|
||||||
(NULL == payload) ||
|
|
||||||
(NULL == payload_len) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* validate stream validity */
|
|
||||||
if (MHD_WEBSOCKET_VALIDITY_INVALID == ws->validity)
|
|
||||||
return MHD_WEBSOCKET_STATUS_STREAM_BROKEN;
|
|
||||||
|
|
||||||
/* decode loop */
|
|
||||||
size_t current = 0;
|
|
||||||
while (current < streambuf_len)
|
|
||||||
{
|
|
||||||
switch (ws->decode_step)
|
|
||||||
{
|
|
||||||
/* start of frame */
|
|
||||||
case MHD_WebSocket_DecodeStep_Start:
|
|
||||||
{
|
|
||||||
/* The first byte contains the opcode, the fin flag and three reserved bits */
|
|
||||||
if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity)
|
|
||||||
{
|
|
||||||
char opcode = streambuf [current];
|
|
||||||
if (0 != (opcode & 0x70))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.2 RSV1-3: If a reserved flag is set */
|
|
||||||
/* (while it isn't specified by an extension) the communication must fail. */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
switch (opcode & 0x0F)
|
|
||||||
{
|
|
||||||
case MHD_WebSocket_Opcode_Continuation:
|
|
||||||
if (0 == ws->data_type)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.4: Continuation frame without previous data frame */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES ==
|
|
||||||
ws->validity)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.5.1: After a close frame has been sent, */
|
|
||||||
/* no data frames may be sent (so we don't accept data frames */
|
|
||||||
/* for decoding anymore) */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WebSocket_Opcode_Text:
|
|
||||||
case MHD_WebSocket_Opcode_Binary:
|
|
||||||
if (0 != ws->data_type)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.4: Continuation expected, but new data frame */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES ==
|
|
||||||
ws->validity)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.5.1: After a close frame has been sent, */
|
|
||||||
/* no data frames may be sent (so we don't accept data frames */
|
|
||||||
/* for decoding anymore) */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WebSocket_Opcode_Close:
|
|
||||||
case MHD_WebSocket_Opcode_Ping:
|
|
||||||
case MHD_WebSocket_Opcode_Pong:
|
|
||||||
if ((opcode & 0x80) == 0)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.4: Control frames may not be fragmented */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
if (MHD_WebSocket_Opcode_Close == (opcode & 0x0F))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.5.1: After a close frame has been sent, */
|
|
||||||
/* no data frames may be sent (so we don't accept data frames */
|
|
||||||
/* for decoding anymore) */
|
|
||||||
ws->validity =
|
|
||||||
MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* RFC 6455 5.2 OPCODE: Only six opcodes are specified. */
|
|
||||||
/* All other are invalid in version 13 of the protocol. */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Length1ofX;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WebSocket_DecodeStep_Length1ofX:
|
|
||||||
{
|
|
||||||
/* The second byte specifies whether the data is masked and the size */
|
|
||||||
/* (the client MUST mask the payload, the server MUST NOT mask the payload) */
|
|
||||||
char frame_len = streambuf [current];
|
|
||||||
char is_masked = (frame_len & 0x80);
|
|
||||||
frame_len &= 0x7f;
|
|
||||||
if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity)
|
|
||||||
{
|
|
||||||
if (0 != is_masked)
|
|
||||||
{
|
|
||||||
if (MHD_WEBSOCKET_FLAG_CLIENT == (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_CLIENT))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.1: All frames from the server must be unmasked */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (MHD_WEBSOCKET_FLAG_SERVER == (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_CLIENT))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.1: All frames from the client must be masked */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (126 <= frame_len)
|
|
||||||
{
|
|
||||||
if (0 != (ws->frame_header [0] & 0x08))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.5: Control frames may not have more payload than 125 bytes */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (1 == frame_len)
|
|
||||||
{
|
|
||||||
if (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0F))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.5.1: The close frame must have at least */
|
|
||||||
/* two bytes of payload if payload is used */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
|
|
||||||
|
|
||||||
if (126 == frame_len)
|
|
||||||
{
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Length1of2;
|
|
||||||
}
|
|
||||||
else if (127 == frame_len)
|
|
||||||
{
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Length1of8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t size = (size_t) frame_len;
|
|
||||||
if ((SIZE_MAX < size) ||
|
|
||||||
(ws->max_payload_size && (ws->max_payload_size < size)) )
|
|
||||||
{
|
|
||||||
/* RFC 6455 7.4.1 1009: If the message is too big to process, we may close the connection */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
}
|
|
||||||
ws->payload_size = size;
|
|
||||||
if (0 != is_masked)
|
|
||||||
{
|
|
||||||
/* with mask */
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* without mask */
|
|
||||||
*((uint32_t *) ws->mask_key) = 0;
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Payload size first byte of 2 bytes */
|
|
||||||
case MHD_WebSocket_DecodeStep_Length1of2:
|
|
||||||
/* Payload size first 7 bytes of 8 bytes */
|
|
||||||
case MHD_WebSocket_DecodeStep_Length1of8:
|
|
||||||
case MHD_WebSocket_DecodeStep_Length2of8:
|
|
||||||
case MHD_WebSocket_DecodeStep_Length3of8:
|
|
||||||
case MHD_WebSocket_DecodeStep_Length4of8:
|
|
||||||
case MHD_WebSocket_DecodeStep_Length5of8:
|
|
||||||
case MHD_WebSocket_DecodeStep_Length6of8:
|
|
||||||
case MHD_WebSocket_DecodeStep_Length7of8:
|
|
||||||
/* Mask first 3 bytes of 4 bytes */
|
|
||||||
case MHD_WebSocket_DecodeStep_Mask1Of4:
|
|
||||||
case MHD_WebSocket_DecodeStep_Mask2Of4:
|
|
||||||
case MHD_WebSocket_DecodeStep_Mask3Of4:
|
|
||||||
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
|
|
||||||
++ws->decode_step;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* 2 byte length finished */
|
|
||||||
case MHD_WebSocket_DecodeStep_Length2of2:
|
|
||||||
{
|
|
||||||
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
|
|
||||||
size_t size = (size_t) MHD_htons (
|
|
||||||
*((uint16_t *) &ws->frame_header [2]));
|
|
||||||
if (125 >= size)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.2 Payload length: The minimal number of bytes */
|
|
||||||
/* must be used for the length */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
if ((SIZE_MAX < size) ||
|
|
||||||
(ws->max_payload_size && (ws->max_payload_size < size)) )
|
|
||||||
{
|
|
||||||
/* RFC 6455 7.4.1 1009: If the message is too big to process, */
|
|
||||||
/* we may close the connection */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
}
|
|
||||||
ws->payload_size = size;
|
|
||||||
if (0 != (ws->frame_header [1] & 0x80))
|
|
||||||
{
|
|
||||||
/* with mask */
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* without mask */
|
|
||||||
*((uint32_t *) ws->mask_key) = 0;
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* 8 byte length finished */
|
|
||||||
case MHD_WebSocket_DecodeStep_Length8of8:
|
|
||||||
{
|
|
||||||
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
|
|
||||||
uint64_t size = MHD_htonll (*((uint64_t *) &ws->frame_header [2]));
|
|
||||||
if (0x7fffffffffffffff < size)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.2 frame-payload-length-63: The length may */
|
|
||||||
/* not exceed 0x7fffffffffffffff */
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream;
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
if (65535 >= size)
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.2 Payload length: The minimal number of bytes */
|
|
||||||
/* must be used for the length */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
}
|
|
||||||
if ((SIZE_MAX < size) ||
|
|
||||||
(ws->max_payload_size && (ws->max_payload_size < size)) )
|
|
||||||
{
|
|
||||||
/* RFC 6455 7.4.1 1009: If the message is too big to process, */
|
|
||||||
/* we may close the connection */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
}
|
|
||||||
ws->payload_size = (size_t) size;
|
|
||||||
|
|
||||||
if (0 != (ws->frame_header [1] & 0x80))
|
|
||||||
{
|
|
||||||
/* with mask */
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* without mask */
|
|
||||||
*((uint32_t *) ws->mask_key) = 0;
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* mask finished */
|
|
||||||
case MHD_WebSocket_DecodeStep_Mask4Of4:
|
|
||||||
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
|
|
||||||
*((uint32_t *) ws->mask_key) = *((uint32_t *) &ws->frame_header [ws->
|
|
||||||
frame_header_size
|
|
||||||
- 4]);
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* header finished */
|
|
||||||
case MHD_WebSocket_DecodeStep_HeaderCompleted:
|
|
||||||
/* return or assign either to data or control */
|
|
||||||
{
|
|
||||||
int ret = MHD_websocket_decode_header_complete (ws,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
if (MHD_WEBSOCKET_STATUS_OK != ret)
|
|
||||||
{
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* payload data */
|
|
||||||
case MHD_WebSocket_DecodeStep_PayloadOfDataFrame:
|
|
||||||
case MHD_WebSocket_DecodeStep_PayloadOfControlFrame:
|
|
||||||
{
|
|
||||||
size_t bytes_needed = ws->payload_size - ws->payload_index;
|
|
||||||
size_t bytes_remaining = streambuf_len - current;
|
|
||||||
size_t bytes_to_take = bytes_needed < bytes_remaining ? bytes_needed :
|
|
||||||
bytes_remaining;
|
|
||||||
if (0 != bytes_to_take)
|
|
||||||
{
|
|
||||||
size_t utf8_start = ws->payload_index;
|
|
||||||
char *decode_payload = ws->decode_step ==
|
|
||||||
MHD_WebSocket_DecodeStep_PayloadOfDataFrame ?
|
|
||||||
ws->data_payload_start :
|
|
||||||
ws->control_payload;
|
|
||||||
|
|
||||||
/* copy the new payload data (with unmasking if necessary */
|
|
||||||
MHD_websocket_copy_payload (decode_payload + ws->payload_index,
|
|
||||||
&streambuf [current],
|
|
||||||
bytes_to_take,
|
|
||||||
*((uint32_t *) ws->mask_key),
|
|
||||||
(unsigned long) (ws->payload_index
|
|
||||||
& 0x03));
|
|
||||||
current += bytes_to_take;
|
|
||||||
ws->payload_index += bytes_to_take;
|
|
||||||
if (((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
|
|
||||||
ws->decode_step) &&
|
|
||||||
(MHD_WebSocket_Opcode_Text == ws->data_type)) ||
|
|
||||||
((MHD_WebSocket_DecodeStep_PayloadOfControlFrame ==
|
|
||||||
ws->decode_step) &&
|
|
||||||
(MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0f)) &&
|
|
||||||
(2 < ws->payload_index)) )
|
|
||||||
{
|
|
||||||
/* RFC 6455 8.1: We need to check the UTF-8 validity */
|
|
||||||
int utf8_step;
|
|
||||||
char *decode_payload_utf8;
|
|
||||||
size_t bytes_to_check;
|
|
||||||
size_t utf8_error_offset = 0;
|
|
||||||
if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step)
|
|
||||||
{
|
|
||||||
utf8_step = ws->data_utf8_step;
|
|
||||||
decode_payload_utf8 = decode_payload + utf8_start;
|
|
||||||
bytes_to_check = bytes_to_take;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
utf8_step = ws->control_utf8_step;
|
|
||||||
if ((MHD_WebSocket_Opcode_Close == (ws->frame_header [0]
|
|
||||||
& 0x0f)) &&
|
|
||||||
(2 > utf8_start) )
|
|
||||||
{
|
|
||||||
/* The first two bytes of the close frame are binary content and */
|
|
||||||
/* must be skipped in the UTF-8 check */
|
|
||||||
utf8_start = 2;
|
|
||||||
utf8_error_offset = 2;
|
|
||||||
}
|
|
||||||
decode_payload_utf8 = decode_payload + utf8_start;
|
|
||||||
bytes_to_check = bytes_to_take - utf8_start;
|
|
||||||
}
|
|
||||||
size_t utf8_check_offset = 0;
|
|
||||||
int utf8_result = MHD_websocket_check_utf8 (decode_payload_utf8,
|
|
||||||
bytes_to_check,
|
|
||||||
&utf8_step,
|
|
||||||
&utf8_check_offset);
|
|
||||||
if (MHD_WebSocket_UTF8Result_Invalid != utf8_result)
|
|
||||||
{
|
|
||||||
/* memorize current validity check step to continue later */
|
|
||||||
ws->data_utf8_step = utf8_step;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 6455 8.1: We must fail on broken UTF-8 sequence */
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current - bytes_to_take
|
|
||||||
+ utf8_check_offset + utf8_error_offset;
|
|
||||||
return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ws->payload_size == ws->payload_index)
|
|
||||||
{
|
|
||||||
/* all payload data of the current frame has been received */
|
|
||||||
int ret = MHD_websocket_decode_payload_complete (ws,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
if (MHD_WEBSOCKET_STATUS_OK != ret)
|
|
||||||
{
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WebSocket_DecodeStep_BrokenStream:
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return MHD_WEBSOCKET_STATUS_STREAM_BROKEN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special treatment for zero payload length messages */
|
|
||||||
if (MHD_WebSocket_DecodeStep_HeaderCompleted == ws->decode_step)
|
|
||||||
{
|
|
||||||
int ret = MHD_websocket_decode_header_complete (ws,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
if (MHD_WEBSOCKET_STATUS_OK != ret)
|
|
||||||
{
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (ws->decode_step)
|
|
||||||
{
|
|
||||||
case MHD_WebSocket_DecodeStep_PayloadOfDataFrame:
|
|
||||||
case MHD_WebSocket_DecodeStep_PayloadOfControlFrame:
|
|
||||||
if (ws->payload_size == ws->payload_index)
|
|
||||||
{
|
|
||||||
/* all payload data of the current frame has been received */
|
|
||||||
int ret = MHD_websocket_decode_payload_complete (ws,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
if (MHD_WEBSOCKET_STATUS_OK != ret)
|
|
||||||
{
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*streambuf_read_len = current;
|
|
||||||
|
|
||||||
/* more data needed */
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_decode_header_complete (struct MHD_WebSocketStream *ws,
|
|
||||||
char **payload,
|
|
||||||
size_t *payload_len)
|
|
||||||
{
|
|
||||||
/* assign either to data or control */
|
|
||||||
char opcode = ws->frame_header [0] & 0x0f;
|
|
||||||
switch (opcode)
|
|
||||||
{
|
|
||||||
case MHD_WebSocket_Opcode_Continuation:
|
|
||||||
{
|
|
||||||
/* validate payload size */
|
|
||||||
size_t new_size_total = ws->payload_size + ws->data_payload_size;
|
|
||||||
if ((0 != ws->max_payload_size) && (ws->max_payload_size <
|
|
||||||
new_size_total) )
|
|
||||||
{
|
|
||||||
/* RFC 6455 7.4.1 1009: If the message is too big to process, */
|
|
||||||
/* we may close the connection */
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream;
|
|
||||||
ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID;
|
|
||||||
if (0 != (ws->flags
|
|
||||||
& MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR))
|
|
||||||
{
|
|
||||||
MHD_websocket_encode_close (ws,
|
|
||||||
MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
payload,
|
|
||||||
payload_len);
|
|
||||||
}
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
}
|
|
||||||
/* allocate buffer for continued data frame */
|
|
||||||
char *new_buf = NULL;
|
|
||||||
if (0 != new_size_total)
|
|
||||||
{
|
|
||||||
new_buf = ws->realloc (ws->data_payload, new_size_total + 1);
|
|
||||||
if (NULL == new_buf)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
new_buf [new_size_total] = 0;
|
|
||||||
ws->data_payload_start = &new_buf[ws->data_payload_size];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ws->data_payload_start = new_buf;
|
|
||||||
}
|
|
||||||
ws->data_payload = new_buf;
|
|
||||||
ws->data_payload_size = new_size_total;
|
|
||||||
}
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WebSocket_Opcode_Text:
|
|
||||||
case MHD_WebSocket_Opcode_Binary:
|
|
||||||
/* allocate buffer for data frame */
|
|
||||||
{
|
|
||||||
size_t new_size_total = ws->payload_size;
|
|
||||||
char *new_buf = NULL;
|
|
||||||
if (0 != new_size_total)
|
|
||||||
{
|
|
||||||
new_buf = ws->malloc (new_size_total + 1);
|
|
||||||
if (NULL == new_buf)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
new_buf [new_size_total] = 0;
|
|
||||||
}
|
|
||||||
ws->data_payload = new_buf;
|
|
||||||
ws->data_payload_start = new_buf;
|
|
||||||
ws->data_payload_size = new_size_total;
|
|
||||||
ws->data_type = opcode;
|
|
||||||
}
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WebSocket_Opcode_Close:
|
|
||||||
case MHD_WebSocket_Opcode_Ping:
|
|
||||||
case MHD_WebSocket_Opcode_Pong:
|
|
||||||
/* allocate buffer for control frame */
|
|
||||||
{
|
|
||||||
size_t new_size_total = ws->payload_size;
|
|
||||||
char *new_buf = NULL;
|
|
||||||
if (0 != new_size_total)
|
|
||||||
{
|
|
||||||
new_buf = ws->malloc (new_size_total + 1);
|
|
||||||
if (NULL == new_buf)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
new_buf[new_size_total] = 0;
|
|
||||||
}
|
|
||||||
ws->control_payload = new_buf;
|
|
||||||
}
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfControlFrame;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream *ws,
|
|
||||||
char **payload,
|
|
||||||
size_t *payload_len)
|
|
||||||
{
|
|
||||||
/* all payload data of the current frame has been received */
|
|
||||||
char is_continue = MHD_WebSocket_Opcode_Continuation ==
|
|
||||||
(ws->frame_header [0] & 0x0F);
|
|
||||||
char is_fin = ws->frame_header [0] & 0x80;
|
|
||||||
if (0 != is_fin)
|
|
||||||
{
|
|
||||||
/* the frame is complete */
|
|
||||||
if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step)
|
|
||||||
{
|
|
||||||
/* data frame */
|
|
||||||
char data_type = ws->data_type;
|
|
||||||
if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) &&
|
|
||||||
(0 != is_continue))
|
|
||||||
{
|
|
||||||
data_type |= 0x40; /* mark as last fragment */
|
|
||||||
}
|
|
||||||
*payload = ws->data_payload;
|
|
||||||
*payload_len = ws->data_payload_size;
|
|
||||||
ws->data_payload = 0;
|
|
||||||
ws->data_payload_start = 0;
|
|
||||||
ws->data_payload_size = 0;
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
|
|
||||||
ws->payload_index = 0;
|
|
||||||
ws->data_type = 0;
|
|
||||||
ws->frame_header_size = 0;
|
|
||||||
return data_type;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* control frame */
|
|
||||||
*payload = ws->control_payload;
|
|
||||||
*payload_len = ws->payload_size;
|
|
||||||
ws->control_payload = 0;
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
|
|
||||||
ws->payload_index = 0;
|
|
||||||
ws->frame_header_size = 0;
|
|
||||||
return (ws->frame_header [0] & 0x0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS))
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.4: To allow streaming, the user can choose */
|
|
||||||
/* to return fragments */
|
|
||||||
if ((MHD_WebSocket_Opcode_Text == ws->data_type) &&
|
|
||||||
(MHD_WEBSOCKET_UTF8STEP_NORMAL != ws->data_utf8_step) )
|
|
||||||
{
|
|
||||||
/* the last UTF-8 sequence is incomplete, so we keep the start of
|
|
||||||
that and only return the part before */
|
|
||||||
size_t given_utf8 = 0;
|
|
||||||
switch (ws->data_utf8_step)
|
|
||||||
{
|
|
||||||
/* one byte given */
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3:
|
|
||||||
given_utf8 = 1;
|
|
||||||
break;
|
|
||||||
/* two bytes given */
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2:
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3:
|
|
||||||
given_utf8 = 2;
|
|
||||||
break;
|
|
||||||
/* three bytes given */
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3:
|
|
||||||
given_utf8 = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
size_t new_len = ws->data_payload_size - given_utf8;
|
|
||||||
if (0 != new_len)
|
|
||||||
{
|
|
||||||
char *next_payload = ws->malloc (given_utf8 + 1);
|
|
||||||
if (NULL == next_payload)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
}
|
|
||||||
memcpy (next_payload,
|
|
||||||
ws->data_payload_start + ws->payload_index - given_utf8,
|
|
||||||
given_utf8);
|
|
||||||
next_payload[given_utf8] = 0;
|
|
||||||
|
|
||||||
ws->data_payload[new_len] = 0;
|
|
||||||
*payload = ws->data_payload;
|
|
||||||
*payload_len = new_len;
|
|
||||||
ws->data_payload = next_payload;
|
|
||||||
ws->data_payload_size = given_utf8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*payload = NULL;
|
|
||||||
*payload_len = 0;
|
|
||||||
}
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
|
|
||||||
ws->payload_index = 0;
|
|
||||||
ws->frame_header_size = 0;
|
|
||||||
if (0 != is_continue)
|
|
||||||
return ws->data_type | 0x20; /* mark as middle fragment */
|
|
||||||
else
|
|
||||||
return ws->data_type | 0x10; /* mark as first fragment */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* we simply pass the entire data frame */
|
|
||||||
*payload = ws->data_payload;
|
|
||||||
*payload_len = ws->data_payload_size;
|
|
||||||
ws->data_payload = 0;
|
|
||||||
ws->data_payload_start = 0;
|
|
||||||
ws->data_payload_size = 0;
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
|
|
||||||
ws->payload_index = 0;
|
|
||||||
ws->frame_header_size = 0;
|
|
||||||
if (0 != is_continue)
|
|
||||||
return ws->data_type | 0x20; /* mark as middle fragment */
|
|
||||||
else
|
|
||||||
return ws->data_type | 0x10; /* mark as first fragment */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 6455 5.4: We must await a continuation frame to get */
|
|
||||||
/* the remainder of this data frame */
|
|
||||||
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
|
|
||||||
ws->frame_header_size = 0;
|
|
||||||
ws->payload_index = 0;
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits the received close reason
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_split_close_reason (const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
unsigned short *reason_code,
|
|
||||||
const char **reason_utf8,
|
|
||||||
size_t *reason_utf8_len)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != reason_code)
|
|
||||||
*reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON;
|
|
||||||
if (NULL != reason_utf8)
|
|
||||||
*reason_utf8 = NULL;
|
|
||||||
if (NULL != reason_utf8_len)
|
|
||||||
*reason_utf8_len = 0;
|
|
||||||
|
|
||||||
/* validate parameters */
|
|
||||||
if ((NULL == payload) && (0 != payload_len))
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
if (1 == payload_len)
|
|
||||||
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
|
|
||||||
if (125 < payload_len)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
|
|
||||||
/* decode reason code */
|
|
||||||
if (2 > payload_len)
|
|
||||||
{
|
|
||||||
if (NULL != reason_code)
|
|
||||||
*reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (NULL != reason_code)
|
|
||||||
*reason_code = MHD_htons (*((uint16_t *) payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* decode reason text */
|
|
||||||
if (2 >= payload_len)
|
|
||||||
{
|
|
||||||
if (NULL != reason_utf8)
|
|
||||||
*reason_utf8 = NULL;
|
|
||||||
if (NULL != reason_utf8_len)
|
|
||||||
*reason_utf8_len = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (NULL != reason_utf8)
|
|
||||||
*reason_utf8 = payload + 2;
|
|
||||||
if (NULL != reason_utf8_len)
|
|
||||||
*reason_utf8_len = payload_len - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes a text into a websocket text frame
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_text (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload_utf8,
|
|
||||||
size_t payload_utf8_len,
|
|
||||||
int fragmentation,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len,
|
|
||||||
int *utf8_step)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != frame)
|
|
||||||
*frame = NULL;
|
|
||||||
if (NULL != frame_len)
|
|
||||||
*frame_len = 0;
|
|
||||||
if ((NULL != utf8_step) &&
|
|
||||||
((MHD_WEBSOCKET_FRAGMENTATION_FIRST == fragmentation) ||
|
|
||||||
(MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) ))
|
|
||||||
{
|
|
||||||
/* the old UTF-8 step will be ignored for new fragments */
|
|
||||||
*utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* validate parameters */
|
|
||||||
if ((NULL == ws) ||
|
|
||||||
((0 != payload_utf8_len) && (NULL == payload_utf8)) ||
|
|
||||||
(NULL == frame) ||
|
|
||||||
(NULL == frame_len) ||
|
|
||||||
(MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
|
|
||||||
(MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) ||
|
|
||||||
((MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) &&
|
|
||||||
(NULL == utf8_step)) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check max length */
|
|
||||||
if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_utf8_len)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check UTF-8 */
|
|
||||||
int utf8_result = MHD_websocket_check_utf8 (payload_utf8,
|
|
||||||
payload_utf8_len,
|
|
||||||
utf8_step,
|
|
||||||
NULL);
|
|
||||||
if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) ||
|
|
||||||
((MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
|
|
||||||
(MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation)) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* encode data */
|
|
||||||
return MHD_websocket_encode_data (ws,
|
|
||||||
payload_utf8,
|
|
||||||
payload_utf8_len,
|
|
||||||
fragmentation,
|
|
||||||
frame,
|
|
||||||
frame_len,
|
|
||||||
MHD_WebSocket_Opcode_Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes binary data into a websocket binary frame
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_binary (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
int fragmentation,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != frame)
|
|
||||||
*frame = NULL;
|
|
||||||
if (NULL != frame_len)
|
|
||||||
*frame_len = 0;
|
|
||||||
|
|
||||||
/* validate parameters */
|
|
||||||
if ((NULL == ws) ||
|
|
||||||
((0 != payload_len) && (NULL == payload)) ||
|
|
||||||
(NULL == frame) ||
|
|
||||||
(NULL == frame_len) ||
|
|
||||||
(MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
|
|
||||||
(MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check max length */
|
|
||||||
if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_len)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_websocket_encode_data (ws,
|
|
||||||
payload,
|
|
||||||
payload_len,
|
|
||||||
fragmentation,
|
|
||||||
frame,
|
|
||||||
frame_len,
|
|
||||||
MHD_WebSocket_Opcode_Binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal function for encoding text/binary data into a websocket frame
|
|
||||||
*/
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_data (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
int fragmentation,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len,
|
|
||||||
char opcode)
|
|
||||||
{
|
|
||||||
/* calculate length and masking */
|
|
||||||
char is_masked = MHD_websocket_encode_is_masked (ws);
|
|
||||||
size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
|
|
||||||
size_t total_len = overhead_len + payload_len;
|
|
||||||
uint32_t mask = 0 != is_masked ? MHD_websocket_generate_mask (ws) : 0;
|
|
||||||
|
|
||||||
/* allocate memory */
|
|
||||||
char *result = ws->malloc (total_len + 1);
|
|
||||||
if (NULL == result)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
result [total_len] = 0;
|
|
||||||
*frame = result;
|
|
||||||
*frame_len = total_len;
|
|
||||||
|
|
||||||
/* add the opcode */
|
|
||||||
switch (fragmentation)
|
|
||||||
{
|
|
||||||
case MHD_WEBSOCKET_FRAGMENTATION_NONE:
|
|
||||||
*(result++) = 0x80 | opcode;
|
|
||||||
break;
|
|
||||||
case MHD_WEBSOCKET_FRAGMENTATION_FIRST:
|
|
||||||
*(result++) = opcode;
|
|
||||||
break;
|
|
||||||
case MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING:
|
|
||||||
*(result++) = MHD_WebSocket_Opcode_Continuation;
|
|
||||||
break;
|
|
||||||
case MHD_WEBSOCKET_FRAGMENTATION_LAST:
|
|
||||||
*(result++) = 0x80 | MHD_WebSocket_Opcode_Continuation;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the length */
|
|
||||||
if (126 > payload_len)
|
|
||||||
{
|
|
||||||
*(result++) = is_masked | (char) payload_len;
|
|
||||||
}
|
|
||||||
else if (65536 > payload_len)
|
|
||||||
{
|
|
||||||
*(result++) = is_masked | 126;
|
|
||||||
*((uint16_t *) result) = MHD_htons ((uint16_t) payload_len);
|
|
||||||
result += 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*(result++) = is_masked | 127;
|
|
||||||
*((uint64_t *) result) = MHD_htonll ((uint64_t) payload_len);
|
|
||||||
result += 8;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the mask */
|
|
||||||
if (0 != is_masked)
|
|
||||||
{
|
|
||||||
*(result++) = ((char *) &mask)[0];
|
|
||||||
*(result++) = ((char *) &mask)[1];
|
|
||||||
*(result++) = ((char *) &mask)[2];
|
|
||||||
*(result++) = ((char *) &mask)[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the payload */
|
|
||||||
if (0 != payload_len)
|
|
||||||
{
|
|
||||||
MHD_websocket_copy_payload (result,
|
|
||||||
payload,
|
|
||||||
payload_len,
|
|
||||||
mask,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes a websocket ping frame
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_ping (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len)
|
|
||||||
{
|
|
||||||
/* encode the ping frame */
|
|
||||||
return MHD_websocket_encode_ping_pong (ws,
|
|
||||||
payload,
|
|
||||||
payload_len,
|
|
||||||
frame,
|
|
||||||
frame_len,
|
|
||||||
MHD_WebSocket_Opcode_Ping);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes a websocket pong frame
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_pong (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len)
|
|
||||||
{
|
|
||||||
/* encode the pong frame */
|
|
||||||
return MHD_websocket_encode_ping_pong (ws,
|
|
||||||
payload,
|
|
||||||
payload_len,
|
|
||||||
frame,
|
|
||||||
frame_len,
|
|
||||||
MHD_WebSocket_Opcode_Pong);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal function for encoding ping/pong frames
|
|
||||||
*/
|
|
||||||
static enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream *ws,
|
|
||||||
const char *payload,
|
|
||||||
size_t payload_len,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len,
|
|
||||||
char opcode)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != frame)
|
|
||||||
*frame = NULL;
|
|
||||||
if (NULL != frame_len)
|
|
||||||
*frame_len = 0;
|
|
||||||
|
|
||||||
/* validate the parameters */
|
|
||||||
if ((NULL == ws) ||
|
|
||||||
((0 != payload_len) && (NULL == payload)) ||
|
|
||||||
(NULL == frame) ||
|
|
||||||
(NULL == frame_len) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload data */
|
|
||||||
if (125 < payload_len)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
|
|
||||||
/* calculate length and masking */
|
|
||||||
char is_masked = MHD_websocket_encode_is_masked (ws);
|
|
||||||
size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
|
|
||||||
size_t total_len = overhead_len + payload_len;
|
|
||||||
uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
|
|
||||||
|
|
||||||
/* allocate memory */
|
|
||||||
char *result = ws->malloc (total_len + 1);
|
|
||||||
if (NULL == result)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
result [total_len] = 0;
|
|
||||||
*frame = result;
|
|
||||||
*frame_len = total_len;
|
|
||||||
|
|
||||||
/* add the opcode */
|
|
||||||
*(result++) = 0x80 | opcode;
|
|
||||||
|
|
||||||
/* add the length */
|
|
||||||
*(result++) = is_masked | (char) payload_len;
|
|
||||||
|
|
||||||
/* add the mask */
|
|
||||||
if (0 != is_masked)
|
|
||||||
{
|
|
||||||
*(result++) = ((char *) &mask)[0];
|
|
||||||
*(result++) = ((char *) &mask)[1];
|
|
||||||
*(result++) = ((char *) &mask)[2];
|
|
||||||
*(result++) = ((char *) &mask)[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the payload */
|
|
||||||
if (0 != payload_len)
|
|
||||||
{
|
|
||||||
MHD_websocket_copy_payload (result,
|
|
||||||
payload,
|
|
||||||
payload_len,
|
|
||||||
mask,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes a websocket close frame
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
|
|
||||||
MHD_websocket_encode_close (struct MHD_WebSocketStream *ws,
|
|
||||||
unsigned short reason_code,
|
|
||||||
const char *reason_utf8,
|
|
||||||
size_t reason_utf8_len,
|
|
||||||
char **frame,
|
|
||||||
size_t *frame_len)
|
|
||||||
{
|
|
||||||
/* initialize output variables for errors cases */
|
|
||||||
if (NULL != frame)
|
|
||||||
*frame = NULL;
|
|
||||||
if (NULL != frame_len)
|
|
||||||
*frame_len = 0;
|
|
||||||
|
|
||||||
/* validate the parameters */
|
|
||||||
if ((NULL == ws) ||
|
|
||||||
((0 != reason_utf8_len) && (NULL == reason_utf8)) ||
|
|
||||||
(NULL == frame) ||
|
|
||||||
(NULL == frame_len) ||
|
|
||||||
((MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) &&
|
|
||||||
(1000 > reason_code)) ||
|
|
||||||
((0 != reason_utf8_len) &&
|
|
||||||
(MHD_WEBSOCKET_CLOSEREASON_NO_REASON == reason_code)) )
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload data, */
|
|
||||||
/* but in this case only 123 bytes, because 2 bytes are reserved */
|
|
||||||
/* for the close reason code. */
|
|
||||||
if (123 < reason_utf8_len)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED;
|
|
||||||
|
|
||||||
/* RFC 6455 5.5.1: If close payload data is given, it must be valid UTF-8 */
|
|
||||||
if (0 != reason_utf8_len)
|
|
||||||
{
|
|
||||||
int utf8_result = MHD_websocket_check_utf8 (reason_utf8,
|
|
||||||
reason_utf8_len,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
if (MHD_WebSocket_UTF8Result_Valid != utf8_result)
|
|
||||||
return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate length and masking */
|
|
||||||
char is_masked = MHD_websocket_encode_is_masked (ws);
|
|
||||||
size_t payload_len = (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code ?
|
|
||||||
2 + reason_utf8_len : 0);
|
|
||||||
size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len);
|
|
||||||
size_t total_len = overhead_len + payload_len;
|
|
||||||
uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
|
|
||||||
|
|
||||||
/* allocate memory */
|
|
||||||
char *result = ws->malloc (total_len + 1);
|
|
||||||
if (NULL == result)
|
|
||||||
return MHD_WEBSOCKET_STATUS_MEMORY_ERROR;
|
|
||||||
result [total_len] = 0;
|
|
||||||
*frame = result;
|
|
||||||
*frame_len = total_len;
|
|
||||||
|
|
||||||
/* add the opcode */
|
|
||||||
*(result++) = 0x88;
|
|
||||||
|
|
||||||
/* add the length */
|
|
||||||
*(result++) = is_masked | (char) payload_len;
|
|
||||||
|
|
||||||
/* add the mask */
|
|
||||||
if (0 != is_masked)
|
|
||||||
{
|
|
||||||
*(result++) = ((char *) &mask)[0];
|
|
||||||
*(result++) = ((char *) &mask)[1];
|
|
||||||
*(result++) = ((char *) &mask)[2];
|
|
||||||
*(result++) = ((char *) &mask)[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the payload */
|
|
||||||
if (0 != reason_code)
|
|
||||||
{
|
|
||||||
/* close reason code */
|
|
||||||
uint16_t reason_code_nb = MHD_htons (reason_code);
|
|
||||||
MHD_websocket_copy_payload (result,
|
|
||||||
(const char *) &reason_code_nb,
|
|
||||||
2,
|
|
||||||
mask,
|
|
||||||
0);
|
|
||||||
result += 2;
|
|
||||||
|
|
||||||
/* custom reason payload */
|
|
||||||
if (0 != reason_utf8_len)
|
|
||||||
{
|
|
||||||
MHD_websocket_copy_payload (result,
|
|
||||||
reason_utf8,
|
|
||||||
reason_utf8_len,
|
|
||||||
mask,
|
|
||||||
2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the 0x80 prefix for masked data, 0x00 otherwise
|
|
||||||
*/
|
|
||||||
static char
|
|
||||||
MHD_websocket_encode_is_masked (struct MHD_WebSocketStream *ws)
|
|
||||||
{
|
|
||||||
return (ws->flags & MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT) ==
|
|
||||||
MHD_WEBSOCKET_FLAG_CLIENT ? 0x80 : 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the size of the overhead in bytes
|
|
||||||
*/
|
|
||||||
static char
|
|
||||||
MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws,
|
|
||||||
size_t payload_len)
|
|
||||||
{
|
|
||||||
return 2 + (MHD_websocket_encode_is_masked (ws) != 0 ? 4 : 0) + (125 <
|
|
||||||
payload_len ?
|
|
||||||
(65535 <
|
|
||||||
payload_len
|
|
||||||
? 8 : 2) : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the payload to the destination (using mask)
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
MHD_websocket_copy_payload (char *dst,
|
|
||||||
const char *src,
|
|
||||||
size_t len,
|
|
||||||
uint32_t mask,
|
|
||||||
unsigned long mask_offset)
|
|
||||||
{
|
|
||||||
if (0 != len)
|
|
||||||
{
|
|
||||||
if (0 == mask)
|
|
||||||
{
|
|
||||||
/* when the mask is zero, we can just copy the data */
|
|
||||||
memcpy (dst, src, len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* mask is used */
|
|
||||||
char mask_[4];
|
|
||||||
*((uint32_t *) mask_) = mask;
|
|
||||||
for (size_t i = 0; i < len; ++i)
|
|
||||||
{
|
|
||||||
dst[i] = src[i] ^ mask_[(i + mask_offset) & 3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks a UTF-8 sequence
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
MHD_websocket_check_utf8 (const char *buf,
|
|
||||||
size_t buf_len,
|
|
||||||
int *utf8_step,
|
|
||||||
size_t *buf_offset)
|
|
||||||
{
|
|
||||||
int utf8_step_ = (NULL != utf8_step) ? *utf8_step :
|
|
||||||
MHD_WEBSOCKET_UTF8STEP_NORMAL;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < buf_len; ++i)
|
|
||||||
{
|
|
||||||
unsigned char character = (unsigned char) buf[i];
|
|
||||||
switch (utf8_step_)
|
|
||||||
{
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_NORMAL:
|
|
||||||
if ((0x00 <= character) && (0x7F >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: single byte UTF-8 sequence */
|
|
||||||
/* (nothing to do here) */
|
|
||||||
}
|
|
||||||
else if ((0xC2 <= character) && (0xDF >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: two byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1;
|
|
||||||
}
|
|
||||||
else if (0xE0 == character)
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0xA0-0xBF */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2;
|
|
||||||
}
|
|
||||||
else if (0xED == character)
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0x80-0x9F */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2;
|
|
||||||
}
|
|
||||||
else if (((0xE1 <= character) && (0xEC >= character)) ||
|
|
||||||
((0xEE <= character) && (0xEF >= character)) )
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: three byte UTF-8 sequence, both tail bytes must be 0x80-0xBF */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2;
|
|
||||||
}
|
|
||||||
else if (0xF0 == character)
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 0x90-0xBF */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3;
|
|
||||||
}
|
|
||||||
else if (0xF4 == character)
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 0x80-0x8F */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3;
|
|
||||||
}
|
|
||||||
else if ((0xF1 <= character) && (0xF3 >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: four byte UTF-8 sequence, all three tail bytes must be 0x80-0xBF */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2:
|
|
||||||
if ((0xA0 <= character) && (0xBF >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Second byte of three byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2:
|
|
||||||
if ((0x80 <= character) && (0x9F >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Second byte of three byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2:
|
|
||||||
if ((0x80 <= character) && (0xBF >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Second byte of three byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3:
|
|
||||||
if ((0x90 <= character) && (0xBF >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Second byte of four byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3:
|
|
||||||
if ((0x80 <= character) && (0x8F >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Second byte of four byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3:
|
|
||||||
if ((0x80 <= character) && (0xBF >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Second byte of four byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3:
|
|
||||||
if ((0x80 <= character) && (0xBF >= character))
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Third byte of four byte UTF-8 sequence */
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* RFC 3629 4: Second byte of two byte UTF-8 sequence */
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1:
|
|
||||||
/* RFC 3629 4: Third byte of three byte UTF-8 sequence */
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2:
|
|
||||||
/* RFC 3629 4: Fourth byte of four byte UTF-8 sequence */
|
|
||||||
case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3:
|
|
||||||
if ((0x80 <= character) && (0xBF >= character))
|
|
||||||
{
|
|
||||||
utf8_step_ = MHD_WEBSOCKET_UTF8STEP_NORMAL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* RFC 3629 4: Invalid UTF-8 byte */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Invalid last step...? */
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = i;
|
|
||||||
return MHD_WebSocket_UTF8Result_Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return values */
|
|
||||||
if (NULL != utf8_step)
|
|
||||||
*utf8_step = utf8_step_;
|
|
||||||
if (NULL != buf_offset)
|
|
||||||
*buf_offset = buf_len;
|
|
||||||
if (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step_)
|
|
||||||
{
|
|
||||||
return MHD_WebSocket_UTF8Result_Incomplete;
|
|
||||||
}
|
|
||||||
return MHD_WebSocket_UTF8Result_Valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a mask for masking by calling
|
|
||||||
* a random number generator.
|
|
||||||
*/
|
|
||||||
static uint32_t
|
|
||||||
MHD_websocket_generate_mask (struct MHD_WebSocketStream *ws)
|
|
||||||
{
|
|
||||||
unsigned char mask_[4];
|
|
||||||
if (NULL != ws->rng)
|
|
||||||
{
|
|
||||||
size_t offset = 0;
|
|
||||||
while (offset < 4)
|
|
||||||
{
|
|
||||||
size_t encoded = ws->rng (ws->cls_rng,
|
|
||||||
mask_ + offset,
|
|
||||||
4 - offset);
|
|
||||||
offset += encoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* this case should never happen */
|
|
||||||
mask_ [0] = 0;
|
|
||||||
mask_ [1] = 0;
|
|
||||||
mask_ [2] = 0;
|
|
||||||
mask_ [3] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *((uint32_t *) mask_);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls the malloc function associated with the websocket steam
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN void *
|
|
||||||
MHD_websocket_malloc (struct MHD_WebSocketStream *ws,
|
|
||||||
size_t buf_len)
|
|
||||||
{
|
|
||||||
if (NULL == ws)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ws->malloc (buf_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls the realloc function associated with the websocket steam
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN void *
|
|
||||||
MHD_websocket_realloc (struct MHD_WebSocketStream *ws,
|
|
||||||
void *buf,
|
|
||||||
size_t new_buf_len)
|
|
||||||
{
|
|
||||||
if (NULL == ws)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ws->realloc (buf, new_buf_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls the free function associated with the websocket steam
|
|
||||||
*/
|
|
||||||
_MHD_EXTERN int
|
|
||||||
MHD_websocket_free (struct MHD_WebSocketStream *ws,
|
|
||||||
void *buf)
|
|
||||||
{
|
|
||||||
if (NULL == ws)
|
|
||||||
{
|
|
||||||
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws->free (buf);
|
|
||||||
|
|
||||||
return MHD_WEBSOCKET_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a 16 bit value into network byte order (MSB first)
|
|
||||||
* in dependence of the host system
|
|
||||||
*/
|
|
||||||
static uint16_t
|
|
||||||
MHD_htons (uint16_t value)
|
|
||||||
{
|
|
||||||
uint16_t endian = 0x0001;
|
|
||||||
|
|
||||||
if (((char *) &endian)[0] == 0x01)
|
|
||||||
{
|
|
||||||
/* least significant byte first */
|
|
||||||
((char *) &endian)[0] = ((char *) &value)[1];
|
|
||||||
((char *) &endian)[1] = ((char *) &value)[0];
|
|
||||||
return endian;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* most significant byte first */
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a 64 bit value into network byte order (MSB first)
|
|
||||||
* in dependence of the host system
|
|
||||||
*/
|
|
||||||
static uint64_t
|
|
||||||
MHD_htonll (uint64_t value)
|
|
||||||
{
|
|
||||||
uint64_t endian = 0x0000000000000001;
|
|
||||||
|
|
||||||
if (((char *) &endian)[0] == 0x01)
|
|
||||||
{
|
|
||||||
/* least significant byte first */
|
|
||||||
((char *) &endian)[0] = ((char *) &value)[7];
|
|
||||||
((char *) &endian)[1] = ((char *) &value)[6];
|
|
||||||
((char *) &endian)[2] = ((char *) &value)[5];
|
|
||||||
((char *) &endian)[3] = ((char *) &value)[4];
|
|
||||||
((char *) &endian)[4] = ((char *) &value)[3];
|
|
||||||
((char *) &endian)[5] = ((char *) &value)[2];
|
|
||||||
((char *) &endian)[6] = ((char *) &value)[1];
|
|
||||||
((char *) &endian)[7] = ((char *) &value)[0];
|
|
||||||
return endian;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* most significant byte first */
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,378 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of libmicrohttpd
|
|
||||||
Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
|
|
||||||
|
|
||||||
libmicrohttpd 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; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file microhttpd/sha1.c
|
|
||||||
* @brief Calculation of SHA-1 digest as defined in FIPS PUB 180-4 (2015)
|
|
||||||
* @author Karlson2k (Evgeny Grin)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "sha1.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#ifdef HAVE_MEMORY_H
|
|
||||||
#include <memory.h>
|
|
||||||
#endif /* HAVE_MEMORY_H */
|
|
||||||
#include "mhd_bithelpers.h"
|
|
||||||
#include "mhd_assert.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise structure for SHA-1 calculation.
|
|
||||||
*
|
|
||||||
* @param ctx_ must be a `struct sha1_ctx *`
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
MHD_SHA1_init (void *ctx_)
|
|
||||||
{
|
|
||||||
struct sha1_ctx *const ctx = ctx_;
|
|
||||||
/* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.1 */
|
|
||||||
/* Just some "magic" numbers defined by standard */
|
|
||||||
ctx->H[0] = UINT32_C (0x67452301);
|
|
||||||
ctx->H[1] = UINT32_C (0xefcdab89);
|
|
||||||
ctx->H[2] = UINT32_C (0x98badcfe);
|
|
||||||
ctx->H[3] = UINT32_C (0x10325476);
|
|
||||||
ctx->H[4] = UINT32_C (0xc3d2e1f0);
|
|
||||||
|
|
||||||
/* Initialise number of bytes. */
|
|
||||||
ctx->count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base of SHA-1 transformation.
|
|
||||||
* Gets full 512 bits / 64 bytes block of data and updates hash values;
|
|
||||||
* @param H hash values
|
|
||||||
* @param data data, must be exactly 64 bytes long
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
sha1_transform (uint32_t H[_SHA1_DIGEST_LENGTH],
|
|
||||||
const uint8_t data[SHA1_BLOCK_SIZE])
|
|
||||||
{
|
|
||||||
/* Working variables,
|
|
||||||
see FIPS PUB 180-4 paragraph 6.1.3 */
|
|
||||||
uint32_t a = H[0];
|
|
||||||
uint32_t b = H[1];
|
|
||||||
uint32_t c = H[2];
|
|
||||||
uint32_t d = H[3];
|
|
||||||
uint32_t e = H[4];
|
|
||||||
|
|
||||||
/* Data buffer, used as cyclic buffer.
|
|
||||||
See FIPS PUB 180-4 paragraphs 5.2.1, 6.1.3 */
|
|
||||||
uint32_t W[16];
|
|
||||||
|
|
||||||
/* 'Ch' and 'Maj' macro functions are defined with
|
|
||||||
widely-used optimization.
|
|
||||||
See FIPS PUB 180-4 formulae 4.1. */
|
|
||||||
#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
|
|
||||||
#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
|
|
||||||
/* Unoptimized (original) versions: */
|
|
||||||
/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
|
|
||||||
/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
|
|
||||||
#define Par(x,y,z) ( (x) ^ (y) ^ (z) )
|
|
||||||
|
|
||||||
/* Single step of SHA-1 computation,
|
|
||||||
see FIPS PUB 180-4 paragraph 6.1.3 step 3.
|
|
||||||
* Note: instead of reassigning all working variables on each step,
|
|
||||||
variables are rotated for each step:
|
|
||||||
SHA1STEP32 (a, b, c, d, e, func, K00, W[0]);
|
|
||||||
SHA1STEP32 (e, a, b, c, d, func, K00, W[1]);
|
|
||||||
so current 'vC' will be used as 'vD' on the next step,
|
|
||||||
current 'vE' will be used as 'vA' on the next step.
|
|
||||||
* Note: 'wt' must be used exactly one time in this macro as it change other data as well
|
|
||||||
every time when used. */
|
|
||||||
|
|
||||||
#define SHA1STEP32(vA,vB,vC,vD,vE,ft,kt,wt) do { \
|
|
||||||
(vE) += _MHD_ROTL32 ((vA), 5) + ft ((vB), (vC), (vD)) + (kt) + (wt); \
|
|
||||||
(vB) = _MHD_ROTL32 ((vB), 30); } while (0)
|
|
||||||
|
|
||||||
/* Get value of W(t) from input data buffer,
|
|
||||||
See FIPS PUB 180-4 paragraph 6.1.3.
|
|
||||||
Input data must be read in big-endian bytes order,
|
|
||||||
see FIPS PUB 180-4 paragraph 3.1.2. */
|
|
||||||
#define GET_W_FROM_DATA(buf,t) \
|
|
||||||
_MHD_GET_32BIT_BE (((const uint8_t*) (buf)) + (t) * SHA1_BYTES_IN_WORD)
|
|
||||||
|
|
||||||
#ifndef _MHD_GET_32BIT_BE_UNALIGNED
|
|
||||||
if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
|
|
||||||
{
|
|
||||||
/* Copy the unaligned input data to the aligned buffer */
|
|
||||||
memcpy (W, data, SHA1_BLOCK_SIZE);
|
|
||||||
/* The W[] buffer itself will be used as the source of the data,
|
|
||||||
* but data will be reloaded in correct bytes order during
|
|
||||||
* the next steps */
|
|
||||||
data = (uint8_t *) W;
|
|
||||||
}
|
|
||||||
#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
|
|
||||||
|
|
||||||
/* SHA-1 values of Kt for t=0..19, see FIPS PUB 180-4 paragraph 4.2.1. */
|
|
||||||
#define K00 UINT32_C(0x5a827999)
|
|
||||||
/* SHA-1 values of Kt for t=20..39, see FIPS PUB 180-4 paragraph 4.2.1.*/
|
|
||||||
#define K20 UINT32_C(0x6ed9eba1)
|
|
||||||
/* SHA-1 values of Kt for t=40..59, see FIPS PUB 180-4 paragraph 4.2.1.*/
|
|
||||||
#define K40 UINT32_C(0x8f1bbcdc)
|
|
||||||
/* SHA-1 values of Kt for t=60..79, see FIPS PUB 180-4 paragraph 4.2.1.*/
|
|
||||||
#define K60 UINT32_C(0xca62c1d6)
|
|
||||||
|
|
||||||
/* During first 16 steps, before making any calculations on each step,
|
|
||||||
the W element is read from input data buffer as big-endian value and
|
|
||||||
stored in array of W elements. */
|
|
||||||
/* Note: instead of using K constants as array, all K values are specified
|
|
||||||
individually for each step. */
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Ch, K00, W[0] = GET_W_FROM_DATA (data, 0));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Ch, K00, W[1] = GET_W_FROM_DATA (data, 1));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Ch, K00, W[2] = GET_W_FROM_DATA (data, 2));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Ch, K00, W[3] = GET_W_FROM_DATA (data, 3));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Ch, K00, W[4] = GET_W_FROM_DATA (data, 4));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Ch, K00, W[5] = GET_W_FROM_DATA (data, 5));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Ch, K00, W[6] = GET_W_FROM_DATA (data, 6));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Ch, K00, W[7] = GET_W_FROM_DATA (data, 7));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Ch, K00, W[8] = GET_W_FROM_DATA (data, 8));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Ch, K00, W[9] = GET_W_FROM_DATA (data, 9));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Ch, K00, W[10] = GET_W_FROM_DATA (data, 10));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Ch, K00, W[11] = GET_W_FROM_DATA (data, 11));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Ch, K00, W[12] = GET_W_FROM_DATA (data, 12));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Ch, K00, W[13] = GET_W_FROM_DATA (data, 13));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Ch, K00, W[14] = GET_W_FROM_DATA (data, 14));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Ch, K00, W[15] = GET_W_FROM_DATA (data, 15));
|
|
||||||
|
|
||||||
/* 'W' generation and assignment for 16 <= t <= 79.
|
|
||||||
See FIPS PUB 180-4 paragraph 6.1.3.
|
|
||||||
As only last 16 'W' are used in calculations, it is possible to
|
|
||||||
use 16 elements array of W as cyclic buffer. */
|
|
||||||
#define Wgen(w,t) _MHD_ROTL32((w)[(t + 13) & 0xf] ^ (w)[(t + 8) & 0xf] \
|
|
||||||
^ (w)[(t + 2) & 0xf] ^ (w)[t & 0xf], 1)
|
|
||||||
|
|
||||||
/* During last 60 steps, before making any calculations on each step,
|
|
||||||
W element is generated from W elements of cyclic buffer and generated value
|
|
||||||
stored back in cyclic buffer. */
|
|
||||||
/* Note: instead of using K constants as array, all K values are specified
|
|
||||||
individually for each step, see FIPS PUB 180-4 paragraph 4.2.1. */
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Ch, K00, W[16 & 0xf] = Wgen (W, 16));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Ch, K00, W[17 & 0xf] = Wgen (W, 17));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Ch, K00, W[18 & 0xf] = Wgen (W, 18));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Ch, K00, W[19 & 0xf] = Wgen (W, 19));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K20, W[20 & 0xf] = Wgen (W, 20));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K20, W[21 & 0xf] = Wgen (W, 21));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K20, W[22 & 0xf] = Wgen (W, 22));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K20, W[23 & 0xf] = Wgen (W, 23));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K20, W[24 & 0xf] = Wgen (W, 24));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K20, W[25 & 0xf] = Wgen (W, 25));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K20, W[26 & 0xf] = Wgen (W, 26));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K20, W[27 & 0xf] = Wgen (W, 27));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K20, W[28 & 0xf] = Wgen (W, 28));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K20, W[29 & 0xf] = Wgen (W, 29));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K20, W[30 & 0xf] = Wgen (W, 30));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K20, W[31 & 0xf] = Wgen (W, 31));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K20, W[32 & 0xf] = Wgen (W, 32));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K20, W[33 & 0xf] = Wgen (W, 33));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K20, W[34 & 0xf] = Wgen (W, 34));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K20, W[35 & 0xf] = Wgen (W, 35));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K20, W[36 & 0xf] = Wgen (W, 36));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K20, W[37 & 0xf] = Wgen (W, 37));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K20, W[38 & 0xf] = Wgen (W, 38));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K20, W[39 & 0xf] = Wgen (W, 39));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Maj, K40, W[40 & 0xf] = Wgen (W, 40));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Maj, K40, W[41 & 0xf] = Wgen (W, 41));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Maj, K40, W[42 & 0xf] = Wgen (W, 42));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Maj, K40, W[43 & 0xf] = Wgen (W, 43));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Maj, K40, W[44 & 0xf] = Wgen (W, 44));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Maj, K40, W[45 & 0xf] = Wgen (W, 45));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Maj, K40, W[46 & 0xf] = Wgen (W, 46));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Maj, K40, W[47 & 0xf] = Wgen (W, 47));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Maj, K40, W[48 & 0xf] = Wgen (W, 48));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Maj, K40, W[49 & 0xf] = Wgen (W, 49));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Maj, K40, W[50 & 0xf] = Wgen (W, 50));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Maj, K40, W[51 & 0xf] = Wgen (W, 51));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Maj, K40, W[52 & 0xf] = Wgen (W, 52));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Maj, K40, W[53 & 0xf] = Wgen (W, 53));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Maj, K40, W[54 & 0xf] = Wgen (W, 54));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Maj, K40, W[55 & 0xf] = Wgen (W, 55));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Maj, K40, W[56 & 0xf] = Wgen (W, 56));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Maj, K40, W[57 & 0xf] = Wgen (W, 57));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Maj, K40, W[58 & 0xf] = Wgen (W, 58));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Maj, K40, W[59 & 0xf] = Wgen (W, 59));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K60, W[60 & 0xf] = Wgen (W, 60));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K60, W[61 & 0xf] = Wgen (W, 61));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K60, W[62 & 0xf] = Wgen (W, 62));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K60, W[63 & 0xf] = Wgen (W, 63));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K60, W[64 & 0xf] = Wgen (W, 64));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K60, W[65 & 0xf] = Wgen (W, 65));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K60, W[66 & 0xf] = Wgen (W, 66));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K60, W[67 & 0xf] = Wgen (W, 67));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K60, W[68 & 0xf] = Wgen (W, 68));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K60, W[69 & 0xf] = Wgen (W, 69));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K60, W[70 & 0xf] = Wgen (W, 70));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K60, W[71 & 0xf] = Wgen (W, 71));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K60, W[72 & 0xf] = Wgen (W, 72));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K60, W[73 & 0xf] = Wgen (W, 73));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K60, W[74 & 0xf] = Wgen (W, 74));
|
|
||||||
SHA1STEP32 (a, b, c, d, e, Par, K60, W[75 & 0xf] = Wgen (W, 75));
|
|
||||||
SHA1STEP32 (e, a, b, c, d, Par, K60, W[76 & 0xf] = Wgen (W, 76));
|
|
||||||
SHA1STEP32 (d, e, a, b, c, Par, K60, W[77 & 0xf] = Wgen (W, 77));
|
|
||||||
SHA1STEP32 (c, d, e, a, b, Par, K60, W[78 & 0xf] = Wgen (W, 78));
|
|
||||||
SHA1STEP32 (b, c, d, e, a, Par, K60, W[79 & 0xf] = Wgen (W, 79));
|
|
||||||
|
|
||||||
/* Compute intermediate hash.
|
|
||||||
See FIPS PUB 180-4 paragraph 6.1.3 step 4. */
|
|
||||||
H[0] += a;
|
|
||||||
H[1] += b;
|
|
||||||
H[2] += c;
|
|
||||||
H[3] += d;
|
|
||||||
H[4] += e;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process portion of bytes.
|
|
||||||
*
|
|
||||||
* @param ctx_ must be a `struct sha1_ctx *`
|
|
||||||
* @param data bytes to add to hash
|
|
||||||
* @param length number of bytes in @a data
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
MHD_SHA1_update (void *ctx_,
|
|
||||||
const uint8_t *data,
|
|
||||||
size_t length)
|
|
||||||
{
|
|
||||||
struct sha1_ctx *const ctx = ctx_;
|
|
||||||
unsigned bytes_have; /**< Number of bytes in buffer */
|
|
||||||
|
|
||||||
mhd_assert ((data != NULL) || (length == 0));
|
|
||||||
|
|
||||||
if (0 == length)
|
|
||||||
return; /* Do nothing */
|
|
||||||
|
|
||||||
/* Note: (count & (SHA1_BLOCK_SIZE-1))
|
|
||||||
equal (count % SHA1_BLOCK_SIZE) for this block size. */
|
|
||||||
bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
|
|
||||||
ctx->count += length;
|
|
||||||
|
|
||||||
if (0 != bytes_have)
|
|
||||||
{
|
|
||||||
unsigned bytes_left = SHA1_BLOCK_SIZE - bytes_have;
|
|
||||||
if (length >= bytes_left)
|
|
||||||
{ /* Combine new data with the data in the buffer and
|
|
||||||
process the full block. */
|
|
||||||
memcpy (ctx->buffer + bytes_have,
|
|
||||||
data,
|
|
||||||
bytes_left);
|
|
||||||
data += bytes_left;
|
|
||||||
length -= bytes_left;
|
|
||||||
sha1_transform (ctx->H, ctx->buffer);
|
|
||||||
bytes_have = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (SHA1_BLOCK_SIZE <= length)
|
|
||||||
{ /* Process any full blocks of new data directly,
|
|
||||||
without copying to the buffer. */
|
|
||||||
sha1_transform (ctx->H, data);
|
|
||||||
data += SHA1_BLOCK_SIZE;
|
|
||||||
length -= SHA1_BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 != length)
|
|
||||||
{ /* Copy incomplete block of new data (if any)
|
|
||||||
to the buffer. */
|
|
||||||
memcpy (ctx->buffer + bytes_have, data, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of "length" padding addition in bytes.
|
|
||||||
* See FIPS PUB 180-4 paragraph 5.1.1.
|
|
||||||
*/
|
|
||||||
#define SHA1_SIZE_OF_LEN_ADD (64 / 8)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finalise SHA-1 calculation, return digest.
|
|
||||||
*
|
|
||||||
* @param ctx_ must be a `struct sha1_ctx *`
|
|
||||||
* @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
MHD_SHA1_finish (void *ctx_,
|
|
||||||
uint8_t digest[SHA1_DIGEST_SIZE])
|
|
||||||
{
|
|
||||||
struct sha1_ctx *const ctx = ctx_;
|
|
||||||
uint64_t num_bits; /**< Number of processed bits */
|
|
||||||
unsigned bytes_have; /**< Number of bytes in buffer */
|
|
||||||
|
|
||||||
num_bits = ctx->count << 3;
|
|
||||||
/* Note: (count & (SHA1_BLOCK_SIZE-1))
|
|
||||||
equals (count % SHA1_BLOCK_SIZE) for this block size. */
|
|
||||||
bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
|
|
||||||
|
|
||||||
/* Input data must be padded with bit "1" and with length of data in bits.
|
|
||||||
See FIPS PUB 180-4 paragraph 5.1.1. */
|
|
||||||
/* Data is always processed in form of bytes (not by individual bits),
|
|
||||||
therefore position of first padding bit in byte is always predefined (0x80). */
|
|
||||||
/* Buffer always have space at least for one byte (as full buffers are
|
|
||||||
processed immediately). */
|
|
||||||
ctx->buffer[bytes_have++] = 0x80;
|
|
||||||
|
|
||||||
if (SHA1_BLOCK_SIZE - bytes_have < SHA1_SIZE_OF_LEN_ADD)
|
|
||||||
{ /* No space in current block to put total length of message.
|
|
||||||
Pad current block with zeros and process it. */
|
|
||||||
if (SHA1_BLOCK_SIZE > bytes_have)
|
|
||||||
memset (ctx->buffer + bytes_have, 0, SHA1_BLOCK_SIZE - bytes_have);
|
|
||||||
/* Process full block. */
|
|
||||||
sha1_transform (ctx->H, ctx->buffer);
|
|
||||||
/* Start new block. */
|
|
||||||
bytes_have = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pad the rest of the buffer with zeros. */
|
|
||||||
memset (ctx->buffer + bytes_have, 0,
|
|
||||||
SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD - bytes_have);
|
|
||||||
/* Put the number of bits in the processed message as a big-endian value. */
|
|
||||||
_MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD,
|
|
||||||
num_bits);
|
|
||||||
/* Process the full final block. */
|
|
||||||
sha1_transform (ctx->H, ctx->buffer);
|
|
||||||
|
|
||||||
/* Put final hash/digest in BE mode */
|
|
||||||
#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
|
|
||||||
if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
|
|
||||||
{
|
|
||||||
uint32_t alig_dgst[_SHA1_DIGEST_LENGTH];
|
|
||||||
_MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
|
|
||||||
_MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
|
|
||||||
_MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
|
|
||||||
_MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
|
|
||||||
_MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
|
|
||||||
/* Copy result to unaligned destination address */
|
|
||||||
memcpy (digest, alig_dgst, SHA1_DIGEST_SIZE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#else /* _MHD_PUT_32BIT_BE_UNALIGNED */
|
|
||||||
if (1)
|
|
||||||
#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */
|
|
||||||
{
|
|
||||||
_MHD_PUT_32BIT_BE (digest + 0 * SHA1_BYTES_IN_WORD, ctx->H[0]);
|
|
||||||
_MHD_PUT_32BIT_BE (digest + 1 * SHA1_BYTES_IN_WORD, ctx->H[1]);
|
|
||||||
_MHD_PUT_32BIT_BE (digest + 2 * SHA1_BYTES_IN_WORD, ctx->H[2]);
|
|
||||||
_MHD_PUT_32BIT_BE (digest + 3 * SHA1_BYTES_IN_WORD, ctx->H[3]);
|
|
||||||
_MHD_PUT_32BIT_BE (digest + 4 * SHA1_BYTES_IN_WORD, ctx->H[4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Erase potentially sensitive data. */
|
|
||||||
memset (ctx, 0, sizeof(struct sha1_ctx));
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of libmicrohttpd
|
|
||||||
Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
|
|
||||||
|
|
||||||
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; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file microhttpd/sha1.h
|
|
||||||
* @brief Calculation of SHA-1 digest
|
|
||||||
* @author Karlson2k (Evgeny Grin)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MHD_SHA1_H
|
|
||||||
#define MHD_SHA1_H 1
|
|
||||||
|
|
||||||
#include "mhd_options.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
#ifdef HAVE_STDDEF_H
|
|
||||||
#include <stddef.h> /* for size_t */
|
|
||||||
#endif /* HAVE_STDDEF_H */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHA-1 digest is kept internally as 5 32-bit words.
|
|
||||||
*/
|
|
||||||
#define _SHA1_DIGEST_LENGTH 5
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of bits in single SHA-1 word
|
|
||||||
*/
|
|
||||||
#define SHA1_WORD_SIZE_BITS 32
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of bytes in single SHA-1 word
|
|
||||||
*/
|
|
||||||
#define SHA1_BYTES_IN_WORD (SHA1_WORD_SIZE_BITS / 8)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of SHA-1 digest in bytes
|
|
||||||
*/
|
|
||||||
#define SHA1_DIGEST_SIZE (_SHA1_DIGEST_LENGTH * SHA1_BYTES_IN_WORD)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of SHA-1 digest string in chars including termination NUL
|
|
||||||
*/
|
|
||||||
#define SHA1_DIGEST_STRING_SIZE ((SHA1_DIGEST_SIZE) * 2 + 1)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of single processing block in bits
|
|
||||||
*/
|
|
||||||
#define SHA1_BLOCK_SIZE_BITS 512
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of single processing block in bytes
|
|
||||||
*/
|
|
||||||
#define SHA1_BLOCK_SIZE (SHA1_BLOCK_SIZE_BITS / 8)
|
|
||||||
|
|
||||||
|
|
||||||
struct sha1_ctx
|
|
||||||
{
|
|
||||||
uint32_t H[_SHA1_DIGEST_LENGTH]; /**< Intermediate hash value / digest at end of calculation */
|
|
||||||
uint8_t buffer[SHA1_BLOCK_SIZE]; /**< SHA256 input data buffer */
|
|
||||||
uint64_t count; /**< number of bytes, mod 2^64 */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise structure for SHA-1 calculation.
|
|
||||||
*
|
|
||||||
* @param ctx must be a `struct sha1_ctx *`
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
MHD_SHA1_init (void *ctx_);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process portion of bytes.
|
|
||||||
*
|
|
||||||
* @param ctx_ must be a `struct sha1_ctx *`
|
|
||||||
* @param data bytes to add to hash
|
|
||||||
* @param length number of bytes in @a data
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
MHD_SHA1_update (void *ctx_,
|
|
||||||
const uint8_t *data,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finalise SHA-1 calculation, return digest.
|
|
||||||
*
|
|
||||||
* @param ctx_ must be a `struct sha1_ctx *`
|
|
||||||
* @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
MHD_SHA1_finish (void *ctx_,
|
|
||||||
uint8_t digest[SHA1_DIGEST_SIZE]);
|
|
||||||
|
|
||||||
#endif /* MHD_SHA1_H */
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,567 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of libmicrohttpd
|
|
||||||
Copyright (C) 2021 David Gausmann
|
|
||||||
|
|
||||||
libmicrohttpd is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published
|
|
||||||
by the Free Software Foundation; either version 3, or (at your
|
|
||||||
option) any later version.
|
|
||||||
|
|
||||||
libmicrohttpd 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
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libmicrohttpd; see the file COPYING. If not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file test_websocket_browser.c
|
|
||||||
* @brief Testcase for WebSocket decoding/encoding with external browser
|
|
||||||
* @author David Gausmann
|
|
||||||
*/
|
|
||||||
#include <sys/types.h>
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#else
|
|
||||||
#include <winsock2.h>
|
|
||||||
#endif
|
|
||||||
#include "microhttpd.h"
|
|
||||||
#include "microhttpd_ws.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#define PORT 80
|
|
||||||
|
|
||||||
#define PAGE \
|
|
||||||
"<!DOCTYPE html>\n" \
|
|
||||||
"<html>\n" \
|
|
||||||
"<head>\n" \
|
|
||||||
"<meta charset=\"UTF-8\">\n" \
|
|
||||||
"<title>Websocket External Test with Webbrowser</title>\n" \
|
|
||||||
"<script>\n" \
|
|
||||||
"\n" \
|
|
||||||
"let current_mode = 0;\n" \
|
|
||||||
"let current_step = 0;\n" \
|
|
||||||
"let sent_payload = null;\n" \
|
|
||||||
"let charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_!@%&/\\\\';\n" \
|
|
||||||
"let step_to_bytes = [ 0, 1, 2, 3, 122, 123, 124, 125, 126, 127, 128, 32766, 32767, 32768, 65534, 65535, 65536, 65537, 1048576, 10485760 ];\n" \
|
|
||||||
"let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
|
|
||||||
" + ':/" "/' +\n" \
|
|
||||||
" window.location.host + '/websocket';\n" \
|
|
||||||
"let socket = null;\n" \
|
|
||||||
"\n" \
|
|
||||||
"window.onload = function (event) {\n" \
|
|
||||||
" if (!window.WebSocket) {\n" \
|
|
||||||
" document.write ('ERROR: The WebSocket class is not supported by your browser.<br>');\n" \
|
|
||||||
" }\n" \
|
|
||||||
" if (!window.fetch) {\n" \
|
|
||||||
" document.write ('ERROR: The fetch-API is not supported by your browser.<br>');\n" \
|
|
||||||
" }\n" \
|
|
||||||
" document.write ('Starting tests.<br>');\n" \
|
|
||||||
" runTest ();\n" \
|
|
||||||
"}\n" \
|
|
||||||
"\n" \
|
|
||||||
"function runTest () {\n" \
|
|
||||||
" switch (current_mode) {\n" \
|
|
||||||
" case 0:\n" \
|
|
||||||
" document.write ('TEXT');\n" \
|
|
||||||
" break;\n" \
|
|
||||||
" case 1:\n" \
|
|
||||||
" document.write ('BINARY');\n" \
|
|
||||||
" break;\n" \
|
|
||||||
" }\n" \
|
|
||||||
" document.write (', ' + step_to_bytes[current_step] + ' Bytes: ');\n" \
|
|
||||||
" socket = new WebSocket(url);\n" \
|
|
||||||
" socket.binaryType = 'arraybuffer';\n" \
|
|
||||||
" socket.onopen = function (event) {\n" \
|
|
||||||
" switch (current_mode) {\n" \
|
|
||||||
" case 0:\n" \
|
|
||||||
" sent_payload = randomText (step_to_bytes[current_step]);\n" \
|
|
||||||
" socket.send (sent_payload);\n" \
|
|
||||||
" break;\n" \
|
|
||||||
" case 1:\n" \
|
|
||||||
" sent_payload = randomBinary (step_to_bytes[current_step]);\n" \
|
|
||||||
" socket.send (sent_payload);\n" \
|
|
||||||
" break;\n" \
|
|
||||||
" }\n" \
|
|
||||||
" }\n" \
|
|
||||||
"\n" \
|
|
||||||
" socket.onclose = function (event) {\n" \
|
|
||||||
" socket.onmessage = null;\n" \
|
|
||||||
" socket.onclose = null;\n" \
|
|
||||||
" socket.onerror = null;\n" \
|
|
||||||
" document.write ('CLOSED unexpectedly.<br>');\n" \
|
|
||||||
" notifyError ();\n" \
|
|
||||||
" }\n" \
|
|
||||||
"\n" \
|
|
||||||
" socket.onerror = function (event) {\n" \
|
|
||||||
" socket.onmessage = null;\n" \
|
|
||||||
" socket.onclose = null;\n" \
|
|
||||||
" socket.onerror = null;\n" \
|
|
||||||
" document.write ('ERROR.<br>');\n" \
|
|
||||||
" notifyError ();\n" \
|
|
||||||
" }\n" \
|
|
||||||
"\n" \
|
|
||||||
" socket.onmessage = async function (event) {\n" \
|
|
||||||
" if (compareData (event.data, sent_payload)) {\n" \
|
|
||||||
" document.write ('SUCCESS.<br>');\n" \
|
|
||||||
" socket.onmessage = null;\n" \
|
|
||||||
" socket.onclose = null;\n" \
|
|
||||||
" socket.onerror = null;\n" \
|
|
||||||
" socket.close();\n" \
|
|
||||||
" socket = null;\n" \
|
|
||||||
" if (step_to_bytes.length <= ++current_step) {\n" \
|
|
||||||
" current_step = 0;\n" \
|
|
||||||
" if (1 < ++current_mode) {\n" \
|
|
||||||
" document.write ('FINISHED ALL TESTS.<br>');\n" \
|
|
||||||
" return;\n" \
|
|
||||||
" }\n" \
|
|
||||||
" }\n" \
|
|
||||||
" runTest ();\n" \
|
|
||||||
" }" \
|
|
||||||
" }\n" \
|
|
||||||
"}\n" \
|
|
||||||
"\n" \
|
|
||||||
"function compareData (data, data2) {\n" \
|
|
||||||
" if (typeof (data) === 'string' && typeof (data2) === 'string') {\n" \
|
|
||||||
" return (data === data2); \n" \
|
|
||||||
" } \n" \
|
|
||||||
" else if ((data instanceof ArrayBuffer) && (data2 instanceof ArrayBuffer)) {\n" \
|
|
||||||
" let view1 = new Uint8Array (data);\n" \
|
|
||||||
" let view2 = new Uint8Array (data2);\n" \
|
|
||||||
" if (view1.length != view2.length)\n" \
|
|
||||||
" return false;\n" \
|
|
||||||
" for (let i = 0; i < view1.length; ++i) {\n" \
|
|
||||||
" if (view1[i] !== view2[i])\n" \
|
|
||||||
" return false;\n" \
|
|
||||||
" }\n" \
|
|
||||||
" return true;\n" \
|
|
||||||
" }\n" \
|
|
||||||
" else\n" \
|
|
||||||
" {\n" \
|
|
||||||
" return false;\n" \
|
|
||||||
" }\n" \
|
|
||||||
"}\n" \
|
|
||||||
"\n" \
|
|
||||||
"function randomText (length) {\n" \
|
|
||||||
" let result = new Array (length);\n" \
|
|
||||||
" for (let i = 0; i < length; ++i)\n" \
|
|
||||||
" result [i] = charset [~~(Math.random () * charset.length)];\n" \
|
|
||||||
" return result.join ('');\n" \
|
|
||||||
"}\n" \
|
|
||||||
"\n" \
|
|
||||||
"function randomBinary (length) {\n" \
|
|
||||||
" let buffer = new ArrayBuffer (length);\n" \
|
|
||||||
" let view = new Uint8Array (buffer);\n" \
|
|
||||||
" for (let i = 0; i < length; ++i)\n" \
|
|
||||||
" view [i] = ~~(Math.random () * 256);\n" \
|
|
||||||
" return buffer;\n" \
|
|
||||||
"}\n" \
|
|
||||||
"\n" \
|
|
||||||
"function notifyError () {\n" \
|
|
||||||
" fetch('error/' + (0 == current_mode ? 'text' : 'binary') + '/' + step_to_bytes[current_step]);\n" \
|
|
||||||
"}\n" \
|
|
||||||
"\n" \
|
|
||||||
"</script>\n" \
|
|
||||||
"</head>\n" \
|
|
||||||
"<body>\n" \
|
|
||||||
"</body>\n" \
|
|
||||||
"</html>"
|
|
||||||
|
|
||||||
#define PAGE_NOT_FOUND \
|
|
||||||
"404 Not Found"
|
|
||||||
|
|
||||||
#define PAGE_INVALID_WEBSOCKET_REQUEST \
|
|
||||||
"Invalid WebSocket request!"
|
|
||||||
|
|
||||||
static void
|
|
||||||
send_all (MHD_socket fd,
|
|
||||||
const char *buf,
|
|
||||||
size_t len);
|
|
||||||
|
|
||||||
static void
|
|
||||||
make_blocking (MHD_socket fd);
|
|
||||||
|
|
||||||
static void
|
|
||||||
upgrade_handler (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
void *req_cls,
|
|
||||||
const char *extra_in,
|
|
||||||
size_t extra_in_size,
|
|
||||||
MHD_socket fd,
|
|
||||||
struct MHD_UpgradeResponseHandle *urh)
|
|
||||||
{
|
|
||||||
/* make the socket blocking (operating-system-dependent code) */
|
|
||||||
make_blocking (fd);
|
|
||||||
|
|
||||||
/* create a websocket stream for this connection */
|
|
||||||
struct MHD_WebSocketStream *ws;
|
|
||||||
int result = MHD_websocket_stream_init (&ws,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
if (0 != result)
|
|
||||||
{
|
|
||||||
/* Couldn't create the websocket stream.
|
|
||||||
* So we close the socket and leave
|
|
||||||
*/
|
|
||||||
MHD_upgrade_action (urh,
|
|
||||||
MHD_UPGRADE_ACTION_CLOSE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Let's wait for incoming data */
|
|
||||||
const size_t buf_len = 256;
|
|
||||||
char buf[buf_len];
|
|
||||||
ssize_t got;
|
|
||||||
while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
|
|
||||||
{
|
|
||||||
got = recv (fd,
|
|
||||||
buf,
|
|
||||||
sizeof (buf),
|
|
||||||
0);
|
|
||||||
if (0 >= got)
|
|
||||||
{
|
|
||||||
/* the TCP/IP socket has been closed */
|
|
||||||
fprintf (stderr,
|
|
||||||
"Error (The socket has been closed unexpectedly)\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse the entire received data */
|
|
||||||
size_t buf_offset = 0;
|
|
||||||
while (buf_offset < (size_t) got)
|
|
||||||
{
|
|
||||||
size_t new_offset = 0;
|
|
||||||
char *payload_data = NULL;
|
|
||||||
size_t payload_len = 0;
|
|
||||||
char *frame_data = NULL;
|
|
||||||
size_t frame_len = 0;
|
|
||||||
int status = MHD_websocket_decode (ws,
|
|
||||||
buf + buf_offset,
|
|
||||||
((size_t) got) - buf_offset,
|
|
||||||
&new_offset,
|
|
||||||
&payload_data,
|
|
||||||
&payload_len);
|
|
||||||
if (0 > status)
|
|
||||||
{
|
|
||||||
/* an error occurred and the connection must be closed */
|
|
||||||
printf ("Decoding failed: status=%d, passed=%u\n", status,
|
|
||||||
((size_t) got) - buf_offset);
|
|
||||||
if (NULL != payload_data)
|
|
||||||
{
|
|
||||||
MHD_websocket_free (ws, payload_data);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buf_offset += new_offset;
|
|
||||||
if (0 < status)
|
|
||||||
{
|
|
||||||
/* the frame is complete */
|
|
||||||
printf (
|
|
||||||
"Decoding succeeded: type=%d, passed=%u, parsed=%u, payload_len=%d\n",
|
|
||||||
status, ((size_t) got) - buf_offset, new_offset, payload_len);
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
|
|
||||||
case MHD_WEBSOCKET_STATUS_BINARY_FRAME:
|
|
||||||
/* The client has sent some data. */
|
|
||||||
if ((NULL != payload_data) || (0 == payload_len))
|
|
||||||
{
|
|
||||||
/* Send the received data back to the client */
|
|
||||||
if (MHD_WEBSOCKET_STATUS_TEXT_FRAME == status)
|
|
||||||
{
|
|
||||||
result = MHD_websocket_encode_text (ws,
|
|
||||||
payload_data,
|
|
||||||
payload_len,
|
|
||||||
0,
|
|
||||||
&frame_data,
|
|
||||||
&frame_len,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = MHD_websocket_encode_binary (ws,
|
|
||||||
payload_data,
|
|
||||||
payload_len,
|
|
||||||
0,
|
|
||||||
&frame_data,
|
|
||||||
&frame_len);
|
|
||||||
}
|
|
||||||
if (0 == result)
|
|
||||||
{
|
|
||||||
send_all (fd,
|
|
||||||
frame_data,
|
|
||||||
frame_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* should never happen */
|
|
||||||
fprintf (stderr,
|
|
||||||
"Error (Empty buffer with payload_len != 0)\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Other frame types are ignored
|
|
||||||
* in this test script.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NULL != payload_data)
|
|
||||||
{
|
|
||||||
MHD_websocket_free (ws, payload_data);
|
|
||||||
}
|
|
||||||
if (NULL != frame_data)
|
|
||||||
{
|
|
||||||
MHD_websocket_free (ws, frame_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free the websocket stream */
|
|
||||||
MHD_websocket_stream_free (ws);
|
|
||||||
|
|
||||||
/* close the socket when it is not needed anymore */
|
|
||||||
MHD_upgrade_action (urh,
|
|
||||||
MHD_UPGRADE_ACTION_CLOSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* This helper function is used for the case that
|
|
||||||
* we need to resend some data
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
send_all (MHD_socket fd,
|
|
||||||
const char *buf,
|
|
||||||
size_t len)
|
|
||||||
{
|
|
||||||
ssize_t ret;
|
|
||||||
size_t off;
|
|
||||||
|
|
||||||
for (off = 0; off < len; off += ret)
|
|
||||||
{
|
|
||||||
ret = send (fd,
|
|
||||||
&buf[off],
|
|
||||||
(int) (len - off),
|
|
||||||
0);
|
|
||||||
if (0 > ret)
|
|
||||||
{
|
|
||||||
if (EAGAIN == errno)
|
|
||||||
{
|
|
||||||
ret = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (0 == ret)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* This helper function contains operating-system-dependent code and
|
|
||||||
* is used to make a socket blocking.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
make_blocking (MHD_socket fd)
|
|
||||||
{
|
|
||||||
#ifndef _WIN32
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
flags = fcntl (fd, F_GETFL);
|
|
||||||
if (-1 == flags)
|
|
||||||
return;
|
|
||||||
if ((flags & ~O_NONBLOCK) != flags)
|
|
||||||
if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
|
|
||||||
abort ();
|
|
||||||
#else
|
|
||||||
unsigned long flags = 0;
|
|
||||||
|
|
||||||
ioctlsocket (fd, FIONBIO, &flags);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static enum MHD_Result
|
|
||||||
access_handler (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const char *url,
|
|
||||||
const char *method,
|
|
||||||
const char *version,
|
|
||||||
const char *upload_data,
|
|
||||||
size_t *upload_data_size,
|
|
||||||
void **req_cls)
|
|
||||||
{
|
|
||||||
static int aptr;
|
|
||||||
struct MHD_Response *response;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
(void) cls; /* Unused. Silent compiler warning. */
|
|
||||||
(void) upload_data; /* Unused. Silent compiler warning. */
|
|
||||||
(void) upload_data_size; /* Unused. Silent compiler warning. */
|
|
||||||
|
|
||||||
if (0 != strcmp (method, "GET"))
|
|
||||||
return MHD_NO; /* unexpected method */
|
|
||||||
if (&aptr != *req_cls)
|
|
||||||
{
|
|
||||||
/* do never respond on first call */
|
|
||||||
*req_cls = &aptr;
|
|
||||||
return MHD_YES;
|
|
||||||
}
|
|
||||||
*req_cls = NULL; /* reset when done */
|
|
||||||
|
|
||||||
if (0 == strcmp (url, "/"))
|
|
||||||
{
|
|
||||||
/* Default page for visiting the server */
|
|
||||||
struct MHD_Response *response = MHD_create_response_from_buffer (
|
|
||||||
strlen (PAGE),
|
|
||||||
PAGE,
|
|
||||||
MHD_RESPMEM_PERSISTENT);
|
|
||||||
ret = MHD_queue_response (connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
response);
|
|
||||||
MHD_destroy_response (response);
|
|
||||||
}
|
|
||||||
else if (0 == strncmp (url, "/error/", 7))
|
|
||||||
{
|
|
||||||
/* Report error */
|
|
||||||
fprintf (stderr, "Error in test (%s)\n", url + 7);
|
|
||||||
|
|
||||||
struct MHD_Response *response = MHD_create_response_from_buffer (
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
MHD_RESPMEM_PERSISTENT);
|
|
||||||
ret = MHD_queue_response (connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
response);
|
|
||||||
MHD_destroy_response (response);
|
|
||||||
}
|
|
||||||
else if (0 == strcmp (url, "/websocket"))
|
|
||||||
{
|
|
||||||
char is_valid = 1;
|
|
||||||
const char *value = NULL;
|
|
||||||
char sec_websocket_accept[29];
|
|
||||||
|
|
||||||
if (0 != MHD_websocket_check_http_version (version))
|
|
||||||
{
|
|
||||||
is_valid = 0;
|
|
||||||
}
|
|
||||||
value = MHD_lookup_connection_value (connection,
|
|
||||||
MHD_HEADER_KIND,
|
|
||||||
MHD_HTTP_HEADER_CONNECTION);
|
|
||||||
if (0 != MHD_websocket_check_connection_header (value))
|
|
||||||
{
|
|
||||||
is_valid = 0;
|
|
||||||
}
|
|
||||||
value = MHD_lookup_connection_value (connection,
|
|
||||||
MHD_HEADER_KIND,
|
|
||||||
MHD_HTTP_HEADER_UPGRADE);
|
|
||||||
if (0 != MHD_websocket_check_upgrade_header (value))
|
|
||||||
{
|
|
||||||
is_valid = 0;
|
|
||||||
}
|
|
||||||
value = MHD_lookup_connection_value (connection,
|
|
||||||
MHD_HEADER_KIND,
|
|
||||||
MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
|
|
||||||
if (0 != MHD_websocket_check_version_header (value))
|
|
||||||
{
|
|
||||||
is_valid = 0;
|
|
||||||
}
|
|
||||||
value = MHD_lookup_connection_value (connection,
|
|
||||||
MHD_HEADER_KIND,
|
|
||||||
MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
|
|
||||||
if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
|
|
||||||
{
|
|
||||||
is_valid = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 == is_valid)
|
|
||||||
{
|
|
||||||
/* upgrade the connection */
|
|
||||||
response = MHD_create_response_for_upgrade (&upgrade_handler,
|
|
||||||
NULL);
|
|
||||||
MHD_add_response_header (response,
|
|
||||||
MHD_HTTP_HEADER_UPGRADE,
|
|
||||||
"websocket");
|
|
||||||
MHD_add_response_header (response,
|
|
||||||
MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
|
|
||||||
sec_websocket_accept);
|
|
||||||
ret = MHD_queue_response (connection,
|
|
||||||
MHD_HTTP_SWITCHING_PROTOCOLS,
|
|
||||||
response);
|
|
||||||
MHD_destroy_response (response);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* return error page */
|
|
||||||
struct MHD_Response *response = MHD_create_response_from_buffer (
|
|
||||||
strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
|
|
||||||
PAGE_INVALID_WEBSOCKET_REQUEST,
|
|
||||||
MHD_RESPMEM_PERSISTENT);
|
|
||||||
ret = MHD_queue_response (connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
response);
|
|
||||||
MHD_destroy_response (response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct MHD_Response *response = MHD_create_response_from_buffer (
|
|
||||||
strlen (PAGE_NOT_FOUND),
|
|
||||||
PAGE_NOT_FOUND,
|
|
||||||
MHD_RESPMEM_PERSISTENT);
|
|
||||||
ret = MHD_queue_response (connection,
|
|
||||||
MHD_HTTP_NOT_FOUND,
|
|
||||||
response);
|
|
||||||
MHD_destroy_response (response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc,
|
|
||||||
char *const *argv)
|
|
||||||
{
|
|
||||||
(void) argc; /* Unused. Silent compiler warning. */
|
|
||||||
(void) argv; /* Unused. Silent compiler warning. */
|
|
||||||
struct MHD_Daemon *daemon;
|
|
||||||
|
|
||||||
daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
|
|
||||||
| MHD_USE_THREAD_PER_CONNECTION
|
|
||||||
| MHD_ALLOW_UPGRADE
|
|
||||||
| MHD_USE_ERROR_LOG,
|
|
||||||
PORT, NULL, NULL,
|
|
||||||
&access_handler, NULL,
|
|
||||||
MHD_OPTION_END);
|
|
||||||
|
|
||||||
if (NULL == daemon)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "Error (Couldn't start daemon for testing)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf ("The server is listening now.\n");
|
|
||||||
printf ("Access the server now with a websocket-capable webbrowser.\n\n");
|
|
||||||
printf ("Press return to close.\n");
|
|
||||||
|
|
||||||
(void) getc (stdin);
|
|
||||||
|
|
||||||
MHD_stop_daemon (daemon);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user