static int wslay_event_context_init (wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data) { int i, r; struct wslay_frame_callbacks frame_callbacks = { wslay_event_frame_send_callback, wslay_event_frame_recv_callback, wslay_event_frame_genmask_callback }; *ctx = (wslay_event_context_ptr)malloc(sizeof(struct wslay_event_context)); if(!*ctx) { return WSLAY_ERR_NOMEM; } memset(*ctx, 0, sizeof(struct wslay_event_context)); wslay_event_config_set_callbacks(*ctx, callbacks); (*ctx)->user_data = user_data; (*ctx)->frame_user_data.ctx = *ctx; (*ctx)->frame_user_data.user_data = user_data; if((r = wslay_frame_context_init(&(*ctx)->frame_ctx, &frame_callbacks, &(*ctx)->frame_user_data)) != 0) { wslay_event_context_free(*ctx); return r; } (*ctx)->read_enabled = (*ctx)->write_enabled = 1; (*ctx)->send_queue = wslay_queue_new(); if(!(*ctx)->send_queue) { wslay_event_context_free(*ctx); return WSLAY_ERR_NOMEM; } (*ctx)->send_ctrl_queue = wslay_queue_new(); if(!(*ctx)->send_ctrl_queue) { wslay_event_context_free(*ctx); return WSLAY_ERR_NOMEM; } (*ctx)->queued_msg_count = 0; (*ctx)->queued_msg_length = 0; for(i = 0; i < 2; ++i) { wslay_event_imsg_reset(&(*ctx)->imsgs[i]); (*ctx)->imsgs[i].chunks = wslay_queue_new(); if(!(*ctx)->imsgs[i].chunks) { wslay_event_context_free(*ctx); return WSLAY_ERR_NOMEM; } } (*ctx)->imsg = &(*ctx)->imsgs[0]; (*ctx)->obufmark = (*ctx)->obuflimit = (*ctx)->obuf; (*ctx)->status_code_sent = WSLAY_CODE_ABNORMAL_CLOSURE; (*ctx)->status_code_recv = WSLAY_CODE_ABNORMAL_CLOSURE; (*ctx)->max_recv_msg_length = (1u << 31)-1; return 0; }
wslay_event_context * wslay_event_context_new ( void * ctx, const struct wslay_event_callbacks * callbacks, void * user_data ) { wslay_event_context * context = talloc_zero ( ctx, sizeof ( wslay_event_context ) ); if ( context == NULL ) { return NULL; } struct wslay_frame_callbacks frame_callbacks = { wslay_event_frame_send_callback, wslay_event_frame_recv_callback, wslay_event_frame_genmask_callback }; context->callbacks = * callbacks; context->user_data = user_data; context->frame_user_data.ctx = context; context->frame_user_data.user_data = user_data; wslay_frame_context * frame_ctx = wslay_frame_context_new ( context, &frame_callbacks, &context->frame_user_data ); if ( frame_ctx == NULL ) { talloc_free ( context ); return NULL; } context->frame_ctx = frame_ctx; context->read_enabled = context->write_enabled = 1; context->send_queue = wslay_queue_new ( context ); if ( context->send_queue == NULL ) { talloc_free ( context ); return NULL; } context->send_ctrl_queue = wslay_queue_new ( context ); if ( context->send_ctrl_queue == NULL ) { talloc_free ( context ); return NULL; } context->queued_msg_count = 0; context->queued_msg_length = 0; uint8_t i; for ( i = 0; i < 2; ++i ) { wslay_event_imsg_reset ( & context->imsgs[i] ); context->imsgs[i].chunks = wslay_queue_new ( context ); if ( context->imsgs[i].chunks == NULL ) { talloc_free ( context ); return NULL; } } context->imsg = & context->imsgs[0]; context->obufmark = context->obuflimit = context->obuf; context->status_code_sent = WSLAY_CODE_ABNORMAL_CLOSURE; context->status_code_recv = WSLAY_CODE_ABNORMAL_CLOSURE; context->max_recv_msg_length = UINT64_MAX; return context; }
int wslay_event_recv(wslay_event_context_ptr ctx) { struct wslay_frame_iocb iocb; ssize_t r; while (ctx->read_enabled) { memset(&iocb, 0, sizeof(iocb)); r = wslay_frame_recv(ctx->frame_ctx, &iocb); if (r >= 0) { int new_frame = 0; /* RSV1 is not allowed on control and continuation frames */ if ((!wslay_event_verify_rsv_bits(ctx, iocb.rsv)) || (wslay_get_rsv1(iocb.rsv) && (wslay_is_ctrl_frame(iocb.opcode) || iocb.opcode == WSLAY_CONTINUATION_FRAME)) || (ctx->server && !iocb.mask) || (!ctx->server && iocb.mask)) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } if (ctx->imsg->opcode == 0xffu) { if (iocb.opcode == WSLAY_TEXT_FRAME || iocb.opcode == WSLAY_BINARY_FRAME || iocb.opcode == WSLAY_CONNECTION_CLOSE || iocb.opcode == WSLAY_PING || iocb.opcode == WSLAY_PONG) { wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode); new_frame = 1; } else { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } } else if (ctx->ipayloadlen == 0 && ctx->ipayloadoff == 0) { if (iocb.opcode == WSLAY_CONTINUATION_FRAME) { ctx->imsg->fin = iocb.fin; } else if (iocb.opcode == WSLAY_CONNECTION_CLOSE || iocb.opcode == WSLAY_PING || iocb.opcode == WSLAY_PONG) { ctx->imsg = &ctx->imsgs[1]; wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode); } else { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } new_frame = 1; } if (new_frame) { if (ctx->imsg->msg_length + iocb.payload_length > ctx->max_recv_msg_length) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_MESSAGE_TOO_BIG, NULL, 0)) != 0) { return r; } break; } ctx->ipayloadlen = iocb.payload_length; wslay_event_call_on_frame_recv_start_callback(ctx, &iocb); if (!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { if ((r = wslay_event_imsg_append_chunk(ctx->imsg, iocb.payload_length)) != 0) { ctx->read_enabled = 0; return r; } } } /* If RSV1 bit is set then it is too early for utf-8 validation */ if ((!wslay_get_rsv1(iocb.rsv) && ctx->imsg->opcode == WSLAY_TEXT_FRAME) || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { size_t i; if (ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { i = 2; } else { i = 0; } for (; i < iocb.data_length; ++i) { uint32_t codep; if (decode(&ctx->imsg->utf8state, &codep, iocb.data[i]) == UTF8_REJECT) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) { return r; } break; } } } if (ctx->imsg->utf8state == UTF8_REJECT) { break; } wslay_event_call_on_frame_recv_chunk_callback(ctx, &iocb); if (iocb.data_length > 0) { if (!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { struct wslay_event_byte_chunk *chunk; chunk = wslay_queue_tail(ctx->imsg->chunks); wslay_event_byte_chunk_copy(chunk, ctx->ipayloadoff, iocb.data, iocb.data_length); } ctx->ipayloadoff += iocb.data_length; } if (ctx->ipayloadoff == ctx->ipayloadlen) { if (ctx->imsg->fin && (ctx->imsg->opcode == WSLAY_TEXT_FRAME || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) && ctx->imsg->utf8state != UTF8_ACCEPT) { if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) { return r; } break; } wslay_event_call_on_frame_recv_end_callback(ctx); if (ctx->imsg->fin) { if (ctx->callbacks.on_msg_recv_callback || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE || ctx->imsg->opcode == WSLAY_PING || ctx->imsg->opcode == WSLAY_PONG) { struct wslay_event_on_msg_recv_arg arg; uint16_t status_code = 0; uint8_t *msg = NULL; size_t msg_length = 0; if (!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { msg = wslay_event_flatten_queue(ctx->imsg->chunks, ctx->imsg->msg_length); if (ctx->imsg->msg_length && !msg) { ctx->read_enabled = 0; return WSLAY_ERR_NOMEM; } msg_length = ctx->imsg->msg_length; } if (ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { const uint8_t *reason; size_t reason_length; if (ctx->imsg->msg_length >= 2) { memcpy(&status_code, msg, 2); status_code = ntohs(status_code); if (!wslay_event_is_valid_status_code(status_code)) { free(msg); if ((r = wslay_event_queue_close_wrapper(ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } reason = msg + 2; reason_length = ctx->imsg->msg_length - 2; } else { reason = NULL; reason_length = 0; } ctx->close_status |= WSLAY_CLOSE_RECEIVED; ctx->status_code_recv = status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code; if ((r = wslay_event_queue_close_wrapper(ctx, status_code, reason, reason_length)) != 0) { free(msg); return r; } } else if (ctx->imsg->opcode == WSLAY_PING) { struct wslay_event_msg pong_arg; pong_arg.opcode = WSLAY_PONG; pong_arg.msg = msg; pong_arg.msg_length = ctx->imsg->msg_length; if ((r = wslay_event_queue_msg(ctx, &pong_arg)) && r != WSLAY_ERR_NO_MORE_MSG) { ctx->read_enabled = 0; free(msg); return r; } } else if (ctx->imsg->opcode == WSLAY_PONG) { struct websocket_info_t *info = ctx->user_data; info->data->ping_cnt = 0; } if (ctx->callbacks.on_msg_recv_callback) { arg.rsv = ctx->imsg->rsv; arg.opcode = ctx->imsg->opcode; arg.msg = msg; arg.msg_length = msg_length; arg.status_code = status_code; ctx->error = 0; ctx->callbacks.on_msg_recv_callback(ctx, &arg, ctx->user_data); if (ctx->imsg->opcode != WSLAY_CONNECTION_CLOSE) { free(msg); wslay_event_imsg_reset(ctx->imsg); if (ctx->imsg == &ctx->imsgs[1]) { ctx->imsg = &ctx->imsgs[0]; } ctx->ipayloadlen = ctx->ipayloadoff = 0; break; } } if (ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { struct websocket_info_t *info = ctx->user_data; if (!wslay_queue_empty(ctx->send_ctrl_queue)) { wslay_event_send(ctx); } websocket_update_state(info->data, WEBSOCKET_STOP); } free(msg); } wslay_event_imsg_reset(ctx->imsg); if (ctx->imsg == &ctx->imsgs[1]) { ctx->imsg = &ctx->imsgs[0]; } } ctx->ipayloadlen = ctx->ipayloadoff = 0; } } else { if (r != WSLAY_ERR_WANT_READ || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { if ((r = wslay_event_queue_close_wrapper(ctx, 0, NULL, 0)) != 0) { return r; } return WSLAY_ERR_CALLBACK_FAILURE; } break; } } return 0; }