summaryrefslogtreecommitdiff
path: root/thirdparty/wslay/wslay_frame.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/wslay/wslay_frame.c')
-rw-r--r--thirdparty/wslay/wslay_frame.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/thirdparty/wslay/wslay_frame.c b/thirdparty/wslay/wslay_frame.c
new file mode 100644
index 0000000000..445e750ca5
--- /dev/null
+++ b/thirdparty/wslay/wslay_frame.c
@@ -0,0 +1,340 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_frame.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+
+#include "wslay_net.h"
+
+#define wslay_min(A, B) (((A) < (B)) ? (A) : (B))
+
+int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
+ const struct wslay_frame_callbacks *callbacks,
+ void *user_data)
+{
+ *ctx = (wslay_frame_context_ptr)malloc(sizeof(struct wslay_frame_context));
+ if(*ctx == NULL) {
+ return -1;
+ }
+ memset(*ctx, 0, sizeof(struct wslay_frame_context));
+ (*ctx)->istate = RECV_HEADER1;
+ (*ctx)->ireqread = 2;
+ (*ctx)->ostate = PREP_HEADER;
+ (*ctx)->user_data = user_data;
+ (*ctx)->ibufmark = (*ctx)->ibuflimit = (*ctx)->ibuf;
+ (*ctx)->callbacks = *callbacks;
+ return 0;
+}
+
+void wslay_frame_context_free(wslay_frame_context_ptr ctx)
+{
+ free(ctx);
+}
+
+ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb)
+{
+ if(iocb->data_length > iocb->payload_length) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(ctx->ostate == PREP_HEADER) {
+ uint8_t *hdptr = ctx->oheader;
+ memset(ctx->oheader, 0, sizeof(ctx->oheader));
+ *hdptr |= (iocb->fin << 7) & 0x80u;
+ *hdptr |= (iocb->rsv << 4) & 0x70u;
+ *hdptr |= iocb->opcode & 0xfu;
+ ++hdptr;
+ *hdptr |= (iocb->mask << 7) & 0x80u;
+ if(wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(iocb->payload_length < 126) {
+ *hdptr |= iocb->payload_length;
+ ++hdptr;
+ } else if(iocb->payload_length < (1 << 16)) {
+ uint16_t len = htons(iocb->payload_length);
+ *hdptr |= 126;
+ ++hdptr;
+ memcpy(hdptr, &len, 2);
+ hdptr += 2;
+ } else if(iocb->payload_length < (1ull << 63)) {
+ uint64_t len = hton64(iocb->payload_length);
+ *hdptr |= 127;
+ ++hdptr;
+ memcpy(hdptr, &len, 8);
+ hdptr += 8;
+ } else {
+ /* Too large payload length */
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(iocb->mask) {
+ if(ctx->callbacks.genmask_callback(ctx->omaskkey, 4,
+ ctx->user_data) != 0) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->omask = 1;
+ memcpy(hdptr, ctx->omaskkey, 4);
+ hdptr += 4;
+ }
+ }
+ ctx->ostate = SEND_HEADER;
+ ctx->oheadermark = ctx->oheader;
+ ctx->oheaderlimit = hdptr;
+ ctx->opayloadlen = iocb->payload_length;
+ ctx->opayloadoff = 0;
+ }
+ if(ctx->ostate == SEND_HEADER) {
+ ptrdiff_t len = ctx->oheaderlimit-ctx->oheadermark;
+ ssize_t r;
+ int flags = 0;
+ if(iocb->data_length > 0) {
+ flags |= WSLAY_MSG_MORE;
+ };
+ r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags,
+ ctx->user_data);
+ if(r > 0) {
+ if(r > len) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->oheadermark += r;
+ if(ctx->oheadermark == ctx->oheaderlimit) {
+ ctx->ostate = SEND_PAYLOAD;
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ if(ctx->ostate == SEND_PAYLOAD) {
+ size_t totallen = 0;
+ if(iocb->data_length > 0) {
+ if(ctx->omask) {
+ uint8_t temp[4096];
+ const uint8_t *datamark = iocb->data,
+ *datalimit = iocb->data+iocb->data_length;
+ while(datamark < datalimit) {
+ size_t datalen = datalimit - datamark;
+ const uint8_t *writelimit = datamark+
+ wslay_min(sizeof(temp), datalen);
+ size_t writelen = writelimit-datamark;
+ ssize_t r;
+ size_t i;
+ for(i = 0; i < writelen; ++i) {
+ temp[i] = datamark[i]^ctx->omaskkey[(ctx->opayloadoff+i)%4];
+ }
+ r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data);
+ if(r > 0) {
+ if((size_t)r > writelen) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ datamark += r;
+ ctx->opayloadoff += r;
+ totallen += r;
+ }
+ } else {
+ if(totallen > 0) {
+ break;
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ }
+ } else {
+ ssize_t r;
+ r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0,
+ ctx->user_data);
+ if(r > 0) {
+ if((size_t)r > iocb->data_length) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->opayloadoff += r;
+ totallen = r;
+ }
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ }
+ if(ctx->opayloadoff == ctx->opayloadlen) {
+ ctx->ostate = PREP_HEADER;
+ }
+ return totallen;
+ }
+ return WSLAY_ERR_INVALID_ARGUMENT;
+}
+
+static void wslay_shift_ibuf(wslay_frame_context_ptr ctx)
+{
+ ptrdiff_t len = ctx->ibuflimit-ctx->ibufmark;
+ memmove(ctx->ibuf, ctx->ibufmark, len);
+ ctx->ibuflimit = ctx->ibuf+len;
+ ctx->ibufmark = ctx->ibuf;
+}
+
+static ssize_t wslay_recv(wslay_frame_context_ptr ctx)
+{
+ ssize_t r;
+ if(ctx->ibufmark != ctx->ibuf) {
+ wslay_shift_ibuf(ctx);
+ }
+ r = ctx->callbacks.recv_callback
+ (ctx->ibuflimit, ctx->ibuf+sizeof(ctx->ibuf)-ctx->ibuflimit,
+ 0, ctx->user_data);
+ if(r > 0) {
+ ctx->ibuflimit += r;
+ } else {
+ r = WSLAY_ERR_WANT_READ;
+ }
+ return r;
+}
+
+#define WSLAY_AVAIL_IBUF(ctx) ((size_t)(ctx->ibuflimit - ctx->ibufmark))
+
+ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb)
+{
+ ssize_t r;
+ if(ctx->istate == RECV_HEADER1) {
+ uint8_t fin, opcode, rsv, payloadlen;
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ fin = (ctx->ibufmark[0] >> 7) & 1;
+ rsv = (ctx->ibufmark[0] >> 4) & 7;
+ opcode = ctx->ibufmark[0] & 0xfu;
+ ctx->iom.opcode = opcode;
+ ctx->iom.fin = fin;
+ ctx->iom.rsv = rsv;
+ ++ctx->ibufmark;
+ ctx->imask = (ctx->ibufmark[0] >> 7) & 1;
+ payloadlen = ctx->ibufmark[0] & 0x7fu;
+ ++ctx->ibufmark;
+ if(wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) {
+ return WSLAY_ERR_PROTO;
+ }
+ if(payloadlen == 126) {
+ ctx->istate = RECV_EXT_PAYLOADLEN;
+ ctx->ireqread = 2;
+ } else if(payloadlen == 127) {
+ ctx->istate = RECV_EXT_PAYLOADLEN;
+ ctx->ireqread = 8;
+ } else {
+ ctx->ipayloadlen = payloadlen;
+ ctx->ipayloadoff = 0;
+ if(ctx->imask) {
+ ctx->istate = RECV_MASKKEY;
+ ctx->ireqread = 4;
+ } else {
+ ctx->istate = RECV_PAYLOAD;
+ }
+ }
+ }
+ if(ctx->istate == RECV_EXT_PAYLOADLEN) {
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ }
+ ctx->ipayloadlen = 0;
+ ctx->ipayloadoff = 0;
+ memcpy((uint8_t*)&ctx->ipayloadlen+(8-ctx->ireqread),
+ ctx->ibufmark, ctx->ireqread);
+ ctx->ipayloadlen = ntoh64(ctx->ipayloadlen);
+ ctx->ibufmark += ctx->ireqread;
+ if(ctx->ireqread == 8) {
+ if(ctx->ipayloadlen < (1 << 16) ||
+ ctx->ipayloadlen & (1ull << 63)) {
+ return WSLAY_ERR_PROTO;
+ }
+ } else if(ctx->ipayloadlen < 126) {
+ return WSLAY_ERR_PROTO;
+ }
+ if(ctx->imask) {
+ ctx->istate = RECV_MASKKEY;
+ ctx->ireqread = 4;
+ } else {
+ ctx->istate = RECV_PAYLOAD;
+ }
+ }
+ if(ctx->istate == RECV_MASKKEY) {
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ }
+ memcpy(ctx->imaskkey, ctx->ibufmark, 4);
+ ctx->ibufmark += 4;
+ ctx->istate = RECV_PAYLOAD;
+ }
+ if(ctx->istate == RECV_PAYLOAD) {
+ uint8_t *readlimit, *readmark;
+ uint64_t rempayloadlen = ctx->ipayloadlen-ctx->ipayloadoff;
+ if(WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ }
+ readmark = ctx->ibufmark;
+ readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ?
+ ctx->ibuflimit : ctx->ibufmark+rempayloadlen;
+ if(ctx->imask) {
+ for(; ctx->ibufmark != readlimit;
+ ++ctx->ibufmark, ++ctx->ipayloadoff) {
+ ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4];
+ }
+ } else {
+ ctx->ibufmark = readlimit;
+ ctx->ipayloadoff += readlimit-readmark;
+ }
+ iocb->fin = ctx->iom.fin;
+ iocb->rsv = ctx->iom.rsv;
+ iocb->opcode = ctx->iom.opcode;
+ iocb->payload_length = ctx->ipayloadlen;
+ iocb->mask = ctx->imask;
+ iocb->data = readmark;
+ iocb->data_length = ctx->ibufmark-readmark;
+ if(ctx->ipayloadlen == ctx->ipayloadoff) {
+ ctx->istate = RECV_HEADER1;
+ ctx->ireqread = 2;
+ }
+ return iocb->data_length;
+ }
+ return WSLAY_ERR_INVALID_ARGUMENT;
+}