aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/ngx_core.h1
-rw-r--r--src/event/ngx_event_quic.c670
-rw-r--r--src/event/ngx_event_quic.h82
-rw-r--r--src/event/ngx_event_quic_protection.c30
-rw-r--r--src/event/ngx_event_quic_protection.h4
-rw-r--r--src/event/ngx_event_quic_transport.c588
-rw-r--r--src/event/ngx_event_quic_transport.h177
7 files changed, 808 insertions, 744 deletions
diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
index a5f7b7af5..eb3acd663 100644
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -85,6 +85,7 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
#include <ngx_event_quic.h>
+#include <ngx_event_quic_transport.h>
#include <ngx_event_quic_protection.h>
#endif
#include <ngx_process_cycle.h>
diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c
index 410fa0fcd..000b8be8b 100644
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -9,82 +9,6 @@
#include <ngx_event.h>
-/* 12.4. Frames and Frame Types */
-#define NGX_QUIC_FT_PADDING 0x00
-#define NGX_QUIC_FT_PING 0x01
-#define NGX_QUIC_FT_ACK 0x02
-#define NGX_QUIC_FT_ACK_ECN 0x03
-#define NGX_QUIC_FT_RESET_STREAM 0x04
-#define NGX_QUIC_FT_STOP_SENDING 0x05
-#define NGX_QUIC_FT_CRYPTO 0x06
-#define NGX_QUIC_FT_NEW_TOKEN 0x07
-#define NGX_QUIC_FT_STREAM0 0x08
-#define NGX_QUIC_FT_STREAM1 0x09
-#define NGX_QUIC_FT_STREAM2 0x0A
-#define NGX_QUIC_FT_STREAM3 0x0B
-#define NGX_QUIC_FT_STREAM4 0x0C
-#define NGX_QUIC_FT_STREAM5 0x0D
-#define NGX_QUIC_FT_STREAM6 0x0E
-#define NGX_QUIC_FT_STREAM7 0x0F
-#define NGX_QUIC_FT_MAX_DATA 0x10
-#define NGX_QUIC_FT_MAX_STREAM_DATA 0x11
-#define NGX_QUIC_FT_MAX_STREAMS 0x12
-#define NGX_QUIC_FT_MAX_STREAMS2 0x13 // XXX
-#define NGX_QUIC_FT_DATA_BLOCKED 0x14
-#define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15
-#define NGX_QUIC_FT_STREAMS_BLOCKED 0x16
-#define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17 // XXX
-#define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18
-#define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19
-#define NGX_QUIC_FT_PATH_CHALLENGE 0x1a
-#define NGX_QUIC_FT_PATH_RESPONSE 0x1b
-#define NGX_QUIC_FT_CONNECTION_CLOSE 0x1c
-#define NGX_QUIC_FT_CONNECTION_CLOSE2 0x1d // XXX
-#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1e
-
-#define ngx_quic_stream_bit_off(val) (((val) & 0x04) ? 1 : 0)
-#define ngx_quic_stream_bit_len(val) (((val) & 0x02) ? 1 : 0)
-#define ngx_quic_stream_bit_fin(val) (((val) & 0x01) ? 1 : 0)
-
-
-#define NGX_QUIC_ERR_NO_ERROR 0x0
-#define NGX_QUIC_ERR_INTERNAL_ERROR 0x1
-#define NGX_QUIC_ERR_SERVER_BUSY 0x2
-#define NGX_QUIC_ERR_FLOW_CONTROL_ERROR 0x3
-#define NGX_QUIC_ERR_STREAM_LIMIT_ERROR 0x4
-#define NGX_QUIC_ERR_STREAM_STATE_ERROR 0x5
-#define NGX_QUIC_ERR_FINAL_SIZE_ERROR 0x6
-#define NGX_QUIC_ERR_FRAME_ENCODING_ERROR 0x7
-#define NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR 0x8
-#define NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR 0x9
-#define NGX_QUIC_ERR_PROTOCOL_VIOLATION 0xA
-#define NGX_QUIC_ERR_INVALID_TOKEN 0xB
-/* 0xC is not defined */
-#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0xD
-#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10
-
-#define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR
-
-/* literal errors indexed by corresponding value */
-static char *ngx_quic_errors[] = {
- "NO_ERROR",
- "INTERNAL_ERROR",
- "SERVER_BUSY",
- "FLOW_CONTROL_ERROR",
- "STREAM_LIMIT_ERROR",
- "STREAM_STATE_ERROR",
- "FINAL_SIZE_ERROR",
- "FRAME_ENCODING_ERROR",
- "TRANSPORT_PARAMETER_ERROR",
- "CONNECTION_ID_LIMIT_ERROR",
- "PROTOCOL_VIOLATION",
- "INVALID_TOKEN",
- "",
- "CRYPTO_BUFFER_EXCEEDED",
- "CRYPTO_ERROR",
-};
-
-
/* TODO: real states, these are stubs */
typedef enum {
NGX_QUIC_ST_INITIAL,
@@ -93,69 +17,6 @@ typedef enum {
} ngx_quic_state_t;
-typedef struct ngx_quic_frame_s ngx_quic_frame_t;
-
-typedef struct {
- ngx_uint_t pn;
-
- // input
- uint64_t largest;
- uint64_t delay;
- uint64_t range_count;
- uint64_t first_range;
- uint64_t ranges[20];
- /* ecn counts */
-} ngx_quic_ack_frame_t;
-
-typedef struct {
- size_t offset;
- size_t len;
- u_char *data;
-} ngx_quic_crypto_frame_t;
-
-
-typedef struct {
- uint64_t seqnum;
- uint64_t retire;
- uint64_t len;
- u_char cid[20];
- u_char srt[16];
-} ngx_quic_ncid_t;
-
-
-typedef struct {
- uint8_t type;
- uint64_t stream_id;
- uint64_t offset;
- uint64_t length;
- u_char *data;
-} ngx_quic_stream_frame_t;
-
-
-typedef struct {
- uint64_t error_code;
- uint64_t frame_type;
- ngx_str_t reason;
-} ngx_quic_close_frame_t;
-
-
-struct ngx_quic_frame_s {
- ngx_uint_t type;
- ngx_quic_level_t level;
- ngx_quic_frame_t *next;
- union {
- ngx_quic_crypto_frame_t crypto;
- ngx_quic_ack_frame_t ack;
- ngx_quic_ncid_t ncid;
- ngx_quic_stream_frame_t stream;
- ngx_quic_close_frame_t close;
- // more frames
- } u;
-
- u_char info[128]; // for debug purposes
-};
-
-
struct ngx_quic_connection_s {
ngx_quic_state_t state;
@@ -229,16 +90,6 @@ static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, uint8_t alert);
-static ngx_int_t ngx_quic_process_long_header(ngx_connection_t *c,
- ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_process_short_header(ngx_connection_t *c,
- ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_process_initial_header(ngx_connection_t *c,
- ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_process_handshake_header(ngx_connection_t *c,
- ngx_quic_header_t *pkt);
-
-static uint64_t ngx_quic_parse_int(u_char **pos);
static ssize_t ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf,
size_t size);
@@ -282,6 +133,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
b = c->buffer;
+ pkt.log = c->log;
pkt.raw = b;
pkt.data = b->start;
pkt.len = b->last - b->start;
@@ -407,6 +259,7 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b)
pkt.raw = b;
pkt.data = p;
pkt.len = b->last - p;
+ pkt.log = c->log;
if (p[0] == 0) {
/* XXX: no idea WTF is this, just ignore */
@@ -448,11 +301,13 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b)
static ngx_int_t
ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc,
- ngx_quic_level_t level, ngx_str_t *payload)
+ enum ssl_encryption_level_t level, ngx_str_t *payload)
{
ngx_str_t res;
ngx_quic_header_t pkt;
+ pkt.log = c->log;
+
static ngx_str_t initial_token = ngx_null_string;
ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
@@ -494,139 +349,12 @@ ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc,
}
-static size_t
-ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack)
-{
- size_t len;
-
- /* minimal ACK packet */
-
- if (p == NULL) {
- len = ngx_quic_varint_len(NGX_QUIC_FT_ACK);
- len += ngx_quic_varint_len(ack->pn);
- len += ngx_quic_varint_len(0);
- len += ngx_quic_varint_len(0);
- len += ngx_quic_varint_len(ack->pn);
-
- return len;
- }
-
- ngx_quic_build_int(&p, NGX_QUIC_FT_ACK);
- ngx_quic_build_int(&p, ack->pn);
- ngx_quic_build_int(&p, 0);
- ngx_quic_build_int(&p, 0);
- ngx_quic_build_int(&p, ack->pn);
-
- return 5;
-}
-
-
-static size_t
-ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto)
-{
- size_t len;
- u_char *start;
-
- if (p == NULL) {
- len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO);
- len += ngx_quic_varint_len(crypto->offset);
- len += ngx_quic_varint_len(crypto->len);
- len += crypto->len;
-
- return len;
- }
-
- start = p;
-
- ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO);
- ngx_quic_build_int(&p, crypto->offset);
- ngx_quic_build_int(&p, crypto->len);
- p = ngx_cpymem(p, crypto->data, crypto->len);
-
- return p - start;
-}
-
-
-static size_t
-ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf)
-{
- size_t len;
- u_char *start;
-
- if (!ngx_quic_stream_bit_len(sf->type)) {
-#if 0
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "attempt to generate a stream frame without length");
-#endif
- // XXX: handle error in caller
- return NGX_ERROR;
- }
-
- if (p == NULL) {
- len = ngx_quic_varint_len(sf->type);
-
- if (ngx_quic_stream_bit_off(sf->type)) {
- len += ngx_quic_varint_len(sf->offset);
- }
-
- len += ngx_quic_varint_len(sf->stream_id);
-
- /* length is always present in generated frames */
- len += ngx_quic_varint_len(sf->length);
-
- len += sf->length;
-
- return len;
- }
-
- start = p;
-
- ngx_quic_build_int(&p, sf->type);
- ngx_quic_build_int(&p, sf->stream_id);
-
- if (ngx_quic_stream_bit_off(sf->type)) {
- ngx_quic_build_int(&p, sf->offset);
- }
-
- /* length is always present in generated frames */
- ngx_quic_build_int(&p, sf->length);
-
- p = ngx_cpymem(p, sf->data, sf->length);
-
- return p - start;
-}
-
-
-size_t
-ngx_quic_frame_len(ngx_quic_frame_t *frame)
-{
- switch (frame->type) {
- case NGX_QUIC_FT_ACK:
- return ngx_quic_create_ack(NULL, &frame->u.ack);
- case NGX_QUIC_FT_CRYPTO:
- return ngx_quic_create_crypto(NULL, &frame->u.crypto);
-
- case NGX_QUIC_FT_STREAM0:
- case NGX_QUIC_FT_STREAM1:
- case NGX_QUIC_FT_STREAM2:
- case NGX_QUIC_FT_STREAM3:
- case NGX_QUIC_FT_STREAM4:
- case NGX_QUIC_FT_STREAM5:
- case NGX_QUIC_FT_STREAM6:
- case NGX_QUIC_FT_STREAM7:
- return ngx_quic_create_stream(NULL, &frame->u.stream);
- default:
- /* BUG: unsupported frame type generated */
- return 0;
- }
-}
-
-
/* pack a group of frames [start; end) into memory p and send as single packet */
ngx_int_t
ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start,
ngx_quic_frame_t *end, size_t total)
{
+ ssize_t len;
u_char *p;
ngx_str_t out;
ngx_quic_frame_t *f;
@@ -645,30 +373,12 @@ ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start,
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "frame: %s", f->info);
- switch (f->type) {
- case NGX_QUIC_FT_ACK:
- p += ngx_quic_create_ack(p, &f->u.ack);
- break;
-
- case NGX_QUIC_FT_CRYPTO:
- p += ngx_quic_create_crypto(p, &f->u.crypto);
- break;
-
- case NGX_QUIC_FT_STREAM0:
- case NGX_QUIC_FT_STREAM1:
- case NGX_QUIC_FT_STREAM2:
- case NGX_QUIC_FT_STREAM3:
- case NGX_QUIC_FT_STREAM4:
- case NGX_QUIC_FT_STREAM5:
- case NGX_QUIC_FT_STREAM6:
- case NGX_QUIC_FT_STREAM7:
- p += ngx_quic_create_stream(p, &f->u.stream);
- break;
-
- default:
- /* BUG: unsupported frame type generated */
+ len = ngx_quic_create_frame(p, p + total, f);
+ if (len == -1) {
return NGX_ERROR;
}
+
+ p += len;
}
out.len = p - out.data;
@@ -892,279 +602,6 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
}
-static ngx_int_t
-ngx_quic_process_short_header(ngx_connection_t *c, ngx_quic_header_t *pkt)
-{
- u_char *p;
-
- p = pkt->data;
-
- ngx_quic_hexdump0(c->log, "short input", pkt->data, pkt->len);
-
- if ((p[0] & NGX_QUIC_PKT_LONG)) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "not a short packet");
- return NGX_ERROR;
- }
-
- pkt->flags = *p++;
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic flags:%xi", pkt->flags);
-
- if (ngx_memcmp(p, c->quic->dcid.data, c->quic->dcid.len) != 0) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcid");
- return NGX_ERROR;
- }
-
- pkt->dcid.len = c->quic->dcid.len;
- pkt->dcid.data = p;
- p += pkt->dcid.len;
-
- pkt->raw->pos = p;
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_quic_process_long_header(ngx_connection_t *c, ngx_quic_header_t *pkt)
-{
- u_char *p;
-
- p = pkt->data;
-
- ngx_quic_hexdump0(c->log, "long input", pkt->data, pkt->len);
-
- if (!(p[0] & NGX_QUIC_PKT_LONG)) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "not a long packet");
- return NGX_ERROR;
- }
-
- pkt->flags = *p++;
-
- pkt->version = ngx_quic_parse_uint32(p);
- p += sizeof(uint32_t);
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic flags:%xi version:%xD", pkt->flags, pkt->version);
-
- if (pkt->version != quic_version) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "unsupported quic version");
- return NGX_ERROR;
- }
-
- pkt->dcid.len = *p++;
- pkt->dcid.data = p;
- p += pkt->dcid.len;
-
- pkt->scid.len = *p++;
- pkt->scid.data = p;
- p += pkt->scid.len;
-
- pkt->raw->pos = p;
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_quic_process_initial_header(ngx_connection_t *c, ngx_quic_header_t *pkt)
-{
- u_char *p;
- ngx_int_t plen;
-
- p = pkt->raw->pos;
-
- pkt->token.len = ngx_quic_parse_int(&p);
- pkt->token.data = p;
-
- p += pkt->token.len;
-
- plen = ngx_quic_parse_int(&p);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic packet length: %d", plen);
-
- if (plen > pkt->data + pkt->len - p) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "truncated initial packet");
- return NGX_ERROR;
- }
-
- pkt->raw->pos = p;
- pkt->len = plen;
-
- ngx_quic_hexdump0(c->log, "DCID", pkt->dcid.data, pkt->dcid.len);
- ngx_quic_hexdump0(c->log, "SCID", pkt->scid.data, pkt->scid.len);
- ngx_quic_hexdump0(c->log, "token", pkt->token.data, pkt->token.len);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic packet length: %d", plen);
-
- return NGX_OK;
-}
-
-static ngx_int_t
-ngx_quic_process_handshake_header(ngx_connection_t *c, ngx_quic_header_t *pkt)
-{
- u_char *p;
- ngx_int_t plen;
-
- p = pkt->raw->pos;
-
- plen = ngx_quic_parse_int(&p);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic packet length: %d", plen);
-
- if (plen > pkt->data + pkt->len - p) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "truncated handshake packet");
- return NGX_ERROR;
- }
-
- pkt->raw->pos = p;
- pkt->len = plen;
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic packet length: %d", plen);
-
- return NGX_OK;
-}
-
-
-ssize_t
-ngx_quic_read_frame(ngx_connection_t *c, u_char *start, u_char *end,
- ngx_quic_frame_t *frame)
-{
- u_char *p;
-
- size_t npad;
-
- p = start;
-
- frame->type = *p++; // TODO: check overflow (p < end)
-
- switch (frame->type) {
-
- case NGX_QUIC_FT_CRYPTO:
- frame->u.crypto.offset = *p++;
- frame->u.crypto.len = ngx_quic_parse_int(&p);
- frame->u.crypto.data = p;
- p += frame->u.crypto.len;
-
- ngx_quic_hexdump0(c->log, "CRYPTO frame",
- frame->u.crypto.data, frame->u.crypto.len);
-
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic CRYPTO frame length: %uL off:%uL pp:%p",
- frame->u.crypto.len, frame->u.crypto.offset,
- frame->u.crypto.data);
- break;
-
- case NGX_QUIC_FT_PADDING:
- npad = 0;
- while (p < end && *p == NGX_QUIC_FT_PADDING) { // XXX
- p++; npad++;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "PADDING frame length %uL", npad);
-
- break;
-
- case NGX_QUIC_FT_ACK:
- case NGX_QUIC_FT_ACK_ECN:
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ACK frame");
-
- frame->u.ack.largest = ngx_quic_parse_int(&p);
- frame->u.ack.delay = ngx_quic_parse_int(&p);
- frame->u.ack.range_count =ngx_quic_parse_int(&p);
- frame->u.ack.first_range =ngx_quic_parse_int(&p);
-
- if (frame->u.ack.range_count) {
- frame->u.ack.ranges[0] = ngx_quic_parse_int(&p);
- }
-
- if (frame->type ==NGX_QUIC_FT_ACK_ECN) {
- return NGX_ERROR;
- }
-
- break;
-
- case NGX_QUIC_FT_PING:
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "PING frame");
- break;
-
- case NGX_QUIC_FT_NEW_CONNECTION_ID:
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "NCID frame");
-
- frame->u.ncid.seqnum = ngx_quic_parse_int(&p);
- frame->u.ncid.retire = ngx_quic_parse_int(&p);
- frame->u.ncid.len = *p++;
- ngx_memcpy(frame->u.ncid.cid, p, frame->u.ncid.len);
- p += frame->u.ncid.len;
-
- ngx_memcpy(frame->u.ncid.srt, p, 16);
- p += 16;
-
- break;
-
- case NGX_QUIC_FT_CONNECTION_CLOSE:
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "connection close frame");
-
- frame->u.close.error_code = ngx_quic_parse_int(&p);
- frame->u.close.frame_type = ngx_quic_parse_int(&p); // not in 0x1d CC
- frame->u.close.reason.len = ngx_quic_parse_int(&p);
- frame->u.close.reason.data = p;
- p += frame->u.close.reason.len;
-
- if (frame->u.close.error_code > NGX_QUIC_ERR_LAST) {
- frame->u.close.error_code = NGX_QUIC_ERR_LAST;
- }
- break;
-
- case NGX_QUIC_FT_STREAM0:
- case NGX_QUIC_FT_STREAM1:
- case NGX_QUIC_FT_STREAM2:
- case NGX_QUIC_FT_STREAM3:
- case NGX_QUIC_FT_STREAM4:
- case NGX_QUIC_FT_STREAM5:
- case NGX_QUIC_FT_STREAM6:
- case NGX_QUIC_FT_STREAM7:
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "STREAM frame, type: 0x%xi", frame->type);
-
- frame->u.stream.type = frame->type;
-
- frame->u.stream.stream_id = ngx_quic_parse_int(&p);
- if (frame->type & 0x04) {
- frame->u.stream.offset = ngx_quic_parse_int(&p);
- } else {
- frame->u.stream.offset = 0;
- }
-
- if (frame->type & 0x02) {
- frame->u.stream.length = ngx_quic_parse_int(&p);
- } else {
- frame->u.stream.length = end - p; /* up to packet end */
- }
-
- frame->u.stream.data = p;
-
- p += frame->u.stream.length;
-
- break;
-
- default:
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "unknown frame type %xi", frame->type);
- return NGX_ERROR;
- }
-
- return p - start;
-}
-
static ngx_int_t
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
@@ -1349,6 +786,9 @@ ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size)
frame->level = ssl_encryption_application;
frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */
+ frame->u.stream.off = 1;
+ frame->u.stream.len = 1;
+ frame->u.stream.fin = 0;
frame->u.stream.type = frame->type;
frame->u.stream.stream_id = qs->id;
@@ -1433,8 +873,11 @@ ngx_quic_payload_handler(ngx_connection_t *c, ngx_quic_header_t *pkt)
while (p < end) {
- len = ngx_quic_read_frame(c, p, end, &frame);
+ len = ngx_quic_parse_frame(p, end, &frame);
if (len < 0) {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "unknown frame type %xi", frame.type);
+ // XXX: log here
return NGX_ERROR;
}
@@ -1456,6 +899,13 @@ ngx_quic_payload_handler(ngx_connection_t *c, ngx_quic_header_t *pkt)
break;
case NGX_QUIC_FT_CRYPTO:
+ ngx_quic_hexdump0(c->log, "CRYPTO frame",
+ frame.u.crypto.data, frame.u.crypto.len);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic CRYPTO frame length: %uL off:%uL pp:%p",
+ frame.u.crypto.len, frame.u.crypto.offset,
+ frame.u.crypto.data);
if (frame.u.crypto.offset != 0x0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
@@ -1491,7 +941,7 @@ ngx_quic_payload_handler(ngx_connection_t *c, ngx_quic_header_t *pkt)
case NGX_QUIC_FT_CONNECTION_CLOSE:
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
"CONN.CLOSE: { %s (0x%xi) type=0x%xi reason='%V'}",
- ngx_quic_errors[frame.u.close.error_code],
+ ngx_quic_error_text(frame.u.close.error_code),
frame.u.close.error_code,
frame.u.close.frame_type,
&frame.u.close.reason);
@@ -1516,10 +966,9 @@ ngx_quic_payload_handler(ngx_connection_t *c, ngx_quic_header_t *pkt)
frame.u.stream.stream_id,
frame.u.stream.offset,
frame.u.stream.length,
- ngx_quic_stream_bit_off(frame.u.stream.type),
- ngx_quic_stream_bit_len(frame.u.stream.type),
- ngx_quic_stream_bit_fin(frame.u.stream.type));
-
+ frame.u.stream.off,
+ frame.u.stream.len,
+ frame.u.stream.fin);
sn = ngx_quic_stream_lookup(&qc->stree, frame.u.stream.stream_id);
if (sn == NULL) {
@@ -1727,7 +1176,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
return NGX_ERROR;
}
- if (ngx_quic_process_long_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
@@ -1737,7 +1186,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
return NGX_ERROR;
}
- if (ngx_quic_process_initial_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
@@ -1804,11 +1253,11 @@ ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
qc = c->quic;
ssl_conn = c->ssl->connection;
- if (ngx_quic_process_long_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
- if (ngx_quic_process_initial_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
@@ -1833,7 +1282,7 @@ ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
ssl_conn = c->ssl->connection;
/* extract cleartext data into pkt */
- if (ngx_quic_process_long_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
@@ -1863,7 +1312,7 @@ ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
return NGX_ERROR;
}
- if (ngx_quic_process_handshake_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
@@ -1891,7 +1340,7 @@ ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
return NGX_DECLINED;
}
- if (ngx_quic_process_short_header(c, pkt) != NGX_OK) {
+ if (ngx_quic_parse_short_header(pkt, &qc->dcid) != NGX_OK) {
return NGX_ERROR;
}
@@ -1906,52 +1355,3 @@ ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
-uint64_t
-ngx_quic_parse_int(u_char **pos)
-{
- u_char *p;
- uint64_t value;
- ngx_uint_t len;
-
- p = *pos;
- len = 1 << ((*p & 0xc0) >> 6);
- value = *p++ & 0x3f;
-
- while (--len) {
- value = (value << 8) + *p++;
- }
-
- *pos = p;
- return value;
-}
-
-
-void
-ngx_quic_build_int(u_char **pos, uint64_t value)
-{
- u_char *p;
- ngx_uint_t len;//, len2;
-
- p = *pos;
- len = 0;
-
- while (value >> ((1 << len) * 8 - 2)) {
- len++;
- }
-
- *p = len << 6;
-
-// len2 =
- len = (1 << len);
- len--;
- *p |= value >> (len * 8);
- p++;
-
- while (len) {
- *p++ = value >> ((len-- - 1) * 8);
- }
-
- *pos = p;
-// return len2;
-}
-
diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h
index 252875569..b3b86d99b 100644
--- a/src/event/ngx_event_quic.h
+++ b/src/event/ngx_event_quic.h
@@ -11,57 +11,8 @@
#include <ngx_event_openssl.h>
-#define quic_version 0xff000018 /* draft-24 (ngtcp2) */
-//#define quic_version 0xff00001b /* draft-27 (FFN 76) */
-
-/* 17.2. Long Header Packets */
-
-#define NGX_QUIC_PKT_LONG 0x80
-
-#define NGX_QUIC_PKT_INITIAL 0xc0
-#define NGX_QUIC_PKT_HANDSHAKE 0xe0
-
-
-#if (NGX_HAVE_NONALIGNED)
-
-#define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p))
-#define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p))
-
-#define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned
-#define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned
-
-#else
-
-#define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1])
-#define ngx_quic_parse_uint32(p) \
- ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
-
-#define ngx_quic_write_uint16(p, s) \
- ((p)[0] = (u_char) ((s) >> 8), \
- (p)[1] = (u_char) (s), \
- (p) + sizeof(uint16_t))
-
-#define ngx_quic_write_uint32(p, s) \
- ((p)[0] = (u_char) ((s) >> 24), \
- (p)[1] = (u_char) ((s) >> 16), \
- (p)[2] = (u_char) ((s) >> 8), \
- (p)[3] = (u_char) (s), \
- (p) + sizeof(uint32_t))
-
-#endif
-
-
-#define ngx_quic_write_uint16_aligned(p, s) \
- (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
-
-#define ngx_quic_write_uint32_aligned(p, s) \
- (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
-
-#define ngx_quic_varint_len(value) \
- ((value) <= 63 ? 1 \
- : ((uint32_t) value) <= 16383 ? 2 \
- : ((uint64_t) value) <= 1073741823 ? 4 \
- : 8)
+#define quic_version 0xff000018 /* draft-24 (ngtcp2) */
+//#define quic_version 0xff00001b /* draft-27 (FFN 76) */
struct ngx_quic_stream_s {
@@ -71,35 +22,6 @@ struct ngx_quic_stream_s {
void *data;
};
-typedef struct ngx_quic_secret_s ngx_quic_secret_t;
-typedef enum ssl_encryption_level_t ngx_quic_level_t;
-
-typedef struct {
- ngx_quic_secret_t *secret;
- ngx_uint_t type;
- ngx_uint_t *number;
- ngx_uint_t flags;
- uint32_t version;
- ngx_str_t token;
- ngx_quic_level_t level;
-
- /* filled in by parser */
- ngx_buf_t *raw; /* udp datagram from wire */
-
- u_char *data; /* quic packet */
- size_t len;
-
- /* cleartext fields */
- ngx_str_t dcid;
- ngx_str_t scid;
-
- uint64_t pn;
-
- ngx_str_t payload; /* decrypted payload */
-
-} ngx_quic_header_t;
-
-void ngx_quic_build_int(u_char **pos, uint64_t value);
void ngx_quic_init_ssl_methods(SSL_CTX* ctx);
diff --git a/src/event/ngx_event_quic_protection.c b/src/event/ngx_event_quic_protection.c
index 544adaace..23b8c011f 100644
--- a/src/event/ngx_event_quic_protection.c
+++ b/src/event/ngx_event_quic_protection.c
@@ -702,30 +702,7 @@ ngx_quic_create_long_packet(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
return NGX_ERROR;
}
- p = ad.data;
-
- *p++ = pkt->flags;
-
- p = ngx_quic_write_uint32(p, quic_version);
-
- *p++ = pkt->scid.len;
- p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
-
- *p++ = pkt->dcid.len;
- p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);
-
- if (pkt->level == ssl_encryption_initial) {
- ngx_quic_build_int(&p, pkt->token.len);
- }
-
- ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl)
- pnp = p;
-
- pn = *pkt->number;
-
- *p++ = pn;
-
- ad.len = p - ad.data;
+ ad.len = ngx_quic_create_long_header(pkt, &ad, out.len, &pnp);
ngx_quic_hexdump0(log, "ad", ad.data, ad.len);
@@ -734,9 +711,8 @@ ngx_quic_create_long_packet(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
}
nonce = ngx_pstrdup(pool, &pkt->secret->iv);
- if (pkt->level == ssl_encryption_handshake) {
- nonce[11] ^= pn;
- }
+ pn = *pkt->number;
+ nonce[11] ^= pn;
ngx_quic_hexdump0(log, "server_iv", pkt->secret->iv.data, 12);
ngx_quic_hexdump0(log, "nonce", nonce, 12);
diff --git a/src/event/ngx_event_quic_protection.h b/src/event/ngx_event_quic_protection.h
index 80dcf110e..499301f41 100644
--- a/src/event/ngx_event_quic_protection.h
+++ b/src/event/ngx_event_quic_protection.h
@@ -8,12 +8,12 @@
#define _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_
-struct ngx_quic_secret_s {
+typedef struct ngx_quic_secret_s {
ngx_str_t secret;
ngx_str_t key;
ngx_str_t iv;
ngx_str_t hp;
-};
+} ngx_quic_secret_t;
typedef struct {
diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c
new file mode 100644
index 000000000..1f49c10f4
--- /dev/null
+++ b/src/event/ngx_event_quic_transport.c
@@ -0,0 +1,588 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p))
+#define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p))
+
+#define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned
+#define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned
+
+#else
+
+#define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1])
+#define ngx_quic_parse_uint32(p) \
+ ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
+
+#define ngx_quic_write_uint16(p, s) \
+ ((p)[0] = (u_char) ((s) >> 8), \
+ (p)[1] = (u_char) (s), \
+ (p) + sizeof(uint16_t))
+
+#define ngx_quic_write_uint32(p, s) \
+ ((p)[0] = (u_char) ((s) >> 24), \
+ (p)[1] = (u_char) ((s) >> 16), \
+ (p)[2] = (u_char) ((s) >> 8), \
+ (p)[3] = (u_char) (s), \
+ (p) + sizeof(uint32_t))
+
+#endif
+
+
+#define ngx_quic_write_uint16_aligned(p, s) \
+ (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
+
+#define ngx_quic_write_uint32_aligned(p, s) \
+ (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
+
+#define ngx_quic_varint_len(value) \
+ ((value) <= 63 ? 1 \
+ : ((uint32_t) value) <= 16383 ? 2 \
+ : ((uint64_t) value) <= 1073741823 ? 4 \
+ : 8)
+
+
+static uint64_t ngx_quic_parse_int(u_char **pos);
+static void ngx_quic_build_int(u_char **pos, uint64_t value);
+
+static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack);
+static size_t ngx_quic_create_crypto(u_char *p,
+ ngx_quic_crypto_frame_t *crypto);
+static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf);
+
+
+/* literal errors indexed by corresponding value */
+static char *ngx_quic_errors[] = {
+ "NO_ERROR",
+ "INTERNAL_ERROR",
+ "SERVER_BUSY",
+ "FLOW_CONTROL_ERROR",
+ "STREAM_LIMIT_ERROR",
+ "STREAM_STATE_ERROR",
+ "FINAL_SIZE_ERROR",
+ "FRAME_ENCODING_ERROR",
+ "TRANSPORT_PARAMETER_ERROR",
+ "CONNECTION_ID_LIMIT_ERROR",
+ "PROTOCOL_VIOLATION",
+ "INVALID_TOKEN",
+ "",
+ "CRYPTO_BUFFER_EXCEEDED",
+ "CRYPTO_ERROR",
+};
+
+
+static uint64_t
+ngx_quic_parse_int(u_char **pos)
+{
+ u_char *p;
+ uint64_t value;
+ ngx_uint_t len;
+
+ p = *pos;
+ len = 1 << ((*p & 0xc0) >> 6);
+ value = *p++ & 0x3f;
+
+ while (--len) {
+ value = (value << 8) + *p++;
+ }
+
+ *pos = p;
+ return value;
+}
+
+
+static void
+ngx_quic_build_int(u_char **pos, uint64_t value)
+{
+ u_char *p;
+ ngx_uint_t len;//, len2;
+
+ p = *pos;
+ len = 0;
+
+ while (value >> ((1 << len) * 8 - 2)) {
+ len++;
+ }
+
+ *p = len << 6;
+
+// len2 =
+ len = (1 << len);
+ len--;
+ *p |= value >> (len * 8);
+ p++;
+
+ while (len) {
+ *p++ = value >> ((len-- - 1) * 8);
+ }
+
+ *pos = p;
+// return len2;
+}
+
+
+u_char *
+ngx_quic_error_text(uint64_t error_code)
+{
+ return (u_char *) ngx_quic_errors[error_code];
+}
+
+
+ngx_int_t
+ngx_quic_parse_long_header(ngx_quic_header_t *pkt)
+{
+ u_char *p;
+
+ p = pkt->data;
+
+ ngx_quic_hexdump0(pkt->log, "long input", pkt->data, pkt->len);
+
+ if (!(p[0] & NGX_QUIC_PKT_LONG)) {
+ ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a long packet");
+ return NGX_ERROR;
+ }
+
+ pkt->flags = *p++;
+
+ pkt->version = ngx_quic_parse_uint32(p);
+ p += sizeof(uint32_t);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
+ "quic flags:%xi version:%xD", pkt->flags, pkt->version);
+
+ if (pkt->version != quic_version) {
+ ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unsupported quic version");
+ return NGX_ERROR;
+ }
+
+ pkt->dcid.len = *p++;
+ pkt->dcid.data = p;
+ p += pkt->dcid.len;
+
+ pkt->scid.len = *p++;
+ pkt->scid.data = p;
+ p += pkt->scid.len;
+
+ pkt->raw->pos = p;
+
+ return NGX_OK;
+}
+
+
+size_t
+ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out,
+ size_t pkt_len, u_char **pnp)
+{
+ u_char *p, *start;
+
+ p = start = out->data;
+
+ *p++ = pkt->flags;
+
+ p = ngx_quic_write_uint32(p, quic_version);
+
+ *p++ = pkt->scid.len;
+ p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
+
+ *p++ = pkt->dcid.len;
+ p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);
+
+ if (pkt->level == ssl_encryption_initial) {
+ ngx_quic_build_int(&p, pkt->token.len);
+ }
+
+ ngx_quic_build_int(&p, pkt_len + 1); // length (inc. pnl)
+
+ *pnp = p;
+
+ *p++ = (uint64_t) (*pkt->number);
+
+ return p - start;
+}
+
+
+ngx_int_t
+ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
+{
+ u_char *p;
+
+ p = pkt->data;
+
+ ngx_quic_hexdump0(pkt->log, "short input", pkt->data, pkt->len);
+
+ if ((p[0] & NGX_QUIC_PKT_LONG)) {
+ ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a short packet");
+ return NGX_ERROR;
+ }
+
+ pkt->flags = *p++;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
+ "quic flags:%xi", pkt->flags);
+
+ if (ngx_memcmp(p, dcid->data, dcid->len) != 0) {
+ ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unexpected quic dcid");
+ return NGX_ERROR;
+ }
+
+ pkt->dcid.len = dcid->len;
+ pkt->dcid.data = p;
+ p += pkt->dcid.len;
+
+ pkt->raw->pos = p;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_parse_initial_header(ngx_quic_header_t *pkt)
+{
+ u_char *p;
+ ngx_int_t plen;
+
+ p = pkt->raw->pos;
+
+ pkt->token.len = ngx_quic_parse_int(&p);
+ pkt->token.data = p;
+
+ p += pkt->token.len;
+
+ plen = ngx_quic_parse_int(&p);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
+ "quic packet length: %d", plen);
+
+ if (plen > pkt->data + pkt->len - p) {
+ ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated initial packet");
+ return NGX_ERROR;
+ }
+
+ pkt->raw->pos = p;
+ pkt->len = plen;
+
+ ngx_quic_hexdump0(pkt->log, "DCID", pkt->dcid.data, pkt->dcid.len);
+ ngx_quic_hexdump0(pkt->log, "SCID", pkt->scid.data, pkt->scid.len);
+ ngx_quic_hexdump0(pkt->log, "token", pkt->token.data, pkt->token.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
+ "quic packet length: %d", plen);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt)
+{
+ u_char *p;
+ ngx_int_t plen;
+
+ p = pkt->raw->pos;
+
+ plen = ngx_quic_parse_int(&p);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
+ "quic packet length: %d", plen);
+
+ if (plen > pkt->data + pkt->len - p) {
+ ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated handshake packet");
+ return NGX_ERROR;
+ }
+
+ pkt->raw->pos = p;
+ pkt->len = plen;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
+ "quic packet length: %d", plen);
+
+ return NGX_OK;
+}
+
+
+#define ngx_quic_stream_bit_off(val) (((val) & 0x04) ? 1 : 0)
+#define ngx_quic_stream_bit_len(val) (((val) & 0x02) ? 1 : 0)
+#define ngx_quic_stream_bit_fin(val) (((val) & 0x01) ? 1 : 0)
+
+ssize_t
+ngx_quic_parse_frame(u_char *start, u_char *end, ngx_quic_frame_t *frame)
+{
+ u_char *p;
+
+ size_t npad;
+
+ p = start;
+
+ frame->type = *p++; // TODO: check overflow (p < end)
+
+ switch (frame->type) {
+
+ case NGX_QUIC_FT_CRYPTO:
+ frame->u.crypto.offset = *p++;
+ frame->u.crypto.len = ngx_quic_parse_int(&p);
+ frame->u.crypto.data = p;
+ p += frame->u.crypto.len;
+
+ break;
+
+ case NGX_QUIC_FT_PADDING:
+ npad = 0;
+ while (p < end && *p == NGX_QUIC_FT_PADDING) { // XXX
+ p++; npad++;
+ }
+
+ break;
+
+ case NGX_QUIC_FT_ACK:
+ case NGX_QUIC_FT_ACK_ECN:
+
+ frame->u.ack.largest = ngx_quic_parse_int(&p);
+ frame->u.ack.delay = ngx_quic_parse_int(&p);
+ frame->u.ack.range_count =ngx_quic_parse_int(&p);
+ frame->u.ack.first_range =ngx_quic_parse_int(&p);
+
+ if (frame->u.ack.range_count) {
+ frame->u.ack.ranges[0] = ngx_quic_parse_int(&p);
+ }
+
+ if (frame->type ==NGX_QUIC_FT_ACK_ECN) {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ case NGX_QUIC_FT_PING:
+ break;
+
+ case NGX_QUIC_FT_NEW_CONNECTION_ID:
+
+ frame->u.ncid.seqnum = ngx_quic_parse_int(&p);
+ frame->u.ncid.retire = ngx_quic_parse_int(&p);
+ frame->u.ncid.len = *p++;
+ ngx_memcpy(frame->u.ncid.cid, p, frame->u.ncid.len);
+ p += frame->u.ncid.len;
+
+ ngx_memcpy(frame->u.ncid.srt, p, 16);
+ p += 16;
+
+ break;
+
+ case NGX_QUIC_FT_CONNECTION_CLOSE:
+
+ frame->u.close.error_code = ngx_quic_parse_int(&p);
+ frame->u.close.frame_type = ngx_quic_parse_int(&p); // not in 0x1d CC
+ frame->u.close.reason.len = ngx_quic_parse_int(&p);
+ frame->u.close.reason.data = p;
+ p += frame->u.close.reason.len;
+
+ if (frame->u.close.error_code > NGX_QUIC_ERR_LAST) {
+ frame->u.close.error_code = NGX_QUIC_ERR_LAST;
+ }
+ break;
+
+ case NGX_QUIC_FT_STREAM0:
+ case NGX_QUIC_FT_STREAM1:
+ case NGX_QUIC_FT_STREAM2:
+ case NGX_QUIC_FT_STREAM3:
+ case NGX_QUIC_FT_STREAM4:
+ case NGX_QUIC_FT_STREAM5:
+ case NGX_QUIC_FT_STREAM6:
+ case NGX_QUIC_FT_STREAM7:
+
+ frame->u.stream.type = frame->type;
+
+ frame->u.stream.off = ngx_quic_stream_bit_off(frame->type);
+ frame->u.stream.len = ngx_quic_stream_bit_len(frame->type);
+ frame->u.stream.fin = ngx_quic_stream_bit_fin(frame->type);
+
+ frame->u.stream.stream_id = ngx_quic_parse_int(&p);
+ if (frame->type & 0x04) {
+ frame->u.stream.offset = ngx_quic_parse_int(&p);
+ } else {
+ frame->u.stream.offset = 0;
+ }
+
+ if (frame->type & 0x02) {
+ frame->u.stream.length = ngx_quic_parse_int(&p);
+ } else {
+ frame->u.stream.length = end - p; /* up to packet end */
+ }
+
+ frame->u.stream.data = p;
+
+ p += frame->u.stream.length;
+
+ break;
+
+ default:
+ return NGX_ERROR;
+ }
+
+ return p - start;
+}
+
+
+ssize_t
+ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f)
+{
+ // TODO: handle end arg
+
+ switch (f->type) {
+ case NGX_QUIC_FT_ACK:
+ return ngx_quic_create_ack(p, &f->u.ack);
+
+ case NGX_QUIC_FT_CRYPTO:
+ return ngx_quic_create_crypto(p, &f->u.crypto);
+
+ case NGX_QUIC_FT_STREAM0:
+ case NGX_QUIC_FT_STREAM1:
+ case NGX_QUIC_FT_STREAM2:
+ case NGX_QUIC_FT_STREAM3:
+ case NGX_QUIC_FT_STREAM4:
+ case NGX_QUIC_FT_STREAM5:
+ case NGX_QUIC_FT_STREAM6:
+ case NGX_QUIC_FT_STREAM7:
+ return ngx_quic_create_stream(p, &f->u.stream);
+
+ default:
+ /* BUG: unsupported frame type generated */
+ return NGX_ERROR;
+ }
+}
+
+
+size_t
+ngx_quic_frame_len(ngx_quic_frame_t *frame)
+{
+ switch (frame->type) {
+ case NGX_QUIC_FT_ACK:
+ return ngx_quic_create_ack(NULL, &frame->u.ack);
+ case NGX_QUIC_FT_CRYPTO:
+ return ngx_quic_create_crypto(NULL, &frame->u.crypto);
+
+ case NGX_QUIC_FT_STREAM0:
+ case NGX_QUIC_FT_STREAM1:
+ case NGX_QUIC_FT_STREAM2:
+ case NGX_QUIC_FT_STREAM3:
+ case NGX_QUIC_FT_STREAM4:
+ case NGX_QUIC_FT_STREAM5:
+ case NGX_QUIC_FT_STREAM6:
+ case NGX_QUIC_FT_STREAM7:
+ return ngx_quic_create_stream(NULL, &frame->u.stream);
+ default:
+ /* BUG: unsupported frame type generated */
+ return 0;
+ }
+}
+
+
+static size_t
+ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack)
+{
+ size_t len;
+
+ /* minimal ACK packet */
+
+ if (p == NULL) {
+ len = ngx_quic_varint_len(NGX_QUIC_FT_ACK);
+ len += ngx_quic_varint_len(ack->pn);
+ len += ngx_quic_varint_len(0);
+ len += ngx_quic_varint_len(0);
+ len += ngx_quic_varint_len(ack->pn);
+
+ return len;
+ }
+
+ ngx_quic_build_int(&p, NGX_QUIC_FT_ACK);
+ ngx_quic_build_int(&p, ack->pn);
+ ngx_quic_build_int(&p, 0);
+ ngx_quic_build_int(&p, 0);
+ ngx_quic_build_int(&p, ack->pn);
+
+ return 5;
+}
+
+
+static size_t
+ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto)
+{
+ size_t len;
+ u_char *start;
+
+ if (p == NULL) {
+ len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO);
+ len += ngx_quic_varint_len(crypto->offset);
+ len += ngx_quic_varint_len(crypto->len);
+ len += crypto->len;
+
+ return len;
+ }
+
+ start = p;
+
+ ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO);
+ ngx_quic_build_int(&p, crypto->offset);
+ ngx_quic_build_int(&p, crypto->len);
+ p = ngx_cpymem(p, crypto->data, crypto->len);
+
+ return p - start;
+}
+
+
+static size_t
+ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf)
+{
+ size_t len;
+ u_char *start;
+
+ if (!sf->len) {
+#if 0
+ ngx_log_error(NGX_LOG_INFO, log, 0,
+ "attempt to generate a stream frame without length");
+#endif
+ // XXX: handle error in caller
+ return NGX_ERROR;
+ }
+
+ if (p == NULL) {
+ len = ngx_quic_varint_len(sf->type);
+
+ if (sf->off) {
+ len += ngx_quic_varint_len(sf->offset);
+ }
+
+ len += ngx_quic_varint_len(sf->stream_id);
+
+ /* length is always present in generated frames */
+ len += ngx_quic_varint_len(sf->length);
+
+ len += sf->length;
+
+ return len;
+ }
+
+ start = p;
+
+ ngx_quic_build_int(&p, sf->type);
+ ngx_quic_build_int(&p, sf->stream_id);
+
+ if (sf->off) {
+ ngx_quic_build_int(&p, sf->offset);
+ }
+
+ /* length is always present in generated frames */
+ ngx_quic_build_int(&p, sf->length);
+
+ p = ngx_cpymem(p, sf->data, sf->length);
+
+ return p - start;
+}
diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h
new file mode 100644
index 000000000..549cc9757
--- /dev/null
+++ b/src/event/ngx_event_quic_transport.h
@@ -0,0 +1,177 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_WIRE_H_INCLUDED_
+#define _NGX_EVENT_QUIC_WIRE_H_INCLUDED_
+
+
+#include <ngx_event_openssl.h>
+
+
+/* 17.2. Long Header Packets */
+#define NGX_QUIC_PKT_LONG 0x80
+
+#define NGX_QUIC_PKT_INITIAL 0xC0
+#define NGX_QUIC_PKT_HANDSHAKE 0xE0
+
+/* 12.4. Frames and Frame Types */
+#define NGX_QUIC_FT_PADDING 0x00
+#define NGX_QUIC_FT_PING 0x01
+#define NGX_QUIC_FT_ACK 0x02
+#define NGX_QUIC_FT_ACK_ECN 0x03
+#define NGX_QUIC_FT_RESET_STREAM 0x04
+#define NGX_QUIC_FT_STOP_SENDING 0x05
+#define NGX_QUIC_FT_CRYPTO 0x06
+#define NGX_QUIC_FT_NEW_TOKEN 0x07
+#define NGX_QUIC_FT_STREAM0 0x08
+#define NGX_QUIC_FT_STREAM1 0x09
+#define NGX_QUIC_FT_STREAM2 0x0A
+#define NGX_QUIC_FT_STREAM3 0x0B
+#define NGX_QUIC_FT_STREAM4 0x0C
+#define NGX_QUIC_FT_STREAM5 0x0D
+#define NGX_QUIC_FT_STREAM6 0x0E
+#define NGX_QUIC_FT_STREAM7 0x0F
+#define NGX_QUIC_FT_MAX_DATA 0x10
+#define NGX_QUIC_FT_MAX_STREAM_DATA 0x11
+#define NGX_QUIC_FT_MAX_STREAMS 0x12
+#define NGX_QUIC_FT_MAX_STREAMS2 0x13 // XXX
+#define NGX_QUIC_FT_DATA_BLOCKED 0x14
+#define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15
+#define NGX_QUIC_FT_STREAMS_BLOCKED 0x16
+#define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17 // XXX
+#define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18
+#define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19
+#define NGX_QUIC_FT_PATH_CHALLENGE 0x1A
+#define NGX_QUIC_FT_PATH_RESPONSE 0x1B
+#define NGX_QUIC_FT_CONNECTION_CLOSE 0x1C
+#define NGX_QUIC_FT_CONNECTION_CLOSE2 0x1D // XXX
+#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1E
+
+/* 22.4. QUIC Transport Error Codes Registry */
+#define NGX_QUIC_ERR_NO_ERROR 0x00
+#define NGX_QUIC_ERR_INTERNAL_ERROR 0x01
+#define NGX_QUIC_ERR_SERVER_BUSY 0x02
+#define NGX_QUIC_ERR_FLOW_CONTROL_ERROR 0x03
+#define NGX_QUIC_ERR_STREAM_LIMIT_ERROR 0x04
+#define NGX_QUIC_ERR_STREAM_STATE_ERROR 0x05
+#define NGX_QUIC_ERR_FINAL_SIZE_ERROR 0x06
+#define NGX_QUIC_ERR_FRAME_ENCODING_ERROR 0x07
+#define NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR 0x08
+#define NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR 0x09
+#define NGX_QUIC_ERR_PROTOCOL_VIOLATION 0x0A
+#define NGX_QUIC_ERR_INVALID_TOKEN 0x0B
+/* 0xC is not defined */
+#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0x0D
+#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10
+
+#define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR
+
+
+typedef struct {
+ ngx_uint_t pn;
+ uint64_t largest;
+ uint64_t delay;
+ uint64_t range_count;
+ uint64_t first_range;
+ uint64_t ranges[20];
+ /* TODO: ecn counts */
+} ngx_quic_ack_frame_t;
+
+
+typedef struct {
+ size_t offset;
+ size_t len;
+ u_char *data;
+} ngx_quic_crypto_frame_t;
+
+
+typedef struct {
+ uint64_t seqnum;
+ uint64_t retire;
+ uint64_t len;
+ u_char cid[20];
+ u_char srt[16];
+} ngx_quic_new_conn_id_frame_t;
+
+
+typedef struct {
+ uint8_t type;
+ uint64_t stream_id;
+ uint64_t offset;
+ uint64_t length;
+ unsigned off:1;
+ unsigned len:1;
+ unsigned fin:1;
+ u_char *data;
+} ngx_quic_stream_frame_t;
+
+
+typedef struct {
+ uint64_t error_code;
+ uint64_t frame_type;
+ ngx_str_t reason;
+} ngx_quic_close_frame_t;
+
+
+typedef struct ngx_quic_frame_s ngx_quic_frame_t;
+
+struct ngx_quic_frame_s {
+ ngx_uint_t type;
+ enum ssl_encryption_level_t level;
+ ngx_quic_frame_t *next;
+ union {
+ ngx_quic_ack_frame_t ack;
+ ngx_quic_crypto_frame_t crypto;
+ ngx_quic_new_conn_id_frame_t ncid;
+ ngx_quic_stream_frame_t stream;
+ ngx_quic_close_frame_t close;
+ } u;
+ u_char info[128]; // for debug
+};
+
+
+typedef struct {
+ ngx_log_t *log;
+
+ struct ngx_quic_secret_s *secret;
+ ngx_uint_t type;
+ ngx_uint_t *number;
+ ngx_uint_t flags;
+ uint32_t version;
+ ngx_str_t token;
+ enum ssl_encryption_level_t level;
+
+ /* filled in by parser */
+ ngx_buf_t *raw; /* udp datagram */
+
+ u_char *data; /* quic packet */
+ size_t len;
+
+ /* cleartext fields */
+ ngx_str_t dcid;
+ ngx_str_t scid;
+ uint64_t pn;
+ ngx_str_t payload; /* decrypted */
+} ngx_quic_header_t;
+
+
+u_char *ngx_quic_error_text(uint64_t error_code);
+
+ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt);
+size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out,
+ size_t pkt_len, u_char **pnp);
+
+ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt,
+ ngx_str_t *dcid);
+ngx_int_t ngx_quic_parse_initial_header(ngx_quic_header_t *pkt);
+ngx_int_t ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt);
+
+ssize_t ngx_quic_parse_frame(u_char *start, u_char *end,
+ ngx_quic_frame_t *frame);
+ssize_t ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f);
+size_t ngx_quic_frame_len(ngx_quic_frame_t *frame);
+
+#endif /* _NGX_EVENT_QUIC_WIRE_H_INCLUDED_ */