From 5d9d6a87124a5439d3432c37a67f9b2babe04407 Mon Sep 17 00:00:00 2001 From: Andreas Weigel Date: Mon, 27 Feb 2017 08:45:32 +0100 Subject: [PATCH] add decode support for continuation frames use FIN bit and implement opcode 0x00 make consistent use of uint64_t for big frame sizes --- libvncserver/websockets.c | 3 +- libvncserver/ws_decode.c | 137 ++++++++++++++++++++++++++++++-------- libvncserver/ws_decode.h | 32 +++++---- test/wsmaketestframe.py | 15 +++-- test/wstest.c | 29 ++++---- test/wstestdata.inc | 54 ++++++++++++--- 6 files changed, 196 insertions(+), 74 deletions(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 73ad81c..921015d 100644 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -339,12 +339,11 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) free(buf); wsctx = calloc(1, sizeof(ws_ctx_t)); - wsctx->version = WEBSOCKETS_VERSION_HYBI; wsctx->encode = webSocketsEncodeHybi; wsctx->decode = webSocketsDecodeHybi; wsctx->ctxInfo.readFunc = ws_read; wsctx->base64 = base64; - hybiDecodeCleanup(wsctx); + hybiDecodeCleanupComplete(wsctx); cl->wsctx = (wsCtx *)wsctx; return TRUE; } diff --git a/libvncserver/ws_decode.c b/libvncserver/ws_decode.c index 472a44a..485478d 100644 --- a/libvncserver/ws_decode.c +++ b/libvncserver/ws_decode.c @@ -8,17 +8,27 @@ #define WS_HYBI_HEADER_LEN_EXTENDED 4 + WS_HYBI_MASK_LEN #define WS_HYBI_HEADER_LEN_LONG 10 + WS_HYBI_MASK_LEN -static int +static inline int +isControlFrame(ws_ctx_t *wsctx) +{ + return 0 != (wsctx->header.opcode & 0x08); +} + +static uint64_t hybiRemaining(ws_ctx_t *wsctx) { return wsctx->nToRead - wsctx->nReadRaw; } -void -hybiDecodeCleanup(ws_ctx_t *wsctx) +static void +hybiDecodeCleanupBasics(ws_ctx_t *wsctx) { + /* keep opcode, cleanup rest */ + wsctx->header.opcode = WS_OPCODE_INVALID; wsctx->header.payloadLen = 0; wsctx->header.mask.u = 0; + wsctx->header.headerLen = 0; + wsctx->header.data = NULL; wsctx->nReadRaw = 0; wsctx->nToRead= 0; wsctx->carrylen = 0; @@ -26,9 +36,24 @@ hybiDecodeCleanup(ws_ctx_t *wsctx) wsctx->readlen = 0; wsctx->hybiDecodeState = WS_HYBI_STATE_HEADER_PENDING; wsctx->writePos = NULL; - rfbLog("cleaned up wsctx\n"); } +static void +hybiDecodeCleanupForContinuation(ws_ctx_t *wsctx) +{ + hybiDecodeCleanupBasics(wsctx); + rfbLog("clean up frame, but expect continuation with opcode %d\n", wsctx->continuation_opcode); +} + +void +hybiDecodeCleanupComplete(ws_ctx_t *wsctx) +{ + hybiDecodeCleanupBasics(wsctx); + wsctx->continuation_opcode = WS_OPCODE_INVALID; + rfbLog("cleaned up wsctx completely\n"); +} + + /** * Return payload data that has been decoded/unmasked from * a websocket frame. @@ -94,10 +119,9 @@ hybiReadHeader(ws_ctx_t *wsctx, int *sockRet, int *nPayload) { int ret; char *headerDst = wsctx->codeBufDecode + wsctx->nReadRaw; - int n = WSHLENMAX - wsctx->nReadRaw; + int n = ((uint64_t)WSHLENMAX) - wsctx->nReadRaw; rfbLog("header_read to %p with len=%d\n", headerDst, n); - //ret = ws_read(cl, headerDst, n); ret = wsctx->ctxInfo.readFunc(wsctx->ctxInfo.ctxPtr, headerDst, n); rfbLog("read %d bytes from socket\n", ret); if (ret <= 0) { @@ -106,29 +130,65 @@ hybiReadHeader(ws_ctx_t *wsctx, int *sockRet, int *nPayload) int olderrno = errno; rfbErr("%s: read; %s\n", __func__, strerror(errno)); errno = olderrno; - *sockRet = -1; + goto err_cleanup_state; } else { *sockRet = 0; + goto err_cleanup_state_sock_closed; } - return WS_HYBI_STATE_ERR; } wsctx->nReadRaw += ret; if (wsctx->nReadRaw < 2) { /* cannot decode header with less than two bytes */ - errno = EAGAIN; - *sockRet = -1; - return WS_HYBI_STATE_HEADER_PENDING; + goto ret_header_pending; } /* first two header bytes received; interpret header data and get rest */ wsctx->header.data = (ws_header_t *)wsctx->codeBufDecode; wsctx->header.opcode = wsctx->header.data->b0 & 0x0f; + wsctx->header.fin = (wsctx->header.data->b0 & 0x80) >> 7; + if (isControlFrame(wsctx)) { + rfbLog("is control frame\n"); + /* is a control frame, leave remembered continuation opcode unchanged; + * just check if there is a wrong fragmentation */ + if (wsctx->header.fin == 0) { + + /* we only accept text/binary continuation frames; RFC6455: + * Control frames (see Section 5.5) MAY be injected in the middle of + * a fragmented message. Control frames themselves MUST NOT be + * fragmented. */ + rfbErr("control frame with FIN bit cleared received, aborting\n"); + errno = EPROTO; + goto err_cleanup_state; + } + } else { + rfbLog("not a control frame\n"); + /* not a control frame, check for continuation opcode */ + if (wsctx->header.opcode == WS_OPCODE_CONTINUATION) { + rfbLog("cont_frame\n"); + /* do we have state (i.e., opcode) for continuation frame? */ + if (wsctx->continuation_opcode == WS_OPCODE_INVALID) { + rfbErr("no continuation state\n"); + errno = EPROTO; + goto err_cleanup_state; + } - /* fin = (header->b0 & 0x80) >> 7; */ /* not used atm */ - wsctx->header.payloadLen = wsctx->header.data->b1 & 0x7f; - rfbLog("first header bytes received; opcode=%d lenbyte=%d\n", wsctx->header.opcode, wsctx->header.payloadLen); + /* otherwise, set opcode = continuation_opcode */ + wsctx->header.opcode = wsctx->continuation_opcode; + rfbLog("set opcode to continuation_opcode: %d\n", wsctx->header.opcode); + } else { + if (wsctx->header.fin == 0) { + wsctx->continuation_opcode = wsctx->header.opcode; + } else { + wsctx->continuation_opcode = WS_OPCODE_INVALID; + } + rfbLog("set continuation_opcode to %d\n", wsctx->continuation_opcode); + } + } + + wsctx->header.payloadLen = (uint64_t)(wsctx->header.data->b1 & 0x7f); + rfbLog("first header bytes received; opcode=%d lenbyte=%d fin=%d\n", wsctx->header.opcode, wsctx->header.payloadLen, wsctx->header.fin); /* * 4.3. Client-to-Server Masking @@ -139,8 +199,7 @@ hybiReadHeader(ws_ctx_t *wsctx, int *sockRet, int *nPayload) if (!(wsctx->header.data->b1 & 0x80)) { rfbErr("%s: got frame without mask; ret=%d\n", __func__, ret); errno = EPROTO; - *sockRet = -1; - return WS_HYBI_STATE_ERR; + goto err_cleanup_state; } @@ -158,22 +217,27 @@ hybiReadHeader(ws_ctx_t *wsctx, int *sockRet, int *nPayload) } else { /* Incomplete frame header, try again */ rfbErr("%s: incomplete frame header; ret=%d\n", __func__, ret); - errno = EAGAIN; - *sockRet = -1; - return WS_HYBI_STATE_HEADER_PENDING; + goto ret_header_pending; } + char *h = wsctx->codeBufDecode; + int i; + rfbLog("Header:\n"); + for (i=0; i <10; i++) { + rfbLog("0x%02X\n", (unsigned char)h[i]); + } + rfbLog("\n"); + /* while RFC 6455 mandates that lengths MUST be encoded with the minimum * number of bytes, it does not specify for the server how to react on * 'wrongly' encoded frames --- this implementation rejects them*/ if ((wsctx->header.headerLen > WS_HYBI_HEADER_LEN_SHORT - && wsctx->header.payloadLen < 126) + && wsctx->header.payloadLen < (uint64_t)126) || (wsctx->header.headerLen > WS_HYBI_HEADER_LEN_EXTENDED - && wsctx->header.payloadLen < 65536)) { + && wsctx->header.payloadLen < (uint64_t)65536)) { rfbErr("%s: invalid length field; headerLen=%d payloadLen=%llu\n", __func__, wsctx->header.headerLen, wsctx->header.payloadLen); errno = EPROTO; - *sockRet = -1; - return WS_HYBI_STATE_ERR; + goto err_cleanup_state; } /* absolute length of frame */ @@ -186,9 +250,20 @@ hybiReadHeader(ws_ctx_t *wsctx, int *sockRet, int *nPayload) wsctx->readPos = (unsigned char *)(wsctx->codeBufDecode + wsctx->header.headerLen); *nPayload = wsctx->nReadRaw - wsctx->header.headerLen; - rfbLog("header complete: state=%d flen=%d writeTo=%p nPayload=%d\n", wsctx->hybiDecodeState, wsctx->nToRead, wsctx->writePos, *nPayload); + rfbLog("header complete: state=%d flen=%llu writeTo=%p nPayload=%d\n", wsctx->hybiDecodeState, wsctx->nToRead, wsctx->writePos, *nPayload); return WS_HYBI_STATE_DATA_NEEDED; + +ret_header_pending: + errno = EAGAIN; + *sockRet = -1; + return WS_HYBI_STATE_HEADER_PENDING; + +err_cleanup_state: + *sockRet = -1; +err_cleanup_state_sock_closed: + hybiDecodeCleanupComplete(wsctx); + return WS_HYBI_STATE_ERR; } static int @@ -248,6 +323,7 @@ hybiReadAndDecode(ws_ctx_t *wsctx, char *dst, int len, int *sockRet, int nInBuf) /* -1 accounts for potential '\0' terminator for base64 decoding */ bufsize = wsctx->codeBufDecode + ARRAYSIZE(wsctx->codeBufDecode) - wsctx->writePos - 1; + rfbLog("bufsize=%d\n", bufsize); if (hybiRemaining(wsctx) > bufsize) { nextRead = bufsize; } else { @@ -453,11 +529,18 @@ spor: /* rfbLog("%s: ret: %d/%d\n", __func__, result, len); */ if (wsctx->hybiDecodeState == WS_HYBI_STATE_FRAME_COMPLETE) { rfbLog("frame received successfully, cleaning up: read=%d hlen=%d plen=%d\n", wsctx->header.nRead, wsctx->header.headerLen, wsctx->header.payloadLen); - /* frame finished, cleanup state */ - hybiDecodeCleanup(wsctx); + if (wsctx->header.fin && !isControlFrame(wsctx)) { + /* frame finished, cleanup state */ + hybiDecodeCleanupComplete(wsctx); + } else { + /* always retain continuation opcode for unfinished data frames + * or control frames, which may interleave with data frames */ + hybiDecodeCleanupForContinuation(wsctx); + } } else if (wsctx->hybiDecodeState == WS_HYBI_STATE_ERR) { - hybiDecodeCleanup(wsctx); + hybiDecodeCleanupComplete(wsctx); } + rfbLog("%s_exit: len=%d; " "CTX: readlen=%d readPos=%p " "writePos=%p " diff --git a/libvncserver/ws_decode.h b/libvncserver/ws_decode.h index 0dcbc83..07d37bd 100644 --- a/libvncserver/ws_decode.h +++ b/libvncserver/ws_decode.h @@ -27,16 +27,11 @@ #endif #define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) -#define WSHLENMAX 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */ +#define WSHLENMAX 14LL /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */ #define WS_HYBI_MASK_LEN 4 #define ARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))) / (size_t)(!(sizeof(a) % sizeof((a[0]))))) -enum { - WEBSOCKETS_VERSION_HIXIE, - WEBSOCKETS_VERSION_HYBI -}; - struct ws_ctx_s; typedef struct ws_ctx_s ws_ctx_t; @@ -111,9 +106,11 @@ typedef struct ws_header_data_s { /** length of frame header including payload len, but without mask */ int headerLen; /** length of the payload data */ - int payloadLen; + uint64_t payloadLen; /** opcode */ unsigned char opcode; + /** fin bit */ + unsigned char fin; } ws_header_data_t; typedef struct ws_ctx_s { @@ -125,11 +122,11 @@ typedef struct ws_ctx_s { int hybiDecodeState; char carryBuf[3]; /* For base64 carry-over */ int carrylen; - int version; int base64; ws_header_data_t header; - int nReadRaw; - int nToRead; + uint64_t nReadRaw; + uint64_t nToRead; + unsigned char continuation_opcode; wsEncodeFunc encode; wsDecodeFunc decode; ctxInfo_t ctxInfo; @@ -137,15 +134,16 @@ typedef struct ws_ctx_s { enum { - WS_OPCODE_CONTINUATION = 0x0, - WS_OPCODE_TEXT_FRAME, - WS_OPCODE_BINARY_FRAME, - WS_OPCODE_CLOSE = 0x8, - WS_OPCODE_PING, - WS_OPCODE_PONG + WS_OPCODE_CONTINUATION = 0x00, + WS_OPCODE_TEXT_FRAME = 0x01, + WS_OPCODE_BINARY_FRAME = 0x02, + WS_OPCODE_CLOSE = 0x08, + WS_OPCODE_PING = 0x09, + WS_OPCODE_PONG = 0x0A, + WS_OPCODE_INVALID = 0xFF }; int webSocketsDecodeHybi(ws_ctx_t *wsctx, char *dst, int len); -void hybiDecodeCleanup(ws_ctx_t *wsctx); +void hybiDecodeCleanupComplete(ws_ctx_t *wsctx); #endif diff --git a/test/wsmaketestframe.py b/test/wsmaketestframe.py index 1d4d24d..fc03e39 100755 --- a/test/wsmaketestframe.py +++ b/test/wsmaketestframe.py @@ -42,12 +42,12 @@ def add_field(s, name, value, first=False): class Testframe(): - def __init__(self, frame, descr, modify_bytes={}, experrno=0, mask=True): + def __init__(self, frame, descr, modify_bytes={}, experrno=0, mask=True, opcode_overwrite=False): self.frame = frame self.descr = descr self.modify_bytes = modify_bytes self.experrno = experrno - self.b64 = True if frame.opcode == 1 else False + self.b64 = True if frame.opcode == 1 or opcode_overwrite == 1 else False self.mask = mask def to_carray_initializer(self, buf): @@ -110,7 +110,14 @@ flist.append(Testframe(websockets.framing.Frame(1, 8, bytearray(list([0x03, 0xEB ### invalid header values flist.append(Testframe(websockets.framing.Frame(1, 1, bytearray("Testit", encoding="utf-8")), "Invalid frame: Wrong masking", experrno="EPROTO", mask=False)) flist.append(Testframe(websockets.framing.Frame(1, 1, bytearray("..Lore Ipsum", encoding="utf-8")), "Invalid frame: Length of < 126 with add. 16 bit len field", experrno="EPROTO", modify_bytes={ 1: 0xFE, 2: 0x00, 3: 0x0F})) -flist.append(Testframe(websockets.framing.Frame(1, 1, bytearray("........Lore Ipsum", encoding="utf-8")), "Invalid frame: Length of < 126 with add. 64 bit len field", experrno="EPROTO", modify_bytes={ 1: 0xFF, 2: 0x00, 3: 0x00, 4: 0x00, 5: 0x00, 6: 0x80, 7: 0x40})) +flist.append(Testframe(websockets.framing.Frame(1, 1, bytearray("........Lore Ipsum", encoding="utf-8")), "Invalid frame: Length of < 126 with add. 64 bit len field", experrno="EPROTO", modify_bytes={ 1: 0xFF, 2: 0x00, 3: 0x00, 4: 0x00, 5: 0x00, 6: 0x00, 7: 0x00, 8: 0x80, 9: 0x40})) + +frag1 = websockets.framing.Frame(0, 1, bytearray("This is a fragmented websocket...", encoding="utf-8")) +frag2 = websockets.framing.Frame(0, 0, bytearray("... and it goes on...", encoding="utf-8")) +frag3 = websockets.framing.Frame(1, 0, bytearray("and on and stop", encoding="utf-8")) +flist.append(Testframe(frag1, "Continuation test frag1")) +flist.append(Testframe(frag2, "Continuation test frag2", opcode_overwrite=1)) +flist.append(Testframe(frag3, "Continuation test frag3", opcode_overwrite=1)) s = "struct ws_frame_test tests[] = {\n" for i in range(len(flist)): @@ -120,5 +127,5 @@ for i in range(len(flist)): s += "\n" s += "};\n" -with open("wstestdata.c", "w") as cdatafile: +with open("wstestdata.inc", "w") as cdatafile: cdatafile.write(s) diff --git a/test/wstest.c b/test/wstest.c index 69cd174..042b75b 100644 --- a/test/wstest.c +++ b/test/wstest.c @@ -98,10 +98,10 @@ static int emu_read(void *ctx, char *dst, size_t len) rfbLog("emu_read called with dst=%p and len=%lu\n", dst, len); if (ft->simulate_sock_malfunction_at > 0 && ft->simulate_sock_malfunction_at == ft->i) { rfbLog("simulating IO error with errno=%d\n", ft->errno_val); - errno = ft->errno_val; + errno = ft->errno_val; return -1; } - + /* return something */ r = rand(); modu = (ft->frame + ft->frame_len) - ft->pos; @@ -130,7 +130,7 @@ static uint64_t run_test(struct ws_frame_test *ft, ws_ctx_t *ctx) while (nleft > 0) { rfbLog("calling ws_decode with dst=%p, len=%lu\n", dst, nleft); n = ctx->decode(ctx, dst, nleft); - rfbLog("read n=%ld\n", n); + rfbLog("read n=%ld\n", n); if (n == 0) { if (ft->close_sock_at > 0) { return OK; @@ -155,7 +155,7 @@ static uint64_t run_test(struct ws_frame_test *ft, ws_ctx_t *ctx) rfbLog("read n=%ld from decode; dst=%p, nleft=%lu\n", n, dst, nleft); } } - + if (memcmp(ft->expectedDecodeBuf, dstbuf, ft->raw_payload_len) != 0) { ft->expectedDecodeBuf[ft->raw_payload_len] = '\0'; dstbuf[ft->raw_payload_len] = '\0'; @@ -168,24 +168,23 @@ static uint64_t run_test(struct ws_frame_test *ft, ws_ctx_t *ctx) int main() -{ +{ ws_ctx_t ctx; int retall= 0; int i; - srand(RND_SEED); + srand(RND_SEED); + hybiDecodeCleanupComplete(&ctx); + ctx.decode = webSocketsDecodeHybi; + ctx.ctxInfo.readFunc = emu_read; + rfbLog = logtest; + rfbErr = logtest; + for (i = 0; i < ARRAYSIZE(tests); i++) { int ret; + /* reset output log buffer to begin */ el_pos = el_log; - rfbLog = logtest; - rfbErr = logtest; - - hybiDecodeCleanup(&ctx); - ctx.decode = webSocketsDecodeHybi; - ctx.version = WEBSOCKETS_VERSION_HYBI; - - ctx.ctxInfo.readFunc = emu_read; ret = run_test(&tests[i], &ctx); printf("%s: \"%s\"\n", ret == 0 ? "PASS" : "FAIL", tests[i].descr); @@ -198,7 +197,7 @@ int main() return retall; } -#else +#else int main() { return 0; diff --git a/test/wstestdata.inc b/test/wstestdata.inc index 9dc919e..595b891 100644 --- a/test/wstestdata.inc +++ b/test/wstestdata.inc @@ -1,6 +1,6 @@ struct ws_frame_test tests[] = { { - .frame={0X81,0X88,0XB7,0XDB,0X16,0X16,0XE1,0X9C,0X40,0X6C,0XD3,0X9C,0X7A,0X26}, + .frame={0X81,0X88,0X2F,0X2A,0X17,0X41,0X79,0X6D,0X41,0X3B,0X4B,0X6D,0X7B,0X71}, .expectedDecodeBuf={0X54,0X65,0X73,0X74,0X69,0X74}, .frame_len=14, .raw_payload_len=6, @@ -12,7 +12,7 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X81,0XFE,0X00,0XD4,0X67,0XFE,0X8A,0X31,0X35,0X90,0XC0,0X59,0X05,0XA9,0XDF,0X48,0X2E,0XB9,0XD8,0X47,0X3D,0XA6,0XC7,0X56,0X3E,0XCC,0XB3,0X44,0X03,0XB9,0XCC,0X41,0X05,0X97,0XC8,0X45,0X03,0XA9,0XC4,0X5E,0X2E,0XB9,0XBB,0X47,0X04,0X93,0XDF,0X56,0X03,0XB9,0XDC,0X05,0X03,0XBD,0XC8,0X59,0X05,0X93,0XDB,0X56,0X3D,0XA6,0XD0,0X5D,0X05,0X97,0XC8,0X5F,0X05,0XCC,0XDC,0X4B,0X2E,0XB9,0XC0,0X5D,0X02,0XA9,0XB3,0X44,0X3D,0XBD,0XC8,0X01,0X06,0XB9,0XDF,0X56,0X2A,0XAA,0XC3,0X03,0X2E,0XB9,0XC0,0X04,0X03,0XB9,0XDF,0X56,0X05,0XB9,0XDC,0X44,0X2E,0XB9,0XD0,0X41,0X3D,0XA9,0XF2,0X5A,0X2B,0X97,0XC8,0X76,0X04,0X93,0XCC,0X45,0X3D,0XAA,0XC3,0X56,0X3D,0XB9,0XB3,0X5D,0X04,0X87,0XC8,0X5B,0X05,0XCC,0XBF,0X01,0X3E,0XA9,0XE6,0X44,0X2E,0XB9,0XBB,0X00,0X3E,0XCC,0XED,0X56,0X05,0XA9,0XB3,0X48,0X3D,0XAD,0XC8,0X01,0X3D,0XA6,0XE2,0X01,0X2E,0XB9,0XCC,0X44,0X3D,0XBD,0XC8,0X5D,0X03,0X93,0XDC,0X44,0X2E,0XB9,0XEE,0X47,0X3D,0XA6,0XC7,0X56,0X3E,0X93,0XDC,0X04,0X05,0XCC,0XBF,0X5A,0X2E,0XB6,0XD8,0X5E,0X3D,0XAD,0XCB,0X49,0X2A,0X94,0XD3,0X56,0X3E,0X90,0XE6,0X01,0X3D,0XAD,0XC8,0X42,0X3D,0XA9,0XBE,0X56,0X3D,0X93,0XE6,0X5D,0X05,0XB9,0XDB,0X44}, + .frame={0X81,0XFE,0X00,0XD4,0X66,0X27,0XE5,0X24,0X34,0X49,0XAF,0X4C,0X04,0X70,0XB0,0X5D,0X2F,0X60,0XB7,0X52,0X3C,0X7F,0XA8,0X43,0X3F,0X15,0XDC,0X51,0X02,0X60,0XA3,0X54,0X04,0X4E,0XA7,0X50,0X02,0X70,0XAB,0X4B,0X2F,0X60,0XD4,0X52,0X05,0X4A,0XB0,0X43,0X02,0X60,0XB3,0X10,0X02,0X64,0XA7,0X4C,0X04,0X4A,0XB4,0X43,0X3C,0X7F,0XBF,0X48,0X04,0X4E,0XA7,0X4A,0X04,0X15,0XB3,0X5E,0X2F,0X60,0XAF,0X48,0X03,0X70,0XDC,0X51,0X3C,0X64,0XA7,0X14,0X07,0X60,0XB0,0X43,0X2B,0X73,0XAC,0X16,0X2F,0X60,0XAF,0X11,0X02,0X60,0XB0,0X43,0X04,0X60,0XB3,0X51,0X2F,0X60,0XBF,0X54,0X3C,0X70,0X9D,0X4F,0X2A,0X4E,0XA7,0X63,0X05,0X4A,0XA3,0X50,0X3C,0X73,0XAC,0X43,0X3C,0X60,0XDC,0X48,0X05,0X5E,0XA7,0X4E,0X04,0X15,0XD0,0X14,0X3F,0X70,0X89,0X51,0X2F,0X60,0XD4,0X15,0X3F,0X15,0X82,0X43,0X04,0X70,0XDC,0X5D,0X3C,0X74,0XA7,0X14,0X3C,0X7F,0X8D,0X14,0X2F,0X60,0XA3,0X51,0X3C,0X64,0XA7,0X48,0X02,0X4A,0XB3,0X51,0X2F,0X60,0X81,0X52,0X3C,0X7F,0XA8,0X43,0X3F,0X4A,0XB3,0X11,0X04,0X15,0XD0,0X4F,0X2F,0X6F,0XB7,0X4B,0X3C,0X74,0XA4,0X5C,0X2B,0X4D,0XBC,0X43,0X3F,0X49,0X89,0X14,0X3C,0X74,0XA7,0X57,0X3C,0X70,0XD1,0X43,0X3C,0X4A,0X89,0X48,0X04,0X60,0XB4,0X51}, .expectedDecodeBuf={0X46,0X72,0X61,0X6D,0X65,0X32,0X20,0X64,0X6F,0X65,0X73,0X20,0X63,0X6F,0X6E,0X74,0X61,0X69,0X6E,0X20,0X6D,0X75,0X63,0X68,0X20,0X6D,0X6F,0X72,0X65,0X20,0X74,0X65,0X78,0X74,0X20,0X61,0X6E,0X64,0X20,0X65,0X76,0X65,0X6E,0X20,0X67,0X6F,0X65,0X73,0X20,0X62,0X65,0X79,0X6F,0X6E,0X64,0X20,0X74,0X68,0X65,0X20,0X31,0X32,0X36,0X20,0X62,0X79,0X74,0X65,0X20,0X6C,0X65,0X6E,0X20,0X66,0X69,0X65,0X6C,0X64,0X2E,0X20,0X46,0X72,0X61,0X6D,0X65,0X32,0X20,0X64,0X6F,0X65,0X73,0X20,0X63,0X6F,0X6E,0X74,0X61,0X69,0X6E,0X20,0X6D,0X75,0X63,0X68,0X20,0X6D,0X6F,0X72,0X65,0X20,0X74,0X65,0X78,0X74,0X20,0X61,0X6E,0X64,0X20,0X65,0X76,0X65,0X6E,0X20,0X67,0X6F,0X65,0X73,0X20,0X62,0X65,0X79,0X6F,0X6E,0X64,0X20,0X74,0X68,0X65,0X20,0X31,0X32,0X36,0X20,0X62,0X79,0X74,0X65,0X20,0X6C,0X65,0X6E,0X20,0X66,0X69,0X65,0X6C,0X64,0X2E}, .frame_len=220, .raw_payload_len=159, @@ -24,7 +24,7 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X82,0X86,0X90,0X5E,0X2B,0X8E,0XC4,0X3B,0X58,0XFA,0XF9,0X2A}, + .frame={0X82,0X86,0XDD,0X9B,0XD8,0X56,0X89,0XFE,0XAB,0X22,0XB4,0XEF}, .expectedDecodeBuf={0X54,0X65,0X73,0X74,0X69,0X74}, .frame_len=12, .raw_payload_len=6, @@ -36,7 +36,7 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X82,0XFE,0X00,0X9F,0X7D,0X97,0X6B,0XA2,0X3B,0XE5,0X0A,0XCF,0X18,0XA5,0X4B,0XC6,0X12,0XF2,0X18,0X82,0X1E,0XF8,0X05,0XD6,0X1C,0XFE,0X05,0X82,0X10,0XE2,0X08,0XCA,0X5D,0XFA,0X04,0XD0,0X18,0XB7,0X1F,0XC7,0X05,0XE3,0X4B,0XC3,0X13,0XF3,0X4B,0XC7,0X0B,0XF2,0X05,0X82,0X1A,0XF8,0X0E,0XD1,0X5D,0XF5,0X0E,0XDB,0X12,0XF9,0X0F,0X82,0X09,0XFF,0X0E,0X82,0X4C,0XA5,0X5D,0X82,0X1F,0XEE,0X1F,0XC7,0X5D,0XFB,0X0E,0XCC,0X5D,0XF1,0X02,0XC7,0X11,0XF3,0X45,0X82,0X3B,0XE5,0X0A,0XCF,0X18,0XA5,0X4B,0XC6,0X12,0XF2,0X18,0X82,0X1E,0XF8,0X05,0XD6,0X1C,0XFE,0X05,0X82,0X10,0XE2,0X08,0XCA,0X5D,0XFA,0X04,0XD0,0X18,0XB7,0X1F,0XC7,0X05,0XE3,0X4B,0XC3,0X13,0XF3,0X4B,0XC7,0X0B,0XF2,0X05,0X82,0X1A,0XF8,0X0E,0XD1,0X5D,0XF5,0X0E,0XDB,0X12,0XF9,0X0F,0X82,0X09,0XFF,0X0E,0X82,0X4C,0XA5,0X5D,0X82,0X1F,0XEE,0X1F,0XC7,0X5D,0XFB,0X0E,0XCC,0X5D,0XF1,0X02,0XC7,0X11,0XF3,0X45}, + .frame={0X82,0XFE,0X00,0X9F,0XB5,0X6E,0X7F,0X4C,0XF3,0X1C,0X1E,0X21,0XD0,0X5C,0X5F,0X28,0XDA,0X0B,0X0C,0X6C,0XD6,0X01,0X11,0X38,0XD4,0X07,0X11,0X6C,0XD8,0X1B,0X1C,0X24,0X95,0X03,0X10,0X3E,0XD0,0X4E,0X0B,0X29,0XCD,0X1A,0X5F,0X2D,0XDB,0X0A,0X5F,0X29,0XC3,0X0B,0X11,0X6C,0XD2,0X01,0X1A,0X3F,0X95,0X0C,0X1A,0X35,0XDA,0X00,0X1B,0X6C,0XC1,0X06,0X1A,0X6C,0X84,0X5C,0X49,0X6C,0XD7,0X17,0X0B,0X29,0X95,0X02,0X1A,0X22,0X95,0X08,0X16,0X29,0XD9,0X0A,0X51,0X6C,0XF3,0X1C,0X1E,0X21,0XD0,0X5C,0X5F,0X28,0XDA,0X0B,0X0C,0X6C,0XD6,0X01,0X11,0X38,0XD4,0X07,0X11,0X6C,0XD8,0X1B,0X1C,0X24,0X95,0X03,0X10,0X3E,0XD0,0X4E,0X0B,0X29,0XCD,0X1A,0X5F,0X2D,0XDB,0X0A,0X5F,0X29,0XC3,0X0B,0X11,0X6C,0XD2,0X01,0X1A,0X3F,0X95,0X0C,0X1A,0X35,0XDA,0X00,0X1B,0X6C,0XC1,0X06,0X1A,0X6C,0X84,0X5C,0X49,0X6C,0XD7,0X17,0X0B,0X29,0X95,0X02,0X1A,0X22,0X95,0X08,0X16,0X29,0XD9,0X0A,0X51}, .expectedDecodeBuf={0X46,0X72,0X61,0X6D,0X65,0X32,0X20,0X64,0X6F,0X65,0X73,0X20,0X63,0X6F,0X6E,0X74,0X61,0X69,0X6E,0X20,0X6D,0X75,0X63,0X68,0X20,0X6D,0X6F,0X72,0X65,0X20,0X74,0X65,0X78,0X74,0X20,0X61,0X6E,0X64,0X20,0X65,0X76,0X65,0X6E,0X20,0X67,0X6F,0X65,0X73,0X20,0X62,0X65,0X79,0X6F,0X6E,0X64,0X20,0X74,0X68,0X65,0X20,0X31,0X32,0X36,0X20,0X62,0X79,0X74,0X65,0X20,0X6C,0X65,0X6E,0X20,0X66,0X69,0X65,0X6C,0X64,0X2E,0X20,0X46,0X72,0X61,0X6D,0X65,0X32,0X20,0X64,0X6F,0X65,0X73,0X20,0X63,0X6F,0X6E,0X74,0X61,0X69,0X6E,0X20,0X6D,0X75,0X63,0X68,0X20,0X6D,0X6F,0X72,0X65,0X20,0X74,0X65,0X78,0X74,0X20,0X61,0X6E,0X64,0X20,0X65,0X76,0X65,0X6E,0X20,0X67,0X6F,0X65,0X73,0X20,0X62,0X65,0X79,0X6F,0X6E,0X64,0X20,0X74,0X68,0X65,0X20,0X31,0X32,0X36,0X20,0X62,0X79,0X74,0X65,0X20,0X6C,0X65,0X6E,0X20,0X66,0X69,0X65,0X6C,0X64,0X2E}, .frame_len=167, .raw_payload_len=159, @@ -48,7 +48,7 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X88,0X82,0X71,0X1D,0X00,0XFE,0X72,0XF6}, + .frame={0X88,0X82,0X6B,0X33,0X77,0X94,0X68,0XD8}, .expectedDecodeBuf={0X03,0XEB}, .frame_len=8, .raw_payload_len=2, @@ -60,7 +60,7 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X88,0XAD,0XD0,0X8D,0X26,0XD8,0XD3,0X66,0X6F,0XFF,0XBD,0XAD,0X47,0XF8,0XB3,0XE1,0X49,0XAB,0XB5,0XAD,0X54,0XBD,0XB1,0XFE,0X49,0XB6,0XF0,0XEC,0X48,0XBC,0XF0,0XE0,0X53,0XBB,0XB8,0XAD,0X4B,0XB7,0XA2,0XE8,0X06,0XAC,0XB8,0XEC,0X48,0XF8,0XA4,0XE5,0X47,0XAC,0XF1}, + .frame={0X88,0XAD,0X4B,0XA1,0XCE,0XE8,0X48,0X4A,0X87,0XCF,0X26,0X81,0XAF,0XC8,0X28,0XCD,0XA1,0X9B,0X2E,0X81,0XBC,0X8D,0X2A,0XD2,0XA1,0X86,0X6B,0XC0,0XA0,0X8C,0X6B,0XCC,0XBB,0X8B,0X23,0X81,0XA3,0X87,0X39,0XC4,0XEE,0X9C,0X23,0XC0,0XA0,0XC8,0X3F,0XC9,0XAF,0X9C,0X6A}, .expectedDecodeBuf={0X03,0XEB,0X49,0X27,0X6D,0X20,0X61,0X20,0X63,0X6C,0X6F,0X73,0X65,0X20,0X72,0X65,0X61,0X73,0X6F,0X6E,0X20,0X61,0X6E,0X64,0X20,0X6D,0X75,0X63,0X68,0X20,0X6D,0X6F,0X72,0X65,0X20,0X74,0X68,0X61,0X6E,0X20,0X74,0X68,0X61,0X74,0X21}, .frame_len=51, .raw_payload_len=45, @@ -84,7 +84,7 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X81,0XFE,0X00,0X0F,0X24,0X22,0X8D,0X9C,0X11,0X6F,0XA3,0XC6,0X6E,0X4E,0X88,0XB0,0X48,0X55,0XA2,0XC6,0X72,0X56}, + .frame={0X81,0XFE,0X00,0X0F,0X71,0XE9,0X29,0X79,0X44,0XA4,0X07,0X23,0X3B,0X85,0X2C,0X55,0X1D,0X9E,0X06,0X23,0X27,0X9D}, .expectedDecodeBuf={0X2E,0XFE,0X00,0X0F,0X72,0X65,0X20,0X49,0X70,0X73,0X75,0X6D}, .frame_len=22, .raw_payload_len=12, @@ -96,8 +96,8 @@ struct ws_frame_test tests[] = { .close_sock_at=0 }, { - .frame={0X81,0XFF,0X00,0X00,0X00,0X00,0X80,0X40,0X7D,0XBB,0X03,0X56,0X7D,0XBB,0X03,0X56,0X7C,0X83,0X2D,0X0C,0X03,0XA2,0X06,0X7A,0X25,0XB9,0X2C,0X0C,0X1F,0XBA}, - .expectedDecodeBuf={0X2E,0XFF,0X00,0X00,0X00,0X00,0X80,0X40,0X4C,0X6F,0X72,0X65,0X20,0X49,0X70,0X73,0X75,0X6D}, + .frame={0X81,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0X40,0X2F,0X40,0XF3,0X5B,0X2F,0X40,0XF2,0X63,0X01,0X1A,0X8D,0X42,0X2A,0X6C,0XAB,0X59,0X00,0X1A,0X91,0X5A}, + .expectedDecodeBuf={0X2E,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0X40,0X72,0X65,0X20,0X49,0X70,0X73,0X75,0X6D}, .frame_len=30, .raw_payload_len=18, .expected_errno=EPROTO, @@ -106,5 +106,41 @@ struct ws_frame_test tests[] = { .simulate_sock_malfunction_at=0, .errno_val=0, .close_sock_at=0 + }, + { + .frame={0X01,0XAC,0XC9,0X6E,0XC7,0X6E,0X9F,0X29,0XAF,0X1E,0XAA,0X17,0X85,0X1E,0XAA,0X17,0X85,0X06,0X80,0X29,0X9D,0X17,0X90,0X39,0XA3,0X1A,0X93,0X39,0XF2,0X5E,0X93,0X39,0X96,0X09,0XAD,0X5C,0X91,0X07,0XAA,0X5C,0XFE,0X04,0XA8,0X5C,0X91,0X5E,0X85,0X07,0XF3,0X1B}, + .expectedDecodeBuf={0X54,0X68,0X69,0X73,0X20,0X69,0X73,0X20,0X61,0X20,0X66,0X72,0X61,0X67,0X6D,0X65,0X6E,0X74,0X65,0X64,0X20,0X77,0X65,0X62,0X73,0X6F,0X63,0X6B,0X65,0X74,0X2E,0X2E,0X2E}, + .frame_len=50, + .raw_payload_len=33, + .expected_errno=0, + .descr="Continuation test frag1", + .i=0, + .simulate_sock_malfunction_at=0, + .errno_val=0, + .close_sock_at=0 + }, + { + .frame={0X00,0X9C,0X52,0XBC,0XD5,0X99,0X1E,0XD5,0XE1,0XEC,0X1B,0XFB,0X93,0XEC,0X08,0XFF,0X97,0XE9,0X36,0XFF,0X97,0XF7,0X30,0X8E,0X83,0XE3,0X1B,0XFB,0XEC,0XEC,0X1E,0XD5,0XE1,0XEC}, + .expectedDecodeBuf={0X2E,0X2E,0X2E,0X20,0X61,0X6E,0X64,0X20,0X69,0X74,0X20,0X67,0X6F,0X65,0X73,0X20,0X6F,0X6E,0X2E,0X2E,0X2E}, + .frame_len=34, + .raw_payload_len=21, + .expected_errno=0, + .descr="Continuation test frag2", + .i=0, + .simulate_sock_malfunction_at=0, + .errno_val=0, + .close_sock_at=0 + }, + { + .frame={0X80,0X94,0X3B,0X88,0XA1,0XE9,0X62,0XDF,0X94,0X82,0X72,0XCF,0X98,0X9C,0X72,0XCF,0XE7,0X9C,0X61,0XCB,0XE3,0X93,0X5F,0XCF,0X98,0X9E}, + .expectedDecodeBuf={0X61,0X6E,0X64,0X20,0X6F,0X6E,0X20,0X61,0X6E,0X64,0X20,0X73,0X74,0X6F,0X70}, + .frame_len=26, + .raw_payload_len=15, + .expected_errno=0, + .descr="Continuation test frag3", + .i=0, + .simulate_sock_malfunction_at=0, + .errno_val=0, + .close_sock_at=0 } };