# HG changeset patch # User Vladimir Homutov # Date 1584723824 -10800 # Node ID c217a907ce424841aa5563783affe89b25975431 # Parent 8f9cb6d66662aa2f3a8b7f750af989a7eb0d3032 Added checks for permitted frame types. + cleanup in macros for packet types + some style fixes in quic_transport.h (case, indentation) diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -336,7 +336,7 @@ ngx_quic_new_connection(ngx_connection_t return NGX_ERROR; } - if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_INITIAL) { + if (!ngx_quic_pkt_in(pkt->flags)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid initial packet: 0x%xi", pkt->flags); return NGX_ERROR; @@ -563,20 +563,21 @@ ngx_quic_input(ngx_connection_t *c, ngx_ pkt.data = p; pkt.len = b->last - p; pkt.log = c->log; + pkt.flags = p[0]; - if (p[0] == 0) { + if (pkt.flags == 0) { /* XXX: no idea WTF is this, just ignore */ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "FIREFOX: ZEROES"); break; } // TODO: check current state - if (p[0] & NGX_QUIC_PKT_LONG) { + if (ngx_quic_long_pkt(pkt.flags)) { - if ((p[0] & 0xf0) == NGX_QUIC_PKT_INITIAL) { + if (ngx_quic_pkt_in(pkt.flags)) { rc = ngx_quic_initial_input(c, &pkt); - } else if ((p[0] & 0xf0) == NGX_QUIC_PKT_HANDSHAKE) { + } else if (ngx_quic_pkt_hs(pkt.flags)) { rc = ngx_quic_handshake_input(c, &pkt); } else { @@ -665,7 +666,7 @@ ngx_quic_handshake_input(ngx_connection_ return NGX_ERROR; } - if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_HANDSHAKE) { + if (!ngx_quic_pkt_hs(pkt->flags)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid packet type: 0x%xi", pkt->flags); return NGX_ERROR; @@ -734,6 +735,14 @@ ngx_quic_payload_handler(ngx_connection_ while (p < end) { len = ngx_quic_parse_frame(pkt, p, end, &frame); + + if (len == NGX_DECLINED) { + /* TODO: handle protocol violation: + * such frame not allowed in this packet + */ + return NGX_ERROR; + } + if (len < 0) { return NGX_ERROR; } diff --git a/src/event/ngx_event_quic_protection.c b/src/event/ngx_event_quic_protection.c --- a/src/event/ngx_event_quic_protection.c +++ b/src/event/ngx_event_quic_protection.c @@ -904,7 +904,7 @@ ngx_quic_decrypt(ngx_pool_t *pool, ngx_s return NGX_ERROR; } - if (pkt->flags & NGX_QUIC_PKT_LONG) { + if (ngx_quic_long_pkt(pkt->flags)) { clearflags = pkt->flags ^ (mask[0] & 0x0f); } else { @@ -926,7 +926,7 @@ ngx_quic_decrypt(ngx_pool_t *pool, ngx_s in.data = p; - if (pkt->flags & NGX_QUIC_PKT_LONG) { + if (ngx_quic_long_pkt(pkt->flags)) { in.len = pkt->len - pnl; } else { diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c --- a/src/event/ngx_event_quic_transport.c +++ b/src/event/ngx_event_quic_transport.c @@ -261,7 +261,7 @@ ngx_quic_parse_long_header(ngx_quic_head return NGX_ERROR; } - if (!(pkt->flags & NGX_QUIC_PKT_LONG)) { + if (!ngx_quic_long_pkt(pkt->flags)) { ngx_log_error(NGX_LOG_ERR, pkt->log, 0, "not a long packet"); return NGX_ERROR; } @@ -368,7 +368,7 @@ ngx_quic_parse_short_header(ngx_quic_hea return NGX_ERROR; } - if (pkt->flags & NGX_QUIC_PKT_LONG) { + if (!ngx_quic_short_pkt(pkt->flags)) { ngx_log_error(NGX_LOG_ERR, pkt->log, 0, "not a short packet"); return NGX_ERROR; } @@ -489,12 +489,12 @@ ngx_quic_parse_frame(ngx_quic_header_t * ngx_quic_frame_t *f) { u_char *p; + uint8_t flags; uint64_t varint; + flags = pkt->flags; p = start; - /* TODO: add a check if frame is allowed in this type of packet */ - p = ngx_quic_parse_int(p, end, &varint); if (p == NULL) { ngx_log_error(NGX_LOG_ERR, pkt->log, 0, @@ -508,6 +508,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_CRYPTO: + if (ngx_quic_pkt_zrtt(flags)) { + goto not_allowed; + } + p = ngx_quic_parse_int(p, end, &f->u.crypto.offset); if (p == NULL) { ngx_log_error(NGX_LOG_ERR, pkt->log, 0, @@ -539,6 +543,9 @@ ngx_quic_parse_frame(ngx_quic_header_t * break; case NGX_QUIC_FT_PADDING: + + /* allowed in any packet type */ + while (p < end && *p == NGX_QUIC_FT_PADDING) { p++; } @@ -548,6 +555,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_ACK: case NGX_QUIC_FT_ACK_ECN: + if (ngx_quic_pkt_zrtt(flags)) { + goto not_allowed; + } + p = ngx_quic_parse_int_multi(p, end, &f->u.ack.largest, &f->u.ack.delay, &f->u.ack.range_count, &f->u.ack.first_range, NULL); @@ -583,10 +594,17 @@ ngx_quic_parse_frame(ngx_quic_header_t * break; case NGX_QUIC_FT_PING: + + /* allowed in any packet type */ + break; case NGX_QUIC_FT_NEW_CONNECTION_ID: + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + p = ngx_quic_parse_int_multi(p, end, &f->u.ncid.seqnum, &f->u.ncid.retire, NULL); if (p == NULL) { @@ -621,8 +639,19 @@ ngx_quic_parse_frame(ngx_quic_header_t * f->u.ncid.seqnum, f->u.ncid.retire, f->u.ncid.len); break; + case NGX_QUIC_FT_CONNECTION_CLOSE2: + + if (!ngx_quic_short_pkt(flags)) { + goto not_allowed; + } + + /* fall through */ + case NGX_QUIC_FT_CONNECTION_CLOSE: - case NGX_QUIC_FT_CONNECTION_CLOSE2: + + if (ngx_quic_pkt_zrtt(flags)) { + goto not_allowed; + } p = ngx_quic_parse_int(p, end, &f->u.close.error_code); if (p == NULL) { @@ -685,6 +714,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_STREAM6: case NGX_QUIC_FT_STREAM7: + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + f->u.stream.type = f->type; f->u.stream.off = ngx_quic_stream_bit_off(f->type); @@ -743,6 +776,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_MAX_DATA: + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + p = ngx_quic_parse_int(p, end, &f->u.max_data.max_data); if (p == NULL) { ngx_log_error(NGX_LOG_ERR, pkt->log, 0, @@ -757,6 +794,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_RESET_STREAM: + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + p = ngx_quic_parse_int_multi(p, end, &f->u.reset_stream.id, &f->u.reset_stream.error_code, &f->u.reset_stream.final_size, NULL); @@ -775,6 +816,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_STOP_SENDING: + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + p = ngx_quic_parse_int_multi(p, end, &f->u.stop_sending.id, &f->u.stop_sending.error_code, NULL); if (p == NULL) { @@ -792,6 +837,10 @@ ngx_quic_parse_frame(ngx_quic_header_t * case NGX_QUIC_FT_STREAMS_BLOCKED: case NGX_QUIC_FT_STREAMS_BLOCKED2: + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + p = ngx_quic_parse_int(p, end, &f->u.streams_blocked.limit); if (p == NULL) { ngx_log_error(NGX_LOG_ERR, pkt->log, 0, @@ -809,14 +858,52 @@ ngx_quic_parse_frame(ngx_quic_header_t * break; + /* TODO: implement parsing for all frames below */ + case NGX_QUIC_FT_NEW_TOKEN: + case NGX_QUIC_FT_HANDSHAKE_DONE: + + if (!ngx_quic_short_pkt(flags)) { + goto not_allowed; + } + + ngx_log_error(NGX_LOG_ERR, pkt->log, 0, + "unimplemented frame type 0x%xi in packet", f->type); + + break; + + case NGX_QUIC_FT_MAX_STREAMS: + case NGX_QUIC_FT_MAX_STREAMS2: + case NGX_QUIC_FT_MAX_STREAM_DATA: + case NGX_QUIC_FT_DATA_BLOCKED: + case NGX_QUIC_FT_STREAM_DATA_BLOCKED: + case NGX_QUIC_FT_RETIRE_CONNECTION_ID: + case NGX_QUIC_FT_PATH_CHALLENGE: + case NGX_QUIC_FT_PATH_RESPONSE: + + if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) { + goto not_allowed; + } + + ngx_log_error(NGX_LOG_ERR, pkt->log, 0, + "unimplemented frame type 0x%xi in packet", f->type); + break; + default: ngx_log_error(NGX_LOG_ERR, pkt->log, 0, - "unsupported frame type 0x%xd in packet", f->type); + "unknown frame type 0x%xi in packet", f->type); return NGX_ERROR; } return p - start; + +not_allowed: + + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, + "frame type 0x%xi is not allowed in packet with flags 0x%xi", + f->type, pkt->flags); + + return NGX_DECLINED; } diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h --- a/src/event/ngx_event_quic_transport.h +++ b/src/event/ngx_event_quic_transport.h @@ -11,62 +11,70 @@ #include -/* 17.2. Long Header Packets */ -#define NGX_QUIC_PKT_LONG 0x80 +#define ngx_quic_long_pkt(flags) ((flags) & 0x80) /* 17.2 */ +#define ngx_quic_short_pkt(flags) (((flags) & 0x80) == 0) /* 17.3 */ -#define NGX_QUIC_PKT_INITIAL 0xC0 -#define NGX_QUIC_PKT_HANDSHAKE 0xE0 +/* Long packet types */ +#define NGX_QUIC_PKT_INITIAL 0xC0 /* 17.2.2 */ +#define NGX_QUIC_PKT_ZRTT 0xD0 /* 17.2.3 */ +#define NGX_QUIC_PKT_HANDSHAKE 0xE0 /* 17.2.4 */ +#define NGX_QUIC_PKT_RETRY 0xF0 /* 17.2.5 */ + +#define ngx_quic_pkt_in(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_INITIAL) +#define ngx_quic_pkt_zrtt(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_ZRTT) +#define ngx_quic_pkt_hs(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_HANDSHAKE) +#define ngx_quic_pkt_retry(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_RETRY) /* 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 -#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1E +#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 +#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 +#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 +#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 +#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_BUFFER_EXCEEDED 0x0D /* 0xE is not defined */ -#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10 +#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10 #define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR @@ -81,11 +89,11 @@ #define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI 0x07 #define NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI 0x08 #define NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI 0x09 -#define NGX_QUIC_TP_ACK_DELAY_EXPONENT 0x0a -#define NGX_QUIC_TP_MAX_ACK_DELAY 0x0b -#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION 0x0c -#define NGX_QUIC_TP_PREFERRED_ADDRESS 0x0d -#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 0x0e +#define NGX_QUIC_TP_ACK_DELAY_EXPONENT 0x0A +#define NGX_QUIC_TP_MAX_ACK_DELAY 0x0B +#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION 0x0C +#define NGX_QUIC_TP_PREFERRED_ADDRESS 0x0D +#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 0x0E typedef struct {