Пример #1
0
int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg, uint8_t rsv)
{
	int r;
	struct wslay_event_omsg *omsg;
	if (!wslay_event_is_msg_queueable(ctx)) {
		return WSLAY_ERR_NO_MORE_MSG;
	}
	/* RSV1 is not allowed for control frames */
	if ((wslay_is_ctrl_frame(arg->opcode) && (arg->msg_length > 125 || wslay_get_rsv1(rsv)))
		|| !wslay_event_verify_rsv_bits(ctx, rsv)) {
		return WSLAY_ERR_INVALID_ARGUMENT;
	}
	if ((r = wslay_event_omsg_non_fragmented_init(&omsg, arg->opcode, rsv, arg->msg, arg->msg_length)) != 0) {
		return r;
	}
	if (wslay_is_ctrl_frame(arg->opcode)) {
		if ((r = wslay_queue_push(ctx->send_ctrl_queue, omsg)) != 0) {
			return r;
		}
	} else {
		if ((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) {
			return r;
		}
	}
	++ctx->queued_msg_count;
	ctx->queued_msg_length += arg->msg_length;
	return 0;
}
Пример #2
0
static mrb_value
mrb_wslay_get_rsv1(mrb_state *mrb, mrb_value self)
{
  mrb_int rsv;

  mrb_get_args(mrb, "i", &rsv);

  return mrb_fixnum_value(wslay_get_rsv1(rsv));
}
Пример #3
0
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;
}
Пример #4
0
static void on_msg_recv_cb(wslay_event_context_ptr ev,
                           const struct wslay_event_on_msg_recv_arg *arg,
                           void *user_data)
{
    struct transaction_t *txn = (struct transaction_t *) user_data;
    struct ws_context *ctx = (struct ws_context *) txn->ws_ctx;
    struct buf inbuf = BUF_INITIALIZER, outbuf = BUF_INITIALIZER;
    struct wslay_event_msg msgarg = { arg->opcode, NULL, 0 };
    uint8_t rsv = WSLAY_RSV_NONE;
    double cmdtime, nettime;
    const char *err_msg;
    int r, err_code = 0;

    /* Place client request into a buf */
    buf_init_ro(&inbuf, (const char *) arg->msg, arg->msg_length);

    /* Decompress request, if necessary */
    if (wslay_get_rsv1(arg->rsv)) {
        /* Add trailing 4 bytes */
        buf_appendmap(&inbuf, "\x00\x00\xff\xff", 4);

        r = zlib_decompress(txn, buf_base(&inbuf), buf_len(&inbuf));
        if (r) {
            syslog(LOG_ERR, "on_msg_recv_cb(): zlib_decompress() failed");

            err_code = WSLAY_CODE_PROTOCOL_ERROR;
            err_msg = DECOMP_FAILED_ERR;
            goto err;
        }

        buf_move(&inbuf, &txn->zbuf);
    }

    /* Log the uncompressed client request */
    buf_truncate(&ctx->log, ctx->log_tail);
    buf_appendcstr(&ctx->log, " (");
    if (txn->strm_ctx) {
        buf_printf(&ctx->log, "stream-id=%d; ",
                   http2_get_streamid(txn->strm_ctx));
    }
    buf_printf(&ctx->log, "opcode=%s; rsv=0x%x; length=%ld",
               wslay_str_opcode(arg->opcode), arg->rsv, arg->msg_length);

    switch (arg->opcode) {
    case WSLAY_CONNECTION_CLOSE:
        buf_printf(&ctx->log, "; status=%d; msg='%s'", arg->status_code,
                   buf_len(&inbuf) ? buf_cstring(&inbuf)+2 : "");
        txn->flags.conn = CONN_CLOSE;
        break;

    case WSLAY_TEXT_FRAME:
    case WSLAY_BINARY_FRAME:
        if (txn->conn->logfd != -1) {
            /* Telemetry logging */
            struct iovec iov[2];
            int niov = 0;

            assert(!buf_len(&txn->buf));
            buf_printf(&txn->buf, "<%ld<", time(NULL));  /* timestamp */
            WRITEV_ADD_TO_IOVEC(iov, niov,
                                buf_base(&txn->buf), buf_len(&txn->buf));
            WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&inbuf), buf_len(&inbuf));
            writev(txn->conn->logfd, iov, niov);
            buf_reset(&txn->buf);
        }

        /* Process the request */
        r = ctx->data_cb(&inbuf, &outbuf, &ctx->log, &ctx->cb_rock);
        if (r) {

            err_code = (r == HTTP_SERVER_ERROR ?
                        WSLAY_CODE_INTERNAL_SERVER_ERROR :
                        WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA);
            err_msg = error_message(r);
            goto err;
        }

        if (txn->conn->logfd != -1) {
            /* Telemetry logging */
            struct iovec iov[2];
            int niov = 0;

            assert(!buf_len(&txn->buf));
            buf_printf(&txn->buf, ">%ld>", time(NULL));  /* timestamp */
            WRITEV_ADD_TO_IOVEC(iov, niov,
                                buf_base(&txn->buf), buf_len(&txn->buf));
            WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&outbuf), buf_len(&outbuf));
            writev(txn->conn->logfd, iov, niov);
            buf_reset(&txn->buf);
        }

        /* Compress the server response, if supported by the client */
        if (ctx->ext & EXT_PMCE_DEFLATE) {
            r = zlib_compress(txn,
                              ctx->pmce.deflate.no_context ? COMPRESS_START : 0,
                              buf_base(&outbuf), buf_len(&outbuf));
            if (r) {
                syslog(LOG_ERR, "on_msg_recv_cb(): zlib_compress() failed");

                err_code = WSLAY_CODE_INTERNAL_SERVER_ERROR;
                err_msg = COMP_FAILED_ERR;
                goto err;
            }

            /* Trim the trailing 4 bytes */
            buf_truncate(&txn->zbuf, buf_len(&txn->zbuf) - 4);
            buf_move(&outbuf, &txn->zbuf);

            rsv |= WSLAY_RSV1_BIT;
        }

        /* Queue the server response */
        msgarg.msg = (const uint8_t *) buf_base(&outbuf);
        msgarg.msg_length = buf_len(&outbuf);
        wslay_event_queue_msg_ex(ev, &msgarg, rsv);

        /* Log the server response */
        buf_printf(&ctx->log,
                   ") => \"Success\" (opcode=%s; rsv=0x%x; length=%ld",
                   wslay_str_opcode(msgarg.opcode), rsv, msgarg.msg_length);
        break;
    }

  err:
    if (err_code) {
        size_t err_msg_len = strlen(err_msg);

        syslog(LOG_DEBUG, "wslay_event_queue_close()");
        wslay_event_queue_close(ev, err_code, (uint8_t *) err_msg, err_msg_len);

        /* Log the server response */
        buf_printf(&ctx->log,
                   ") => \"Fail\" (opcode=%s; rsv=0x%x; length=%ld"
                   "; status=%d; msg='%s'",
                   wslay_str_opcode(WSLAY_CONNECTION_CLOSE), rsv, err_msg_len,
                   err_code, err_msg);
    }

    /* Add timing stats */
    cmdtime_endtimer(&cmdtime, &nettime);
    buf_printf(&ctx->log, ") [timing: cmd=%f net=%f total=%f]",
               cmdtime, nettime, cmdtime + nettime);

    syslog(LOG_INFO, "%s", buf_cstring(&ctx->log));

    buf_free(&inbuf);
    buf_free(&outbuf);
}