summaryrefslogtreecommitdiff
path: root/src/microhttpd_ws/mhd_websocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd_ws/mhd_websocket.c')
-rw-r--r--src/microhttpd_ws/mhd_websocket.c587
1 files changed, 478 insertions, 109 deletions
diff --git a/src/microhttpd_ws/mhd_websocket.c b/src/microhttpd_ws/mhd_websocket.c
index ea1a5bda..da787f7d 100644
--- a/src/microhttpd_ws/mhd_websocket.c
+++ b/src/microhttpd_ws/mhd_websocket.c
@@ -36,6 +36,10 @@ struct MHD_WebSocketStream
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. */
@@ -122,13 +126,6 @@ enum MHD_WebSocket_UTF8Result
MHD_WebSocket_UTF8Result_Incomplete = 2
};
-#define htonll(x) \
- ((1 == htonl (1)) ? (x) : ((uint64_t) htonl ((x) & 0xFFFFFFFF) << 32) \
- | htonl ((x) >> 32))
-#define ntohll(x) \
- ((1 == ntohl (1)) ? (x) : ((uint64_t) ntohl ((x) & 0xFFFFFFFF) << 32) \
- | ntohl ((x) >> 32))
-
static void
MHD_websocket_copy_payload (char*dst,
const char*src,
@@ -142,12 +139,12 @@ MHD_websocket_check_utf8 (const char*buf,
int*utf8_step,
size_t*buf_offset);
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
char**payload,
size_t*payload_len);
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
char**payload,
size_t*payload_len);
@@ -158,7 +155,7 @@ static char
MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws,
size_t payload_len);
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -167,7 +164,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
size_t*frame_len,
char opcode);
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -176,35 +173,338 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
char opcode);
static uint32_t
-MHD_websocket_generate_mask ();
+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 int
-MHD_websocket_create_accept (const char*sec_websocket_key,
- char*sec_websocket_accept)
+_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_key) ||
- (NULL == sec_websocket_accept) )
+ 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;
- sha1_init_ctx (&ctx);
- sha1_process_bytes (sec_websocket_key, length, &ctx);
- sha1_process_bytes (suffix, 36, &ctx);
- sha1_finish_ctx (&ctx, sha1);
+ 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, */
@@ -234,7 +534,7 @@ MHD_websocket_create_accept (const char*sec_websocket_key,
/**
* Initializes a new websocket stream
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
int flags,
size_t max_payload_size)
@@ -244,7 +544,9 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
max_payload_size,
malloc,
realloc,
- free);
+ free,
+ NULL,
+ NULL);
}
@@ -252,13 +554,15 @@ MHD_websocket_stream_init (struct MHD_WebSocketStream**ws,
* Initializes a new websocket stream with
* additional parameters for allocation functions
*/
-_MHD_EXTERN int
+_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)
+ MHD_WebSocketFreeCallback callback_free,
+ void* cls_rng,
+ MHD_WebSocketRandomNumberGenerator callback_rng)
{
/* initialize output variables for errors cases */
if (NULL != ws)
@@ -270,7 +574,9 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) ||
(NULL == callback_malloc) ||
(NULL == callback_realloc) ||
- (NULL == callback_free) )
+ (NULL == callback_free) ||
+ ((0 != (flags & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+ (NULL == callback_rng)))
{
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
}
@@ -288,6 +594,8 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
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 */
@@ -300,7 +608,7 @@ MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws,
/**
* Frees a previously allocated websocket stream
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
{
/* validate parameters */
@@ -323,7 +631,7 @@ MHD_websocket_stream_free (struct MHD_WebSocketStream*ws)
/**
* Invalidates a websocket stream (no more decoding possible)
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws)
{
/* validate parameters */
@@ -340,7 +648,7 @@ MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws)
/**
* Returns whether a websocket stream is valid
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_VALIDITY
MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws)
{
/* validate parameters */
@@ -354,7 +662,7 @@ MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws)
/**
* Decodes incoming data to a websocket frame
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_decode (struct MHD_WebSocketStream*ws,
const char*streambuf,
size_t streambuf_len,
@@ -372,7 +680,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
/* validate parameters */
if ((NULL == ws) ||
- (NULL == streambuf) && (0 != streambuf_len) ||
+ ((NULL == streambuf) && (0 != streambuf_len)) ||
(NULL == streambuf_read_len) ||
(NULL == payload) ||
(NULL == payload_len) )
@@ -657,8 +965,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
else
{
size_t size = (size_t) frame_len;
- if ((SIZE_MAX < size) || ws->max_payload_size &&
- (ws->max_payload_size < size) )
+ 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;
@@ -713,8 +1021,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
case MHD_WebSocket_DecodeStep_Length2of2:
{
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
- size_t size = (size_t) htons (*((unsigned
- short*) &ws->frame_header [2]));
+ 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 */
@@ -733,8 +1040,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
*streambuf_read_len = current;
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
}
- if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size <
- size) )
+ 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 */
@@ -771,7 +1078,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
case MHD_WebSocket_DecodeStep_Length8of8:
{
ws->frame_header [ws->frame_header_size++] = streambuf [current++];
- uint64_t size = htonll (*((uint64_t*) &ws->frame_header [2]));
+ 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 */
@@ -809,8 +1116,8 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
*streambuf_read_len = current;
return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR;
}
- if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size <
- size) )
+ 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 */
@@ -893,13 +1200,13 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
& 0x03));
current += bytes_to_take;
ws->payload_index += bytes_to_take;
- if ((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
+ if (((MHD_WebSocket_DecodeStep_PayloadOfDataFrame ==
ws->decode_step) &&
- (MHD_WebSocket_Opcode_Text == ws->data_type) ||
- (MHD_WebSocket_DecodeStep_PayloadOfControlFrame ==
+ (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) )
+ (2 < ws->payload_index)) )
{
/* RFC 6455 8.1: We need to check the UTF-8 validity */
int utf8_step;
@@ -1016,7 +1323,7 @@ MHD_websocket_decode (struct MHD_WebSocketStream*ws,
}
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
char**payload,
size_t*payload_len)
@@ -1119,13 +1426,15 @@ MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws,
}
-static int
+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_fin = ws->frame_header [0] & 0x80;
+ 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 */
@@ -1134,9 +1443,9 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
/* data frame */
char data_type = ws->data_type;
if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) &&
- (MHD_WebSocket_Opcode_Continuation == (ws->frame_header [0] & 0x0F)))
+ (0 != is_continue))
{
- data_type |= 0x20; /* mark as last fragment */
+ data_type |= 0x40; /* mark as last fragment */
}
*payload = ws->data_payload;
*payload_len = ws->data_payload_size;
@@ -1170,7 +1479,7 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
{
/* the last UTF-8 sequence is incomplete, so we keep the start of
that and only return the part before */
- size_t given_utf8;
+ size_t given_utf8 = 0;
switch (ws->data_utf8_step)
{
/* one byte given */
@@ -1220,7 +1529,10 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
ws->payload_index = 0;
ws->frame_header_size = 0;
- return ws->data_type | 0x10; /* mark as fragment */
+ if (0 != is_continue)
+ return ws->data_type | 0x20; /* mark as middle fragment */
+ else
+ return ws->data_type | 0x10; /* mark as first fragment */
}
else
{
@@ -1233,7 +1545,10 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
ws->decode_step = MHD_WebSocket_DecodeStep_Start;
ws->payload_index = 0;
ws->frame_header_size = 0;
- return ws->data_type | 0x10; /* mark as fragment */
+ if (0 != is_continue)
+ return ws->data_type | 0x20; /* mark as middle fragment */
+ else
+ return ws->data_type | 0x10; /* mark as first fragment */
}
}
else
@@ -1251,7 +1566,7 @@ MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws,
/**
* Splits the received close reason
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_split_close_reason (const char*payload,
size_t payload_len,
unsigned short*reason_code,
@@ -1283,7 +1598,7 @@ MHD_websocket_split_close_reason (const char*payload,
else
{
if (NULL != reason_code)
- *reason_code = htons (*((unsigned short*) payload));
+ *reason_code = MHD_htons (*((uint16_t*) payload));
}
/* decode reason text */
@@ -1309,7 +1624,7 @@ MHD_websocket_split_close_reason (const char*payload,
/**
* Encodes a text into a websocket text frame
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
const char*payload_utf8,
size_t payload_utf8_len,
@@ -1333,13 +1648,13 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
/* validate parameters */
if ((NULL == ws) ||
- (0 != payload_utf8_len) && (NULL == payload_utf8) ||
+ ((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) )
+ ((MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) &&
+ (NULL == utf8_step)) )
{
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
}
@@ -1356,8 +1671,8 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
utf8_step,
NULL);
if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) ||
- (MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
- (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) )
+ ((MHD_WebSocket_UTF8Result_Incomplete == utf8_result) &&
+ (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation)) )
{
return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR;
}
@@ -1376,7 +1691,7 @@ MHD_websocket_encode_text (struct MHD_WebSocketStream*ws,
/**
* Encodes binary data into a websocket binary frame
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -1392,7 +1707,7 @@ MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
/* validate parameters */
if ((NULL == ws) ||
- (0 != payload_len) && (NULL == payload) ||
+ ((0 != payload_len) && (NULL == payload)) ||
(NULL == frame) ||
(NULL == frame_len) ||
(MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) ||
@@ -1420,7 +1735,7 @@ MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws,
/**
* Internal function for encoding text/binary data into a websocket frame
*/
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -1433,7 +1748,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
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 () : 0;
+ uint32_t mask = 0 != is_masked ? MHD_websocket_generate_mask (ws) : 0;
/* allocate memory */
char*result = ws->malloc (total_len + 1);
@@ -1468,13 +1783,13 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
else if (65536 > payload_len)
{
*(result++) = is_masked | 126;
- *((unsigned short *) result) = htons ((unsigned short) payload_len);
+ *((uint16_t *) result) = MHD_htons ((uint16_t) payload_len);
result += 2;
}
else
{
*(result++) = is_masked | 127;
- *((uint64_t *) result) = htonll ((uint64_t) payload_len);
+ *((uint64_t *) result) = MHD_htonll ((uint64_t) payload_len);
result += 8;
}
@@ -1505,7 +1820,7 @@ MHD_websocket_encode_data (struct MHD_WebSocketStream*ws,
/**
* Encodes a websocket ping frame
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -1525,7 +1840,7 @@ MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws,
/**
* Encodes a websocket pong frame
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -1545,7 +1860,7 @@ MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws,
/**
* Internal function for encoding ping/pong frames
*/
-static int
+static enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
const char*payload,
size_t payload_len,
@@ -1561,7 +1876,7 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
/* validate the parameters */
if ((NULL == ws) ||
- (0 != payload_len) && (NULL == payload) ||
+ ((0 != payload_len) && (NULL == payload)) ||
(NULL == frame) ||
(NULL == frame_len) )
{
@@ -1576,7 +1891,7 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
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 () : 0;
+ uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
/* allocate memory */
char*result = ws->malloc (total_len + 1);
@@ -1618,7 +1933,7 @@ MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws,
/**
* Encodes a websocket close frame
*/
-_MHD_EXTERN int
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
unsigned short reason_code,
const char*reason_utf8,
@@ -1634,13 +1949,13 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
/* validate the parameters */
if ((NULL == ws) ||
- (0 != reason_utf8_len) && (NULL == reason_utf8) ||
+ ((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) )
+ ((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;
}
@@ -1668,7 +1983,7 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
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 () : 0;
+ uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 0;
/* allocate memory */
char*result = ws->malloc (total_len + 1);
@@ -1697,7 +2012,7 @@ MHD_websocket_encode_close (struct MHD_WebSocketStream*ws,
if (0 != reason_code)
{
/* close reason code */
- unsigned short reason_code_nb = htons (reason_code);
+ uint16_t reason_code_nb = MHD_htons (reason_code);
MHD_websocket_copy_payload (result,
(const char*) &reason_code_nb,
2,
@@ -1815,8 +2130,8 @@ MHD_websocket_check_utf8 (const char*buf,
/* 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) )
+ 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;
@@ -1991,30 +2306,32 @@ MHD_websocket_check_utf8 (const char*buf,
/**
- * Calls srand in the scope of MHD to set the seed
- * for the random number generator used for masking.
- */
-_MHD_EXTERN int
-MHD_websocket_srand (unsigned long seed)
-{
- srand (seed);
-
- return MHD_WEBSOCKET_STATUS_OK;
-}
-
-
-/**
* Generates a mask for masking by calling
* a random number generator.
*/
static uint32_t
-MHD_websocket_generate_mask ()
+MHD_websocket_generate_mask (struct MHD_WebSocketStream*ws)
{
unsigned char mask_[4];
- mask_ [0] = (unsigned char) (rand () & 0xFF);
- mask_ [1] = (unsigned char) (rand () & 0xFF);
- mask_ [2] = (unsigned char) (rand () & 0xFF);
- mask_ [3] = (unsigned char) (rand () & 0xFF);
+ 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_);
}
@@ -2024,15 +2341,15 @@ MHD_websocket_generate_mask ()
* Calls the malloc function associated with the websocket steam
*/
_MHD_EXTERN void*
-MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
- size_t len)
+MHD_websocket_malloc (struct MHD_WebSocketStream* ws,
+ size_t buf_len)
{
if (NULL == ws)
{
return NULL;
}
- return ws->malloc (len);
+ return ws->malloc (buf_len);
}
@@ -2040,16 +2357,16 @@ MHD_websocket_malloc (struct MHD_WebSocketStream*ws,
* Calls the realloc function associated with the websocket steam
*/
_MHD_EXTERN void*
-MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
- void*cls,
- size_t len)
+MHD_websocket_realloc (struct MHD_WebSocketStream* ws,
+ void* buf,
+ size_t new_buf_len)
{
if (NULL == ws)
{
return NULL;
}
- return ws->realloc (cls, len);
+ return ws->realloc (buf, new_buf_len);
}
@@ -2057,15 +2374,67 @@ MHD_websocket_realloc (struct MHD_WebSocketStream*ws,
* Calls the free function associated with the websocket steam
*/
_MHD_EXTERN int
-MHD_websocket_free (struct MHD_WebSocketStream*ws,
- void*cls)
+MHD_websocket_free (struct MHD_WebSocketStream* ws,
+ void* buf)
{
if (NULL == ws)
{
return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
}
- ws->free (cls);
+ 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;
+ }
+}