mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
deps: update to ngtcp2 0.8.1 and nghttp3 0.7.0
Refs: https://github.com/nodejs/node/pull/44619 Co-authored-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/44622 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
1 parent
bf47abe5f3
commit
7621d56a1b
121 files changed
+19288
-7153
No files matched your search
+396
-416
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -31,7 +31,7 @@
|
||||
*
|
||||
* Version number of the nghttp3 library release.
|
||||
*/
|
||||
#define NGHTTP3_VERSION "0.1.0-DEV"
|
||||
#define NGHTTP3_VERSION "0.7.0"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -41,6 +41,6 @@
|
||||
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
|
||||
* becomes 0x010203.
|
||||
*/
|
||||
#define NGHTTP3_VERSION_NUM 0x000100
|
||||
#define NGHTTP3_VERSION_NUM 0x000700
|
||||
|
||||
#endif /* NGHTTP3_VERSION_H */
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* nghttp3
|
||||
*
|
||||
* Copyright (c) 2022 nghttp3 contributors
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "nghttp3_balloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "nghttp3_mem.h"
|
||||
|
||||
void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
|
||||
const nghttp3_mem *mem) {
|
||||
assert((blklen & 0xfu) == 0);
|
||||
|
||||
balloc->mem = mem;
|
||||
balloc->blklen = blklen;
|
||||
balloc->head = NULL;
|
||||
nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
|
||||
}
|
||||
|
||||
void nghttp3_balloc_free(nghttp3_balloc *balloc) {
|
||||
if (balloc == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
nghttp3_balloc_clear(balloc);
|
||||
}
|
||||
|
||||
void nghttp3_balloc_clear(nghttp3_balloc *balloc) {
|
||||
nghttp3_memblock_hd *p, *next;
|
||||
|
||||
for (p = balloc->head; p; p = next) {
|
||||
next = p->next;
|
||||
nghttp3_mem_free(balloc->mem, p);
|
||||
}
|
||||
|
||||
balloc->head = NULL;
|
||||
nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
|
||||
}
|
||||
|
||||
int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) {
|
||||
uint8_t *p;
|
||||
nghttp3_memblock_hd *hd;
|
||||
|
||||
assert(n <= balloc->blklen);
|
||||
|
||||
if (nghttp3_buf_left(&balloc->buf) < n) {
|
||||
p = nghttp3_mem_malloc(balloc->mem, sizeof(nghttp3_memblock_hd) + 0x10u +
|
||||
balloc->blklen);
|
||||
if (p == NULL) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
|
||||
hd = (nghttp3_memblock_hd *)(void *)p;
|
||||
hd->next = balloc->head;
|
||||
balloc->head = hd;
|
||||
nghttp3_buf_wrap_init(
|
||||
&balloc->buf,
|
||||
(uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) &
|
||||
~(uintptr_t)0xfu),
|
||||
balloc->blklen);
|
||||
}
|
||||
|
||||
assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
|
||||
|
||||
*pbuf = balloc->buf.last;
|
||||
balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* nghttp3
|
||||
*
|
||||
* Copyright (c) 2022 nghttp3 contributors
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGHTTP3_BALLOC_H
|
||||
#define NGHTTP3_BALLOC_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
#include "nghttp3_buf.h"
|
||||
|
||||
typedef struct nghttp3_memblock_hd nghttp3_memblock_hd;
|
||||
|
||||
/*
|
||||
* nghttp3_memblock_hd is the header of memory block.
|
||||
*/
|
||||
struct nghttp3_memblock_hd {
|
||||
nghttp3_memblock_hd *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp3_balloc is a custom memory allocator. It allocates |blklen|
|
||||
* bytes of memory at once on demand, and returns its slice when the
|
||||
* allocation is requested.
|
||||
*/
|
||||
typedef struct nghttp3_balloc {
|
||||
/* mem is the underlying memory allocator. */
|
||||
const nghttp3_mem *mem;
|
||||
/* blklen is the size of memory block. */
|
||||
size_t blklen;
|
||||
/* head points to the list of memory block allocated so far. */
|
||||
nghttp3_memblock_hd *head;
|
||||
/* buf wraps the current memory block for allocation requests. */
|
||||
nghttp3_buf buf;
|
||||
} nghttp3_balloc;
|
||||
|
||||
/*
|
||||
* nghttp3_balloc_init initializes |balloc| with |blklen| which is the
|
||||
* size of memory block.
|
||||
*/
|
||||
void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp3_balloc_free releases all allocated memory blocks.
|
||||
*/
|
||||
void nghttp3_balloc_free(nghttp3_balloc *balloc);
|
||||
|
||||
/*
|
||||
* nghttp3_balloc_get allocates |n| bytes of memory and assigns its
|
||||
* pointer to |*pbuf|.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n);
|
||||
|
||||
/*
|
||||
* nghttp3_balloc_clear releases all allocated memory blocks and
|
||||
* initializes its state.
|
||||
*/
|
||||
void nghttp3_balloc_clear(nghttp3_balloc *balloc);
|
||||
|
||||
#endif /* NGHTTP3_BALLOC_H */
|
||||
+482
-1463
File diff suppressed because it is too large.
Load diff
+27
-115
@@ -48,80 +48,38 @@
|
||||
blocked streams for QPACK encoder. */
|
||||
#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100
|
||||
|
||||
/* NGHTTP3_PUSH_PROMISE_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_NONE 0x00
|
||||
/* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is
|
||||
completely received. */
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_RECVED 0x01
|
||||
/* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is
|
||||
cancelled by server before receiving PUSH_PROMISE completely.
|
||||
This flag should have no effect if push stream has already
|
||||
opened. */
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL 0x02
|
||||
/* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is
|
||||
canceled by the local endpoint. */
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL 0x04
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED \
|
||||
(NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | \
|
||||
NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)
|
||||
/* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that
|
||||
unsent_max_pushes has been updated for this push ID. */
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED 0x08
|
||||
/* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise
|
||||
object is bound to a stream where PUSH_PROMISE frame is received
|
||||
first. nghttp3_push_promise object might be created before
|
||||
receiving any PUSH_PROMISE when pushed stream is received before
|
||||
it.*/
|
||||
#define NGHTTP3_PUSH_PROMISE_FLAG_BOUND 0x10
|
||||
|
||||
typedef struct nghttp3_push_promise {
|
||||
nghttp3_map_entry me;
|
||||
nghttp3_tnode node;
|
||||
nghttp3_http_state http;
|
||||
/* stream is server initiated unidirectional stream which fulfils
|
||||
the push promise. */
|
||||
nghttp3_stream *stream;
|
||||
/* stream_id is the stream ID where this PUSH_PROMISE is first
|
||||
received. PUSH_PROMISE with same push ID is allowed to be sent
|
||||
in the multiple streams. We ignore those duplicated PUSH_PROMISE
|
||||
entirely because we don't see any value to add extra cost of
|
||||
processing for it. Even ignoring those frame is not yet easy
|
||||
because we have to decode the header blocks. Server push is
|
||||
overly complex and there is no good use case after all. */
|
||||
int64_t stream_id;
|
||||
/* flags is bitwise OR of zero or more of
|
||||
NGHTTP3_PUSH_PROMISE_FLAG_*. */
|
||||
uint16_t flags;
|
||||
} nghttp3_push_promise;
|
||||
|
||||
/* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGHTTP3_CONN_FLAG_NONE 0x0000
|
||||
#define NGHTTP3_CONN_FLAG_NONE 0x0000u
|
||||
/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has
|
||||
been received. */
|
||||
#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001
|
||||
#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u
|
||||
/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has
|
||||
opened. */
|
||||
#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002
|
||||
#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u
|
||||
/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder
|
||||
stream has opened. */
|
||||
#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004
|
||||
#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u
|
||||
/* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder
|
||||
stream has opened. */
|
||||
#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008
|
||||
/* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID has
|
||||
been queued to control stream. */
|
||||
#define NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED 0x0010
|
||||
#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u
|
||||
/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has
|
||||
received. */
|
||||
#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020
|
||||
#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u
|
||||
/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has
|
||||
been submitted for transmission. */
|
||||
#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040
|
||||
#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u
|
||||
|
||||
typedef struct nghttp3_chunk {
|
||||
nghttp3_opl_entry oplent;
|
||||
} nghttp3_chunk;
|
||||
|
||||
nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent);
|
||||
|
||||
struct nghttp3_conn {
|
||||
nghttp3_objalloc out_chunk_objalloc;
|
||||
nghttp3_objalloc stream_objalloc;
|
||||
nghttp3_callbacks callbacks;
|
||||
nghttp3_map streams;
|
||||
nghttp3_map pushes;
|
||||
nghttp3_qpack_decoder qdec;
|
||||
nghttp3_qpack_encoder qenc;
|
||||
nghttp3_pq qpack_blocked_streams;
|
||||
@@ -138,11 +96,9 @@ struct nghttp3_conn {
|
||||
nghttp3_settings settings;
|
||||
struct {
|
||||
/* max_pushes is the number of push IDs that local endpoint can
|
||||
issue. This field is used by server only. */
|
||||
issue. This field is used by server only and used just for
|
||||
validation */
|
||||
uint64_t max_pushes;
|
||||
/* next_push_id is the next push ID server uses. This field is
|
||||
used by server only. */
|
||||
int64_t next_push_id;
|
||||
} uni;
|
||||
} local;
|
||||
|
||||
@@ -154,18 +110,6 @@ struct nghttp3_conn {
|
||||
issue. This field is used on server side only. */
|
||||
uint64_t max_client_streams;
|
||||
} bidi;
|
||||
struct {
|
||||
/* push_idtr tracks which push ID has been used by remote
|
||||
server. This field is used by client only. */
|
||||
nghttp3_gaptr push_idtr;
|
||||
/* unsent_max_pushes is the maximum number of push which the local
|
||||
endpoint can accept. This limit is not yet notified to the
|
||||
remote endpoint. This field is used by client only. */
|
||||
uint64_t unsent_max_pushes;
|
||||
/* max_push is the maximum number of push which the local
|
||||
endpoint can accept. This field is used by client only. */
|
||||
uint64_t max_pushes;
|
||||
} uni;
|
||||
nghttp3_settings settings;
|
||||
} remote;
|
||||
|
||||
@@ -174,7 +118,13 @@ struct nghttp3_conn {
|
||||
int64_t goaway_id;
|
||||
|
||||
int64_t max_stream_id_bidi;
|
||||
int64_t max_push_id;
|
||||
|
||||
/* pri_fieldbuf is a buffer to store incoming Priority Field Value
|
||||
in PRIORITY_UPDATE frame. */
|
||||
uint8_t pri_fieldbuf[8];
|
||||
/* pri_fieldlen is the number of bytes written into
|
||||
pri_fieldbuf. */
|
||||
size_t pri_fieldbuflen;
|
||||
} rx;
|
||||
|
||||
struct {
|
||||
@@ -192,17 +142,9 @@ struct nghttp3_conn {
|
||||
|
||||
nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id);
|
||||
|
||||
nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn,
|
||||
int64_t push_id);
|
||||
|
||||
int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
|
||||
int64_t stream_id);
|
||||
|
||||
int nghttp3_conn_create_push_promise(nghttp3_conn *conn,
|
||||
nghttp3_push_promise **ppp,
|
||||
int64_t push_id,
|
||||
nghttp3_tnode *assoc_tnode);
|
||||
|
||||
nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
|
||||
nghttp3_stream *stream, const uint8_t *src,
|
||||
size_t srclen, int fin);
|
||||
@@ -214,10 +156,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
|
||||
nghttp3_stream *stream,
|
||||
const uint8_t *src, size_t srclen);
|
||||
|
||||
nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc,
|
||||
nghttp3_stream *stream, const uint8_t *src,
|
||||
size_t srclen, int fin);
|
||||
|
||||
nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
|
||||
const uint8_t *src,
|
||||
size_t srclen);
|
||||
@@ -226,24 +164,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
|
||||
const uint8_t *src,
|
||||
size_t srclen);
|
||||
|
||||
int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id,
|
||||
nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn,
|
||||
const nghttp3_frame_cancel_push *fr);
|
||||
|
||||
int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn,
|
||||
const nghttp3_frame_cancel_push *fr);
|
||||
|
||||
int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream,
|
||||
int64_t push_id);
|
||||
|
||||
int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
|
||||
const uint8_t *data, size_t datalen);
|
||||
|
||||
int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
|
||||
const nghttp3_frame_priority_update *fr);
|
||||
|
||||
nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
|
||||
nghttp3_stream *stream,
|
||||
nghttp3_push_promise *pp,
|
||||
const uint8_t *data, size_t datalen,
|
||||
int fin);
|
||||
|
||||
@@ -255,12 +183,6 @@ int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
|
||||
|
||||
void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn);
|
||||
|
||||
int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id);
|
||||
|
||||
int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id);
|
||||
|
||||
int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn);
|
||||
|
||||
int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
|
||||
@@ -270,20 +192,10 @@ void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, nghttp3_stream *stream);
|
||||
|
||||
/*
|
||||
* nghttp3_conn_get_next_tx_stream returns next stream to send. It
|
||||
* returns NULL if there is no such stream.
|
||||
*/
|
||||
nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn);
|
||||
|
||||
int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id,
|
||||
uint64_t seq, nghttp3_tnode *assoc_tnode,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem);
|
||||
|
||||
#endif /* NGHTTP3_CONN_H */
|
||||
+1
@@ -58,6 +58,7 @@ int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) {
|
||||
}
|
||||
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; }
|
||||
|
||||
+2
-2
@@ -88,7 +88,7 @@
|
||||
STIN uint32_t htonl(uint32_t hostlong) {
|
||||
uint32_t res;
|
||||
unsigned char *p = (unsigned char *)&res;
|
||||
*p++ = hostlong >> 24;
|
||||
*p++ = (unsigned char)(hostlong >> 24);
|
||||
*p++ = (hostlong >> 16) & 0xffu;
|
||||
*p++ = (hostlong >> 8) & 0xffu;
|
||||
*p = hostlong & 0xffu;
|
||||
@@ -98,7 +98,7 @@ STIN uint32_t htonl(uint32_t hostlong) {
|
||||
STIN uint16_t htons(uint16_t hostshort) {
|
||||
uint16_t res;
|
||||
unsigned char *p = (unsigned char *)&res;
|
||||
*p++ = hostshort >> 8;
|
||||
*p++ = (unsigned char)(hostshort >> 8);
|
||||
*p = hostshort & 0xffu;
|
||||
return res;
|
||||
}
|
||||
|
||||
+6
-6
@@ -36,8 +36,6 @@ const char *nghttp3_strerror(int liberr) {
|
||||
return "ERR_WOULDBLOCK";
|
||||
case NGHTTP3_ERR_STREAM_IN_USE:
|
||||
return "ERR_STREAM_IN_USE";
|
||||
case NGHTTP3_ERR_PUSH_ID_BLOCKED:
|
||||
return "ERR_PUSH_ID_BLOCKED";
|
||||
case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
|
||||
return "ERR_MALFORMED_HTTP_HEADER";
|
||||
case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
|
||||
@@ -48,14 +46,12 @@ const char *nghttp3_strerror(int liberr) {
|
||||
return "ERR_QPACK_FATAL";
|
||||
case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE:
|
||||
return "ERR_QPACK_HEADER_TOO_LARGE";
|
||||
case NGHTTP3_ERR_IGNORE_STREAM:
|
||||
return "ERR_IGNORE_STREAM";
|
||||
case NGHTTP3_ERR_STREAM_NOT_FOUND:
|
||||
return "ERR_STREAM_NOT_FOUND";
|
||||
case NGHTTP3_ERR_IGNORE_PUSH_PROMISE:
|
||||
return "ERR_IGNORE_PUSH_PROMISE";
|
||||
case NGHTTP3_ERR_CONN_CLOSING:
|
||||
return "ERR_CONN_CLOSING";
|
||||
case NGHTTP3_ERR_STREAM_DATA_OVERFLOW:
|
||||
return "ERR_STREAM_DATA_OVERFLOW";
|
||||
case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
|
||||
return "ERR_QPACK_DECOMPRESSION_FAILED";
|
||||
case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
|
||||
@@ -106,6 +102,8 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
|
||||
case NGHTTP3_ERR_H3_MISSING_SETTINGS:
|
||||
return NGHTTP3_H3_MISSING_SETTINGS;
|
||||
case NGHTTP3_ERR_H3_INTERNAL_ERROR:
|
||||
case NGHTTP3_ERR_NOMEM:
|
||||
case NGHTTP3_ERR_CALLBACK_FAILURE:
|
||||
return NGHTTP3_H3_INTERNAL_ERROR;
|
||||
case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
|
||||
return NGHTTP3_H3_CLOSED_CRITICAL_STREAM;
|
||||
@@ -124,3 +122,5 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
|
||||
return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; }
|
||||
+33
-47
@@ -26,6 +26,7 @@
|
||||
#include "nghttp3_frame.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nghttp3_conv.h"
|
||||
#include "nghttp3_str.h"
|
||||
@@ -70,44 +71,6 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen,
|
||||
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
|
||||
}
|
||||
|
||||
uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p,
|
||||
const nghttp3_frame_cancel_push *fr) {
|
||||
p = nghttp3_frame_write_hd(p, &fr->hd);
|
||||
p = nghttp3_put_varint(p, fr->push_id);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t
|
||||
nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen,
|
||||
const nghttp3_frame_cancel_push *fr) {
|
||||
size_t payloadlen = nghttp3_put_varint_len(fr->push_id);
|
||||
|
||||
*ppayloadlen = (int64_t)payloadlen;
|
||||
|
||||
return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) +
|
||||
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
|
||||
}
|
||||
|
||||
uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p,
|
||||
const nghttp3_frame_max_push_id *fr) {
|
||||
p = nghttp3_frame_write_hd(p, &fr->hd);
|
||||
p = nghttp3_put_varint(p, fr->push_id);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t
|
||||
nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen,
|
||||
const nghttp3_frame_max_push_id *fr) {
|
||||
size_t payloadlen = nghttp3_put_varint_len(fr->push_id);
|
||||
|
||||
*ppayloadlen = (int64_t)payloadlen;
|
||||
|
||||
return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) +
|
||||
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
|
||||
}
|
||||
|
||||
uint8_t *nghttp3_frame_write_goaway(uint8_t *p,
|
||||
const nghttp3_frame_goaway *fr) {
|
||||
p = nghttp3_frame_write_hd(p, &fr->hd);
|
||||
@@ -126,6 +89,38 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
|
||||
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
nghttp3_frame_write_priority_update(uint8_t *p,
|
||||
const nghttp3_frame_priority_update *fr) {
|
||||
p = nghttp3_frame_write_hd(p, &fr->hd);
|
||||
p = nghttp3_put_varint(p, fr->pri_elem_id);
|
||||
|
||||
assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW);
|
||||
|
||||
*p++ = 'u';
|
||||
*p++ = '=';
|
||||
*p++ = (uint8_t)('0' + fr->pri.urgency);
|
||||
|
||||
if (fr->pri.inc) {
|
||||
#define NGHTTP3_PRIORITY_INCREMENTAL ", i"
|
||||
p = nghttp3_cpymem(p, (const uint8_t *)NGHTTP3_PRIORITY_INCREMENTAL,
|
||||
sizeof(NGHTTP3_PRIORITY_INCREMENTAL) - 1);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t nghttp3_frame_write_priority_update_len(
|
||||
int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) {
|
||||
size_t payloadlen = nghttp3_put_varint_len(fr->pri_elem_id) + sizeof("u=U") -
|
||||
1 + (fr->pri.inc ? sizeof(", i") - 1 : 0);
|
||||
|
||||
*ppayloadlen = (int64_t)payloadlen;
|
||||
|
||||
return nghttp3_put_varint_len(fr->hd.type) +
|
||||
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
|
||||
}
|
||||
|
||||
int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
|
||||
const nghttp3_mem *mem) {
|
||||
size_t i;
|
||||
@@ -207,12 +202,3 @@ void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
|
||||
|
||||
nghttp3_nva_del(fr->nva, mem);
|
||||
}
|
||||
|
||||
void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr,
|
||||
const nghttp3_mem *mem) {
|
||||
if (fr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
nghttp3_nva_del(fr->nva, mem);
|
||||
}
|
||||
+33
-61
@@ -42,6 +42,10 @@ typedef enum nghttp3_frame_type {
|
||||
NGHTTP3_FRAME_PUSH_PROMISE = 0x05,
|
||||
NGHTTP3_FRAME_GOAWAY = 0x07,
|
||||
NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d,
|
||||
/* PRIORITY_UPDATE:
|
||||
https://tools.ietf.org/html/draft-ietf-httpbis-priority-03 */
|
||||
NGHTTP3_FRAME_PRIORITY_UPDATE = 0x0f0700,
|
||||
NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID = 0x0f0701,
|
||||
} nghttp3_frame_type;
|
||||
|
||||
typedef enum nghttp3_h2_reserved_type {
|
||||
@@ -66,14 +70,10 @@ typedef struct nghttp3_frame_headers {
|
||||
size_t nvlen;
|
||||
} nghttp3_frame_headers;
|
||||
|
||||
typedef struct nghttp3_frame_cancel_push {
|
||||
nghttp3_frame_hd hd;
|
||||
int64_t push_id;
|
||||
} nghttp3_frame_cancel_push;
|
||||
|
||||
#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06
|
||||
#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01
|
||||
#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07
|
||||
#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08
|
||||
|
||||
#define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2
|
||||
#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3
|
||||
@@ -91,32 +91,28 @@ typedef struct nghttp3_frame_settings {
|
||||
nghttp3_settings_entry iv[1];
|
||||
} nghttp3_frame_settings;
|
||||
|
||||
typedef struct nghttp3_frame_push_promise {
|
||||
nghttp3_frame_hd hd;
|
||||
nghttp3_nv *nva;
|
||||
size_t nvlen;
|
||||
int64_t push_id;
|
||||
} nghttp3_frame_push_promise;
|
||||
|
||||
typedef struct nghttp3_frame_goaway {
|
||||
nghttp3_frame_hd hd;
|
||||
int64_t id;
|
||||
} nghttp3_frame_goaway;
|
||||
|
||||
typedef struct nghttp3_frame_max_push_id {
|
||||
typedef struct nghttp3_frame_priority_update {
|
||||
nghttp3_frame_hd hd;
|
||||
int64_t push_id;
|
||||
} nghttp3_frame_max_push_id;
|
||||
/* pri_elem_id is stream ID if hd.type ==
|
||||
NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type ==
|
||||
NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined
|
||||
otherwise. */
|
||||
int64_t pri_elem_id;
|
||||
nghttp3_pri pri;
|
||||
} nghttp3_frame_priority_update;
|
||||
|
||||
typedef union nghttp3_frame {
|
||||
nghttp3_frame_hd hd;
|
||||
nghttp3_frame_data data;
|
||||
nghttp3_frame_headers headers;
|
||||
nghttp3_frame_cancel_push cancel_push;
|
||||
nghttp3_frame_settings settings;
|
||||
nghttp3_frame_push_promise push_promise;
|
||||
nghttp3_frame_goaway goaway;
|
||||
nghttp3_frame_max_push_id max_push_id;
|
||||
nghttp3_frame_priority_update priority_update;
|
||||
} nghttp3_frame;
|
||||
|
||||
/*
|
||||
@@ -150,42 +146,6 @@ uint8_t *nghttp3_frame_write_settings(uint8_t *dest,
|
||||
size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen,
|
||||
const nghttp3_frame_settings *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to
|
||||
* |dest|. This function assumes that |dest| has enough space to
|
||||
* write |fr|.
|
||||
*
|
||||
* This function returns |dest| plus the number of bytes written.
|
||||
*/
|
||||
uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest,
|
||||
const nghttp3_frame_cancel_push *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_cancel_push_len returns the number of bytes
|
||||
* required to write |fr|. fr->hd.length is ignored. This function
|
||||
* stores payload length in |*ppayloadlen|.
|
||||
*/
|
||||
size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen,
|
||||
const nghttp3_frame_cancel_push *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to
|
||||
* |dest|. This function assumes that |dest| has enough space to
|
||||
* write |fr|.
|
||||
*
|
||||
* This function returns |dest| plus the number of bytes written.
|
||||
*/
|
||||
uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest,
|
||||
const nghttp3_frame_max_push_id *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_max_push_id_len returns the number of bytes
|
||||
* required to write |fr|. fr->hd.length is ignored. This function
|
||||
* stores payload length in |*ppayloadlen|.
|
||||
*/
|
||||
size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen,
|
||||
const nghttp3_frame_max_push_id *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|.
|
||||
* This function assumes that |dest| has enough space to write |fr|.
|
||||
@@ -203,6 +163,25 @@ uint8_t *nghttp3_frame_write_goaway(uint8_t *dest,
|
||||
size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
|
||||
const nghttp3_frame_goaway *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame
|
||||
* |fr| to |dest|. This function assumes that |dest| has enough space
|
||||
* to write |fr|.
|
||||
*
|
||||
* This function returns |dest| plus the number of bytes written;
|
||||
*/
|
||||
uint8_t *
|
||||
nghttp3_frame_write_priority_update(uint8_t *dest,
|
||||
const nghttp3_frame_priority_update *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_write_priority_update_len returns the number of bytes
|
||||
* required to write |fr|. fr->hd.length is ignored. This function
|
||||
* stores payload length in |*ppayloadlen|.
|
||||
*/
|
||||
size_t nghttp3_frame_write_priority_update_len(
|
||||
int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr);
|
||||
|
||||
/*
|
||||
* nghttp3_nva_copy copies name/value pairs from |nva|, which contains
|
||||
* |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so
|
||||
@@ -233,11 +212,4 @@ void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem);
|
||||
void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp3_frame_push_promise_free frees memory allocated for |fr|.
|
||||
* It assumes that fr->nva is created by nghttp3_nva_copy() or NULL.
|
||||
*/
|
||||
void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
#endif /* NGHTTP3_FRAME_H */
|
||||
+66
-27
@@ -24,29 +24,26 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp3_gaptr.h"
|
||||
#include "nghttp3_range.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
nghttp3_range range = {0, UINT64_MAX};
|
||||
void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
|
||||
nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range),
|
||||
mem);
|
||||
|
||||
rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar,
|
||||
sizeof(nghttp3_range), mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
gaptr->mem = mem;
|
||||
}
|
||||
|
||||
static int gaptr_gap_init(nghttp3_gaptr *gaptr) {
|
||||
nghttp3_range range = {0, UINT64_MAX};
|
||||
int rv;
|
||||
|
||||
rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL);
|
||||
if (rv != 0) {
|
||||
nghttp3_ksl_free(&gaptr->gap);
|
||||
return rv;
|
||||
}
|
||||
|
||||
gaptr->mem = mem;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -58,11 +55,19 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) {
|
||||
nghttp3_ksl_free(&gaptr->gap);
|
||||
}
|
||||
|
||||
int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
|
||||
int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset,
|
||||
uint64_t datalen) {
|
||||
int rv;
|
||||
nghttp3_range k, m, l, r, q = {offset, offset + datalen};
|
||||
nghttp3_ksl_it it;
|
||||
|
||||
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
|
||||
rv = gaptr_gap_init(gaptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
nghttp3_ksl_range_exclusive_compar);
|
||||
|
||||
@@ -74,7 +79,7 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
|
||||
}
|
||||
|
||||
if (nghttp3_range_eq(&k, &m)) {
|
||||
nghttp3_ksl_remove(&gaptr->gap, &it, &k);
|
||||
nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
|
||||
continue;
|
||||
}
|
||||
nghttp3_range_cut(&l, &r, &k, &m);
|
||||
@@ -96,35 +101,69 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
|
||||
}
|
||||
|
||||
uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) {
|
||||
nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap);
|
||||
nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
|
||||
nghttp3_ksl_it it;
|
||||
nghttp3_range r;
|
||||
|
||||
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
it = nghttp3_ksl_begin(&gaptr->gap);
|
||||
r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
|
||||
|
||||
return r.begin;
|
||||
}
|
||||
|
||||
nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
|
||||
uint64_t offset) {
|
||||
nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
|
||||
uint64_t offset) {
|
||||
nghttp3_range q = {offset, offset + 1};
|
||||
return nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
nghttp3_ksl_range_exclusive_compar);
|
||||
nghttp3_ksl_it it;
|
||||
|
||||
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
|
||||
nghttp3_range r = {0, UINT64_MAX};
|
||||
return r;
|
||||
}
|
||||
|
||||
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
nghttp3_ksl_range_exclusive_compar);
|
||||
|
||||
assert(!nghttp3_ksl_it_end(&it));
|
||||
|
||||
return *(nghttp3_range *)nghttp3_ksl_it_key(&it);
|
||||
}
|
||||
|
||||
int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
|
||||
size_t datalen) {
|
||||
uint64_t datalen) {
|
||||
nghttp3_range q = {offset, offset + datalen};
|
||||
nghttp3_ksl_it it = nghttp3_ksl_lower_bound_compar(
|
||||
&gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar);
|
||||
nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
|
||||
nghttp3_range m = nghttp3_range_intersect(&q, &k);
|
||||
nghttp3_ksl_it it;
|
||||
nghttp3_range k;
|
||||
nghttp3_range m;
|
||||
|
||||
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
nghttp3_ksl_range_exclusive_compar);
|
||||
k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
|
||||
m = nghttp3_range_intersect(&q, &k);
|
||||
|
||||
return nghttp3_range_len(&m) == 0;
|
||||
}
|
||||
|
||||
void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) {
|
||||
nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap);
|
||||
nghttp3_ksl_it it;
|
||||
nghttp3_range r;
|
||||
|
||||
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
it = nghttp3_ksl_begin(&gaptr->gap);
|
||||
|
||||
assert(!nghttp3_ksl_it_end(&it));
|
||||
|
||||
r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
|
||||
|
||||
nghttp3_ksl_remove(&gaptr->gap, NULL, &r);
|
||||
nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
|
||||
}
|
||||
+8
-13
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "nghttp3_mem.h"
|
||||
#include "nghttp3_ksl.h"
|
||||
#include "nghttp3_range.h"
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX).
|
||||
@@ -48,14 +49,8 @@ typedef struct nghttp3_gaptr {
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr_init initializes |gaptr|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem);
|
||||
void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr_free frees resources allocated for |gaptr|.
|
||||
@@ -72,7 +67,7 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr);
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen);
|
||||
int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen);
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr_first_gap_offset returns the offset to the first gap.
|
||||
@@ -81,18 +76,18 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen);
|
||||
uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr);
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr_get_first_gap_after returns the iterator pointing to
|
||||
* the first gap which overlaps or comes after |offset|.
|
||||
* nghttp3_gaptr_get_first_gap_after returns the first gap which
|
||||
* overlaps or comes after |offset|.
|
||||
*/
|
||||
nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
|
||||
uint64_t offset);
|
||||
nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
|
||||
uint64_t offset);
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset +
|
||||
* datalen) is completely pushed into this object.
|
||||
*/
|
||||
int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
|
||||
size_t datalen);
|
||||
uint64_t datalen);
|
||||
|
||||
/*
|
||||
* nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if
|
||||
|
||||
+916
-106
File diff suppressed because it is too large.
Load diff
+78
-22
@@ -39,48 +39,55 @@ typedef struct nghttp3_http_state nghttp3_http_state;
|
||||
/* HTTP related flags to enforce HTTP semantics */
|
||||
|
||||
/* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGHTTP3_HTTP_FLAG_NONE 0x00
|
||||
#define NGHTTP3_HTTP_FLAG_NONE 0x00u
|
||||
/* header field seen so far */
|
||||
#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01
|
||||
#define NGHTTP3_HTTP_FLAG__PATH 0x02
|
||||
#define NGHTTP3_HTTP_FLAG__METHOD 0x04
|
||||
#define NGHTTP3_HTTP_FLAG__SCHEME 0x08
|
||||
#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u
|
||||
#define NGHTTP3_HTTP_FLAG__PATH 0x02u
|
||||
#define NGHTTP3_HTTP_FLAG__METHOD 0x04u
|
||||
#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u
|
||||
/* host is not pseudo header, but we require either host or
|
||||
:authority */
|
||||
#define NGHTTP3_HTTP_FLAG_HOST 0x10
|
||||
#define NGHTTP3_HTTP_FLAG__STATUS 0x20
|
||||
#define NGHTTP3_HTTP_FLAG_HOST 0x10u
|
||||
#define NGHTTP3_HTTP_FLAG__STATUS 0x20u
|
||||
/* required header fields for HTTP request except for CONNECT
|
||||
method. */
|
||||
#define NGHTTP3_HTTP_FLAG_REQ_HEADERS \
|
||||
(NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \
|
||||
NGHTTP3_HTTP_FLAG__SCHEME)
|
||||
#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40
|
||||
#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u
|
||||
/* HTTP method flags */
|
||||
#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80
|
||||
#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100
|
||||
#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200
|
||||
#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u
|
||||
#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u
|
||||
#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u
|
||||
#define NGHTTP3_HTTP_FLAG_METH_ALL \
|
||||
(NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \
|
||||
NGHTTP3_HTTP_FLAG_METH_OPTIONS)
|
||||
/* :path category */
|
||||
/* path starts with "/" */
|
||||
#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400
|
||||
#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u
|
||||
/* path "*" */
|
||||
#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800
|
||||
#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u
|
||||
/* scheme */
|
||||
/* "http" or "https" scheme */
|
||||
#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000
|
||||
#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u
|
||||
/* set if final response is expected */
|
||||
#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000
|
||||
#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u
|
||||
/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header
|
||||
field is seen. */
|
||||
#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000
|
||||
#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u
|
||||
/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is
|
||||
processed. */
|
||||
#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u
|
||||
/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered
|
||||
while parsing priority header field. */
|
||||
#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u
|
||||
|
||||
/*
|
||||
* This function is called when HTTP header field |nv| in a frame of type
|
||||
* |frame_type| is received for |http|. This function will validate |nv|
|
||||
* against the current state of stream. Pass nonzero if this is request
|
||||
* headers. Pass nonzero to |trailers| if |nv| is included in trailers.
|
||||
* This function is called when HTTP header field |nv| received for
|
||||
* |http|. This function will validate |nv| against the current state
|
||||
* of stream. Pass nonzero if this is request headers. Pass nonzero
|
||||
* to |trailers| if |nv| is included in trailers. |connect_protocol|
|
||||
* is nonzero if Extended CONNECT Method is enabled.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -91,8 +98,8 @@ typedef struct nghttp3_http_state nghttp3_http_state;
|
||||
* Invalid HTTP header field was received but it can be treated as
|
||||
* if it was not received because of compatibility reasons.
|
||||
*/
|
||||
int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
|
||||
nghttp3_qpack_nv *nv, int request, int trailers);
|
||||
int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
|
||||
int request, int trailers, int connect_protocol);
|
||||
|
||||
/*
|
||||
* This function is called when request header is received. This
|
||||
@@ -143,4 +150,53 @@ int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n);
|
||||
void nghttp3_http_record_request_method(nghttp3_stream *stream,
|
||||
const nghttp3_nv *nva, size_t nvlen);
|
||||
|
||||
/*
|
||||
* RFC 8941 Structured Field Values.
|
||||
*/
|
||||
typedef enum nghttp3_sf_value_type {
|
||||
NGHTTP3_SF_VALUE_TYPE_BOOLEAN,
|
||||
NGHTTP3_SF_VALUE_TYPE_INTEGER,
|
||||
NGHTTP3_SF_VALUE_TYPE_DECIMAL,
|
||||
NGHTTP3_SF_VALUE_TYPE_STRING,
|
||||
NGHTTP3_SF_VALUE_TYPE_TOKEN,
|
||||
NGHTTP3_SF_VALUE_TYPE_BYTESEQ,
|
||||
NGHTTP3_SF_VALUE_TYPE_INNER_LIST,
|
||||
} nghttp3_sf_value_type;
|
||||
|
||||
/*
|
||||
* nghttp3_sf_value stores Structured Field Values item. For Inner
|
||||
* List, only type is set to NGHTTP3_SF_VALUE_TYPE_INNER_LIST.
|
||||
*/
|
||||
typedef struct nghttp3_sf_value {
|
||||
uint8_t type;
|
||||
union {
|
||||
int b;
|
||||
int64_t i;
|
||||
double d;
|
||||
struct {
|
||||
const uint8_t *base;
|
||||
size_t len;
|
||||
} s;
|
||||
};
|
||||
} nghttp3_sf_value;
|
||||
|
||||
/*
|
||||
* nghttp3_sf_parse_item parses the input sequence [|begin|, |end|)
|
||||
* and stores the parsed an Item in |dest|. It returns the number of
|
||||
* bytes consumed if it succeeds, or -1. This function is declared
|
||||
* here for unit tests.
|
||||
*/
|
||||
nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
|
||||
const uint8_t *begin, const uint8_t *end);
|
||||
|
||||
/*
|
||||
* nghttp3_sf_parse_inner_list parses the input sequence [|begin|, |end|)
|
||||
* and stores the parsed an Inner List in |dest|. It returns the number of
|
||||
* bytes consumed if it succeeds, or -1. This function is declared
|
||||
* here for unit tests.
|
||||
*/
|
||||
nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
|
||||
const uint8_t *begin,
|
||||
const uint8_t *end);
|
||||
|
||||
#endif /* NGHTTP3_HTTP_H */
|
||||
+2
-10
@@ -27,18 +27,10 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
|
||||
rv = nghttp3_gaptr_init(&idtr->gap, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) {
|
||||
nghttp3_gaptr_init(&idtr->gap, mem);
|
||||
|
||||
idtr->server = server;
|
||||
idtr->mem = mem;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp3_idtr_free(nghttp3_idtr *idtr) {
|
||||
|
||||
+2
-11
@@ -45,24 +45,15 @@ typedef struct nghttp3_idtr {
|
||||
/* server is nonzero if this object records server initiated stream
|
||||
ID. */
|
||||
int server;
|
||||
/* mem is custom memory allocator */
|
||||
const nghttp3_mem *mem;
|
||||
} nghttp3_idtr;
|
||||
|
||||
/*
|
||||
* nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer
|
||||
* per chunk.
|
||||
* nghttp3_idtr_init initializes |idtr|.
|
||||
*
|
||||
* If this object records server initiated ID (even number), set
|
||||
* |server| to nonzero.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem);
|
||||
void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp3_idtr_free frees resources allocated for |idtr|.
|
||||
|
||||
+131
-53
@@ -34,9 +34,11 @@
|
||||
#include "nghttp3_mem.h"
|
||||
#include "nghttp3_range.h"
|
||||
|
||||
static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
|
||||
|
||||
static size_t ksl_nodelen(size_t keylen) {
|
||||
return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
|
||||
(size_t)~0xf;
|
||||
return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
|
||||
~(uintptr_t)0xfu;
|
||||
}
|
||||
|
||||
static size_t ksl_blklen(size_t nodelen) {
|
||||
@@ -52,31 +54,48 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node,
|
||||
memcpy(node->key, key, ksl->keylen);
|
||||
}
|
||||
|
||||
int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen,
|
||||
const nghttp3_mem *mem) {
|
||||
void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
|
||||
size_t keylen, const nghttp3_mem *mem) {
|
||||
size_t nodelen = ksl_nodelen(keylen);
|
||||
size_t blklen = ksl_blklen(nodelen);
|
||||
nghttp3_ksl_blk *head;
|
||||
|
||||
ksl->head = nghttp3_mem_malloc(mem, blklen);
|
||||
if (!ksl->head) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
ksl->front = ksl->back = ksl->head;
|
||||
nghttp3_objalloc_init(&ksl->blkalloc,
|
||||
((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
|
||||
mem);
|
||||
|
||||
ksl->head = NULL;
|
||||
ksl->front = ksl->back = NULL;
|
||||
ksl->compar = compar;
|
||||
ksl->keylen = keylen;
|
||||
ksl->nodelen = nodelen;
|
||||
ksl->n = 0;
|
||||
ksl->mem = mem;
|
||||
}
|
||||
|
||||
static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) {
|
||||
return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc,
|
||||
ksl_blklen(ksl->nodelen));
|
||||
}
|
||||
|
||||
static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
|
||||
nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
|
||||
}
|
||||
|
||||
static int ksl_head_init(nghttp3_ksl *ksl) {
|
||||
nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl);
|
||||
if (!head) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
|
||||
head = ksl->head;
|
||||
head->next = head->prev = NULL;
|
||||
head->n = 0;
|
||||
head->leaf = 1;
|
||||
|
||||
ksl->head = head;
|
||||
ksl->front = ksl->back = head;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef NOMEMPOOL
|
||||
/*
|
||||
* ksl_free_blk frees |blk| recursively.
|
||||
*/
|
||||
@@ -89,15 +108,20 @@ static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
|
||||
}
|
||||
}
|
||||
|
||||
nghttp3_mem_free(ksl->mem, blk);
|
||||
ksl_blk_objalloc_del(ksl, blk);
|
||||
}
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
void nghttp3_ksl_free(nghttp3_ksl *ksl) {
|
||||
if (!ksl) {
|
||||
if (!ksl || !ksl->head) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NOMEMPOOL
|
||||
ksl_free_blk(ksl, ksl->head);
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
nghttp3_objalloc_free(&ksl->blkalloc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -111,7 +135,7 @@ void nghttp3_ksl_free(nghttp3_ksl *ksl) {
|
||||
static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
|
||||
nghttp3_ksl_blk *rblk;
|
||||
|
||||
rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
|
||||
rblk = ksl_blk_objalloc_new(ksl);
|
||||
if (rblk == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -197,9 +221,9 @@ static int ksl_split_head(nghttp3_ksl *ksl) {
|
||||
|
||||
lblk = ksl->head;
|
||||
|
||||
nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
|
||||
nhead = ksl_blk_objalloc_new(ksl);
|
||||
if (nhead == NULL) {
|
||||
nghttp3_mem_free(ksl->mem, rblk);
|
||||
ksl_blk_objalloc_del(ksl, rblk);
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
nhead->next = nhead->prev = NULL;
|
||||
@@ -246,29 +270,33 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i,
|
||||
static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
|
||||
const nghttp3_ksl_key *key,
|
||||
nghttp3_ksl_compar compar) {
|
||||
nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid;
|
||||
size_t i;
|
||||
nghttp3_ksl_node *node;
|
||||
|
||||
while (right - left > 1) {
|
||||
mid = (left + right) >> 1;
|
||||
node = nghttp3_ksl_nth_node(ksl, blk, (size_t)mid);
|
||||
if (compar((nghttp3_ksl_key *)node->key, key)) {
|
||||
left = mid;
|
||||
} else {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes;
|
||||
i < blk->n && compar((nghttp3_ksl_key *)node->key, key);
|
||||
++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
|
||||
;
|
||||
|
||||
return (size_t)right;
|
||||
return i;
|
||||
}
|
||||
|
||||
int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
|
||||
const nghttp3_ksl_key *key, void *data) {
|
||||
nghttp3_ksl_blk *blk = ksl->head;
|
||||
nghttp3_ksl_blk *blk;
|
||||
nghttp3_ksl_node *node;
|
||||
size_t i;
|
||||
int rv;
|
||||
|
||||
if (!ksl->head) {
|
||||
rv = ksl_head_init(ksl);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
blk = ksl->head;
|
||||
|
||||
if (blk->n == NGHTTP3_KSL_MAX_NBLK) {
|
||||
rv = ksl_split_head(ksl);
|
||||
if (rv != 0) {
|
||||
@@ -380,10 +408,10 @@ static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
|
||||
ksl->back = lblk;
|
||||
}
|
||||
|
||||
nghttp3_mem_free(ksl->mem, rblk);
|
||||
ksl_blk_objalloc_del(ksl, rblk);
|
||||
|
||||
if (ksl->head == blk && blk->n == 2) {
|
||||
nghttp3_mem_free(ksl->mem, ksl->head);
|
||||
ksl_blk_objalloc_del(ksl, ksl->head);
|
||||
ksl->head = lblk;
|
||||
} else {
|
||||
ksl_remove_node(ksl, blk, i + 1);
|
||||
@@ -477,12 +505,42 @@ static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs,
|
||||
return !compar(lhs, rhs) && !compar(rhs, lhs);
|
||||
}
|
||||
|
||||
int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
|
||||
const nghttp3_ksl_it *hint,
|
||||
const nghttp3_ksl_key *key) {
|
||||
nghttp3_ksl_blk *blk = hint->blk;
|
||||
|
||||
assert(ksl->head);
|
||||
|
||||
if (blk->n <= NGHTTP3_KSL_MIN_NBLK) {
|
||||
return nghttp3_ksl_remove(ksl, it, key);
|
||||
}
|
||||
|
||||
ksl_remove_node(ksl, blk, hint->i);
|
||||
|
||||
--ksl->n;
|
||||
|
||||
if (it) {
|
||||
if (hint->i == blk->n && blk->next) {
|
||||
nghttp3_ksl_it_init(it, ksl, blk->next, 0);
|
||||
} else {
|
||||
nghttp3_ksl_it_init(it, ksl, blk, hint->i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
|
||||
const nghttp3_ksl_key *key) {
|
||||
nghttp3_ksl_blk *blk = ksl->head;
|
||||
nghttp3_ksl_node *node;
|
||||
size_t i;
|
||||
|
||||
if (!ksl->head) {
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!blk->leaf && blk->n == 2 &&
|
||||
nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK &&
|
||||
nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) {
|
||||
@@ -558,6 +616,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
|
||||
nghttp3_ksl_it it;
|
||||
size_t i;
|
||||
|
||||
if (!blk) {
|
||||
nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
return it;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, ksl->compar);
|
||||
|
||||
@@ -595,6 +658,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
|
||||
nghttp3_ksl_it it;
|
||||
size_t i;
|
||||
|
||||
if (!blk) {
|
||||
nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
return it;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, compar);
|
||||
|
||||
@@ -631,6 +699,8 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
|
||||
nghttp3_ksl_node *node;
|
||||
size_t i;
|
||||
|
||||
assert(ksl->head);
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
|
||||
|
||||
@@ -675,36 +745,49 @@ static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) {
|
||||
size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; }
|
||||
|
||||
void nghttp3_ksl_clear(nghttp3_ksl *ksl) {
|
||||
size_t i;
|
||||
nghttp3_ksl_blk *head;
|
||||
|
||||
if (!ksl->head->leaf) {
|
||||
for (i = 0; i < ksl->head->n; ++i) {
|
||||
ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, ksl->head, i)->blk);
|
||||
}
|
||||
if (!ksl->head) {
|
||||
return;
|
||||
}
|
||||
|
||||
ksl->front = ksl->back = ksl->head;
|
||||
#ifdef NOMEMPOOL
|
||||
ksl_free_blk(ksl, ksl->head);
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
ksl->front = ksl->back = ksl->head = NULL;
|
||||
ksl->n = 0;
|
||||
|
||||
head = ksl->head;
|
||||
|
||||
head->next = head->prev = NULL;
|
||||
head->n = 0;
|
||||
head->leaf = 1;
|
||||
nghttp3_objalloc_clear(&ksl->blkalloc);
|
||||
}
|
||||
|
||||
void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
|
||||
void nghttp3_ksl_print(nghttp3_ksl *ksl) {
|
||||
if (!ksl->head) {
|
||||
return;
|
||||
}
|
||||
|
||||
ksl_print(ksl, ksl->head, 0);
|
||||
}
|
||||
|
||||
nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) {
|
||||
nghttp3_ksl_it it;
|
||||
nghttp3_ksl_it_init(&it, ksl, ksl->front, 0);
|
||||
|
||||
if (ksl->head) {
|
||||
nghttp3_ksl_it_init(&it, ksl, ksl->front, 0);
|
||||
} else {
|
||||
nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) {
|
||||
nghttp3_ksl_it it;
|
||||
nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
|
||||
|
||||
if (ksl->head) {
|
||||
nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
|
||||
} else {
|
||||
nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
@@ -715,11 +798,6 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
|
||||
it->i = i;
|
||||
}
|
||||
|
||||
void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) {
|
||||
assert(it->i < it->blk->n);
|
||||
return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data;
|
||||
}
|
||||
|
||||
void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) {
|
||||
assert(!nghttp3_ksl_it_begin(it));
|
||||
|
||||
|
||||
+42
-25
@@ -34,6 +34,8 @@
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
#include "nghttp3_objalloc.h"
|
||||
|
||||
/*
|
||||
* Skip List using single key instead of range.
|
||||
*/
|
||||
@@ -80,25 +82,34 @@ struct nghttp3_ksl_node {
|
||||
* nghttp3_ksl_blk contains nghttp3_ksl_node objects.
|
||||
*/
|
||||
struct nghttp3_ksl_blk {
|
||||
/* next points to the next block if leaf field is nonzero. */
|
||||
nghttp3_ksl_blk *next;
|
||||
/* prev points to the previous block if leaf field is nonzero. */
|
||||
nghttp3_ksl_blk *prev;
|
||||
/* n is the number of nodes this object contains in nodes. */
|
||||
uint32_t n;
|
||||
/* leaf is nonzero if this block contains leaf nodes. */
|
||||
uint32_t leaf;
|
||||
union {
|
||||
uint64_t align;
|
||||
/* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK
|
||||
nghttp3_ksl_node objects. Because nghttp3_ksl_node object is
|
||||
allocated along with the additional variable length key
|
||||
storage, the size of buffer is unknown until nghttp3_ksl_init
|
||||
is called. */
|
||||
uint8_t nodes[1];
|
||||
struct {
|
||||
/* next points to the next block if leaf field is nonzero. */
|
||||
nghttp3_ksl_blk *next;
|
||||
/* prev points to the previous block if leaf field is
|
||||
nonzero. */
|
||||
nghttp3_ksl_blk *prev;
|
||||
/* n is the number of nodes this object contains in nodes. */
|
||||
uint32_t n;
|
||||
/* leaf is nonzero if this block contains leaf nodes. */
|
||||
uint32_t leaf;
|
||||
union {
|
||||
uint64_t align;
|
||||
/* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK
|
||||
nghttp3_ksl_node objects. Because nghttp3_ksl_node object
|
||||
is allocated along with the additional variable length key
|
||||
storage, the size of buffer is unknown until
|
||||
nghttp3_ksl_init is called. */
|
||||
uint8_t nodes[1];
|
||||
};
|
||||
};
|
||||
|
||||
nghttp3_opl_entry oplent;
|
||||
};
|
||||
};
|
||||
|
||||
nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent);
|
||||
|
||||
/*
|
||||
* nghttp3_ksl_compar is a function type which returns nonzero if key
|
||||
* |lhs| should be placed before |rhs|. It returns 0 otherwise.
|
||||
@@ -123,6 +134,7 @@ struct nghttp3_ksl_it {
|
||||
* nghttp3_ksl is a deterministic paged skip list.
|
||||
*/
|
||||
struct nghttp3_ksl {
|
||||
nghttp3_objalloc blkalloc;
|
||||
/* head points to the root block. */
|
||||
nghttp3_ksl_blk *head;
|
||||
/* front points to the first leaf block. */
|
||||
@@ -136,21 +148,14 @@ struct nghttp3_ksl {
|
||||
/* nodelen is the actual size of nghttp3_ksl_node including key
|
||||
storage. */
|
||||
size_t nodelen;
|
||||
const nghttp3_mem *mem;
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp3_ksl_init initializes |ksl|. |compar| specifies compare
|
||||
* function. |keylen| is the length of key.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen,
|
||||
const nghttp3_mem *mem);
|
||||
void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
|
||||
size_t keylen, const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is
|
||||
@@ -192,6 +197,17 @@ int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
|
||||
int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
|
||||
const nghttp3_ksl_key *key);
|
||||
|
||||
/*
|
||||
* nghttp3_ksl_remove_hint removes the |key| from |ksl|. |hint| must
|
||||
* point to the same node denoted by |key|. |hint| is used to remove
|
||||
* a node efficiently in some cases. Other than that, it behaves
|
||||
* exactly like nghttp3_ksl_remove. |it| and |hint| can point to the
|
||||
* same object.
|
||||
*/
|
||||
int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
|
||||
const nghttp3_ksl_it *hint,
|
||||
const nghttp3_ksl_key *key);
|
||||
|
||||
/*
|
||||
* nghttp3_ksl_lower_bound returns the iterator which points to the
|
||||
* first node which has the key which is equal to |key| or the last
|
||||
@@ -267,7 +283,8 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
|
||||
* |it| points to. It is undefined to call this function when
|
||||
* nghttp3_ksl_it_end(it) returns nonzero.
|
||||
*/
|
||||
void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it);
|
||||
#define nghttp3_ksl_it_get(IT) \
|
||||
nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
|
||||
|
||||
/*
|
||||
* nghttp3_ksl_it_next advances the iterator by one. It is undefined
|
||||
|
||||
+4
@@ -44,4 +44,8 @@
|
||||
|
||||
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
|
||||
|
||||
/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in
|
||||
variable-length integer encoding. */
|
||||
#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1)
|
||||
|
||||
#endif /* NGHTTP3_MACRO_H */
|
||||
+199
-198
@@ -28,172 +28,166 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nghttp3_conv.h"
|
||||
|
||||
#define INITIAL_TABLE_LENGTH 256
|
||||
#define NGHTTP3_INITIAL_TABLE_LENBITS 4
|
||||
|
||||
int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
|
||||
void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
|
||||
map->mem = mem;
|
||||
map->tablelen = INITIAL_TABLE_LENGTH;
|
||||
map->table =
|
||||
nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_bucket));
|
||||
if (map->table == NULL) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
|
||||
map->tablelen = 0;
|
||||
map->tablelenbits = 0;
|
||||
map->table = NULL;
|
||||
map->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp3_map_free(nghttp3_map *map) {
|
||||
size_t i;
|
||||
nghttp3_map_bucket *bkt;
|
||||
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->ksl) {
|
||||
nghttp3_ksl_free(bkt->ksl);
|
||||
nghttp3_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
}
|
||||
|
||||
nghttp3_mem_free(map->mem, map->table);
|
||||
}
|
||||
|
||||
void nghttp3_map_each_free(nghttp3_map *map,
|
||||
int (*func)(nghttp3_map_entry *entry, void *ptr),
|
||||
void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
uint32_t i;
|
||||
nghttp3_map_bucket *bkt;
|
||||
nghttp3_ksl_it it;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
func(bkt->ptr, ptr);
|
||||
bkt->ptr = NULL;
|
||||
assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it);
|
||||
nghttp3_ksl_it_next(&it)) {
|
||||
func(nghttp3_ksl_it_get(&it), ptr);
|
||||
}
|
||||
|
||||
nghttp3_ksl_free(bkt->ksl);
|
||||
nghttp3_mem_free(map->mem, bkt->ksl);
|
||||
bkt->ksl = NULL;
|
||||
}
|
||||
func(bkt->data, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp3_map_each(nghttp3_map *map,
|
||||
int (*func)(nghttp3_map_entry *entry, void *ptr),
|
||||
int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
int rv;
|
||||
uint32_t i;
|
||||
nghttp3_map_bucket *bkt;
|
||||
nghttp3_ksl_it it;
|
||||
|
||||
if (map->size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = func(bkt->ptr, ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it);
|
||||
nghttp3_ksl_it_next(&it)) {
|
||||
rv = func(nghttp3_ksl_it_get(&it), ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) {
|
||||
entry->key = key;
|
||||
}
|
||||
|
||||
/* FNV1a hash */
|
||||
static uint32_t hash(key_type key, uint32_t mod) {
|
||||
uint8_t *p, *end;
|
||||
uint32_t h = 0x811C9DC5u;
|
||||
|
||||
key = nghttp3_htonl64(key);
|
||||
p = (uint8_t *)&key;
|
||||
end = p + sizeof(key_type);
|
||||
|
||||
for (; p != end;) {
|
||||
h ^= *p++;
|
||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
||||
}
|
||||
|
||||
return h & (mod - 1);
|
||||
}
|
||||
|
||||
static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) {
|
||||
return *(key_type *)lhs < *(key_type *)rhs;
|
||||
}
|
||||
|
||||
static int map_insert(nghttp3_map *map, nghttp3_map_bucket *table,
|
||||
uint32_t tablelen, nghttp3_map_entry *entry) {
|
||||
uint32_t h = hash(entry->key, tablelen);
|
||||
nghttp3_map_bucket *bkt = &table[h];
|
||||
const nghttp3_mem *mem = map->mem;
|
||||
int rv;
|
||||
|
||||
if (bkt->ptr == NULL &&
|
||||
(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) {
|
||||
bkt->ptr = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!bkt->ksl) {
|
||||
bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl));
|
||||
if (bkt->ksl == NULL) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
|
||||
}
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
|
||||
rv = func(bkt->data, ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bkt->ptr = NULL;
|
||||
}
|
||||
|
||||
return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 */
|
||||
static int map_resize(nghttp3_map *map, uint32_t new_tablelen) {
|
||||
static uint32_t hash(nghttp3_map_key_type key) {
|
||||
return (uint32_t)((key * 11400714819323198485llu) >> 32);
|
||||
}
|
||||
|
||||
static size_t h2idx(uint32_t hash, uint32_t bits) {
|
||||
return hash >> (32 - bits);
|
||||
}
|
||||
|
||||
static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
|
||||
nghttp3_map_bucket *bkt, size_t idx) {
|
||||
return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
|
||||
}
|
||||
|
||||
static void map_bucket_swap(nghttp3_map_bucket *bkt, uint32_t *phash,
|
||||
nghttp3_map_key_type *pkey, void **pdata) {
|
||||
uint32_t h = bkt->hash;
|
||||
nghttp3_map_key_type key = bkt->key;
|
||||
void *data = bkt->data;
|
||||
|
||||
bkt->hash = *phash;
|
||||
bkt->key = *pkey;
|
||||
bkt->data = *pdata;
|
||||
|
||||
*phash = h;
|
||||
*pkey = key;
|
||||
*pdata = data;
|
||||
}
|
||||
|
||||
static void map_bucket_set_data(nghttp3_map_bucket *bkt, uint32_t hash,
|
||||
nghttp3_map_key_type key, void *data) {
|
||||
bkt->hash = hash;
|
||||
bkt->key = key;
|
||||
bkt->data = data;
|
||||
}
|
||||
|
||||
void nghttp3_map_print_distance(nghttp3_map *map) {
|
||||
uint32_t i;
|
||||
size_t idx;
|
||||
nghttp3_map_bucket *bkt;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
fprintf(stderr, "@%u <EMPTY>\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = h2idx(bkt->hash, map->tablelenbits);
|
||||
fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
|
||||
bkt->hash, bkt->key, idx,
|
||||
distance(map->tablelen, map->tablelenbits, bkt, idx));
|
||||
}
|
||||
}
|
||||
|
||||
static int insert(nghttp3_map_bucket *table, uint32_t tablelen,
|
||||
uint32_t tablelenbits, uint32_t hash,
|
||||
nghttp3_map_key_type key, void *data) {
|
||||
size_t idx = h2idx(hash, tablelenbits);
|
||||
size_t d = 0, dd;
|
||||
nghttp3_map_bucket *bkt;
|
||||
|
||||
for (;;) {
|
||||
bkt = &table[idx];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
map_bucket_set_data(bkt, hash, key, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dd = distance(tablelen, tablelenbits, bkt, idx);
|
||||
if (d > dd) {
|
||||
map_bucket_swap(bkt, &hash, &key, &data);
|
||||
d = dd;
|
||||
} else if (bkt->key == key) {
|
||||
/* TODO This check is just a waste after first swap or if this
|
||||
function is called from map_resize. That said, there is no
|
||||
difference with or without this conditional in performance
|
||||
wise. */
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 and new_tablelen == (1 <<
|
||||
new_tablelenbits) must hold. */
|
||||
static int map_resize(nghttp3_map *map, uint32_t new_tablelen,
|
||||
uint32_t new_tablelenbits) {
|
||||
uint32_t i;
|
||||
nghttp3_map_bucket *new_table;
|
||||
nghttp3_map_bucket *bkt;
|
||||
nghttp3_ksl_it it;
|
||||
int rv;
|
||||
(void)rv;
|
||||
|
||||
new_table =
|
||||
nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket));
|
||||
@@ -203,64 +197,46 @@ static int map_resize(nghttp3_map *map, uint32_t new_tablelen) {
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = map_insert(map, new_table, new_tablelen, bkt->ptr);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
|
||||
bkt->data);
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it);
|
||||
nghttp3_ksl_it_next(&it)) {
|
||||
rv = map_insert(map, new_table, new_tablelen, nghttp3_ksl_it_get(&it));
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->ksl) {
|
||||
nghttp3_ksl_free(bkt->ksl);
|
||||
nghttp3_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
assert(0 == rv);
|
||||
}
|
||||
|
||||
nghttp3_mem_free(map->mem, map->table);
|
||||
map->tablelen = new_tablelen;
|
||||
map->tablelenbits = new_tablelenbits;
|
||||
map->table = new_table;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (i = 0; i < new_tablelen; ++i) {
|
||||
bkt = &new_table[i];
|
||||
if (bkt->ksl) {
|
||||
nghttp3_ksl_free(bkt->ksl);
|
||||
nghttp3_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) {
|
||||
int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) {
|
||||
int rv;
|
||||
|
||||
assert(data);
|
||||
|
||||
/* Load factor is 0.75 */
|
||||
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
||||
rv = map_resize(map, map->tablelen * 2);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
if (map->tablelen) {
|
||||
rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
rv = map_resize(map, 1 << NGHTTP3_INITIAL_TABLE_LENBITS,
|
||||
NGHTTP3_INITIAL_TABLE_LENBITS);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = map_insert(map, map->table, map->tablelen, new_entry);
|
||||
|
||||
rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
|
||||
data);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -268,68 +244,93 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key) {
|
||||
nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
||||
nghttp3_ksl_it it;
|
||||
void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key) {
|
||||
uint32_t h;
|
||||
size_t idx;
|
||||
nghttp3_map_bucket *bkt;
|
||||
size_t d = 0;
|
||||
|
||||
if (bkt->ptr) {
|
||||
if (bkt->ptr->key == key) {
|
||||
return bkt->ptr;
|
||||
}
|
||||
if (map->size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
it = nghttp3_ksl_lower_bound(bkt->ksl, &key);
|
||||
if (nghttp3_ksl_it_end(&it) ||
|
||||
*(key_type *)nghttp3_ksl_it_key(&it) != key) {
|
||||
h = hash(key);
|
||||
idx = h2idx(h, map->tablelenbits);
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL ||
|
||||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||
return NULL;
|
||||
}
|
||||
return nghttp3_ksl_it_get(&it);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
if (bkt->key == key) {
|
||||
return bkt->data;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp3_map_remove(nghttp3_map *map, key_type key) {
|
||||
nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
||||
int rv;
|
||||
int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) {
|
||||
uint32_t h;
|
||||
size_t idx, didx;
|
||||
nghttp3_map_bucket *bkt;
|
||||
size_t d = 0;
|
||||
|
||||
if (bkt->ptr) {
|
||||
if (bkt->ptr->key == key) {
|
||||
bkt->ptr = NULL;
|
||||
--map->size;
|
||||
return 0;
|
||||
}
|
||||
if (map->size == 0) {
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
--map->size;
|
||||
return 0;
|
||||
}
|
||||
h = hash(key);
|
||||
idx = h2idx(h, map->tablelenbits);
|
||||
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL ||
|
||||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->key == key) {
|
||||
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||
|
||||
didx = idx;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
if (bkt->data == NULL ||
|
||||
distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
map->table[didx] = *bkt;
|
||||
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||
didx = idx;
|
||||
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
|
||||
--map->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp3_map_clear(nghttp3_map *map) {
|
||||
uint32_t i;
|
||||
nghttp3_map_bucket *bkt;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
bkt->ptr = NULL;
|
||||
if (bkt->ksl) {
|
||||
nghttp3_ksl_free(bkt->ksl);
|
||||
nghttp3_mem_free(map->mem, bkt->ksl);
|
||||
bkt->ksl = NULL;
|
||||
}
|
||||
if (map->tablelen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
|
||||
map->size = 0;
|
||||
}
|
||||
|
||||
|
||||
+23
-40
@@ -34,21 +34,15 @@
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
#include "nghttp3_mem.h"
|
||||
#include "nghttp3_ksl.h"
|
||||
|
||||
/* Implementation of unordered map */
|
||||
|
||||
typedef uint64_t key_type;
|
||||
|
||||
typedef struct nghttp3_map_entry nghttp3_map_entry;
|
||||
|
||||
struct nghttp3_map_entry {
|
||||
key_type key;
|
||||
};
|
||||
typedef uint64_t nghttp3_map_key_type;
|
||||
|
||||
typedef struct nghttp3_map_bucket {
|
||||
nghttp3_map_entry *ptr;
|
||||
nghttp3_ksl *ksl;
|
||||
uint32_t hash;
|
||||
nghttp3_map_key_type key;
|
||||
void *data;
|
||||
} nghttp3_map_bucket;
|
||||
|
||||
typedef struct nghttp3_map {
|
||||
@@ -56,18 +50,13 @@ typedef struct nghttp3_map {
|
||||
const nghttp3_mem *mem;
|
||||
size_t size;
|
||||
uint32_t tablelen;
|
||||
uint32_t tablelenbits;
|
||||
} nghttp3_map;
|
||||
|
||||
/*
|
||||
* Initializes the map |map|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
|
||||
void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |map|. The stored entries
|
||||
@@ -79,21 +68,14 @@ void nghttp3_map_free(nghttp3_map *map);
|
||||
/*
|
||||
* Deallocates each entries using |func| function and any resources
|
||||
* allocated for |map|. The |func| function is responsible for freeing
|
||||
* given the |entry| object. The |ptr| will be passed to the |func| as
|
||||
* given the |data| object. The |ptr| will be passed to the |func| as
|
||||
* send argument. The return value of the |func| will be ignored.
|
||||
*/
|
||||
void nghttp3_map_each_free(nghttp3_map *map,
|
||||
int (*func)(nghttp3_map_entry *entry, void *ptr),
|
||||
void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr);
|
||||
|
||||
/*
|
||||
* Initializes the |entry| with the |key|. All entries to be inserted
|
||||
* to the map must be initialized with this function.
|
||||
*/
|
||||
void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key);
|
||||
|
||||
/*
|
||||
* Inserts the new |entry| with the key |entry->key| to the map |map|.
|
||||
* Inserts the new |data| with the |key| to the map |map|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -103,25 +85,25 @@ void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key);
|
||||
* NGHTTP3_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry);
|
||||
int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data);
|
||||
|
||||
/*
|
||||
* Returns the entry associated by the key |key|. If there is no such
|
||||
* entry, this function returns NULL.
|
||||
* Returns the data associated by the key |key|. If there is no such
|
||||
* data, this function returns NULL.
|
||||
*/
|
||||
nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key);
|
||||
void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key);
|
||||
|
||||
/*
|
||||
* Removes the entry associated by the key |key| from the |map|. The
|
||||
* removed entry is not freed by this function.
|
||||
* Removes the data associated by the key |key| from the |map|. The
|
||||
* removed data is not freed by this function.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP3_ERR_INVALID_ARGUMENT
|
||||
* The entry associated by |key| does not exist.
|
||||
* The data associated by |key| does not exist.
|
||||
*/
|
||||
int nghttp3_map_remove(nghttp3_map *map, key_type key);
|
||||
int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key);
|
||||
|
||||
/*
|
||||
* Removes all entries from |map|.
|
||||
@@ -134,21 +116,22 @@ void nghttp3_map_clear(nghttp3_map *map);
|
||||
size_t nghttp3_map_size(nghttp3_map *map);
|
||||
|
||||
/*
|
||||
* Applies the function |func| to each entry in the |map| with the
|
||||
* Applies the function |func| to each data in the |map| with the
|
||||
* optional user supplied pointer |ptr|.
|
||||
*
|
||||
* If the |func| returns 0, this function calls the |func| with the
|
||||
* next entry. If the |func| returns nonzero, it will not call the
|
||||
* next data. If the |func| returns nonzero, it will not call the
|
||||
* |func| for further entries and return the return value of the
|
||||
* |func| immediately. Thus, this function returns 0 if all the
|
||||
* invocations of the |func| return 0, or nonzero value which the last
|
||||
* invocation of |func| returns.
|
||||
*
|
||||
* Don't use this function to free each entry. Use
|
||||
* Don't use this function to free each data. Use
|
||||
* nghttp3_map_each_free() instead.
|
||||
*/
|
||||
int nghttp3_map_each(nghttp3_map *map,
|
||||
int (*func)(nghttp3_map_entry *entry, void *ptr),
|
||||
int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr);
|
||||
|
||||
void nghttp3_map_print_distance(nghttp3_map *map);
|
||||
|
||||
#endif /* NGHTTP3_MAP_H */
|
||||
+64
-17
@@ -26,26 +26,28 @@
|
||||
*/
|
||||
#include "nghttp3_mem.h"
|
||||
|
||||
static void *default_malloc(size_t size, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
#include <stdio.h>
|
||||
|
||||
static void *default_malloc(size_t size, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void default_free(void *ptr, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
static void default_free(void *ptr, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
static void *default_realloc(void *ptr, size_t size, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
@@ -55,23 +57,68 @@ static nghttp3_mem mem_default = {NULL, default_malloc, default_free,
|
||||
|
||||
const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; }
|
||||
|
||||
#ifndef MEMDEBUG
|
||||
void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) {
|
||||
return mem->malloc(size, mem->mem_user_data);
|
||||
return mem->malloc(size, mem->user_data);
|
||||
}
|
||||
|
||||
void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) {
|
||||
mem->free(ptr, mem->mem_user_data);
|
||||
}
|
||||
|
||||
void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr,
|
||||
void *mem_user_data) {
|
||||
free_func(ptr, mem_user_data);
|
||||
mem->free(ptr, mem->user_data);
|
||||
}
|
||||
|
||||
void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) {
|
||||
return mem->calloc(nmemb, size, mem->mem_user_data);
|
||||
return mem->calloc(nmemb, size, mem->user_data);
|
||||
}
|
||||
|
||||
void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) {
|
||||
return mem->realloc(ptr, size, mem->mem_user_data);
|
||||
return mem->realloc(ptr, size, mem->user_data);
|
||||
}
|
||||
#else /* MEMDEBUG */
|
||||
void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
|
||||
const char *func, const char *file,
|
||||
size_t line) {
|
||||
void *nptr = mem->malloc(size, mem->user_data);
|
||||
|
||||
fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
|
||||
file, line);
|
||||
|
||||
return nptr;
|
||||
}
|
||||
|
||||
void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
|
||||
const char *file, size_t line) {
|
||||
fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
|
||||
|
||||
mem->free(ptr, mem->user_data);
|
||||
}
|
||||
|
||||
void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr,
|
||||
void *user_data, const char *func,
|
||||
const char *file, size_t line) {
|
||||
fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
|
||||
|
||||
free_func(ptr, user_data);
|
||||
}
|
||||
|
||||
void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
|
||||
size_t size, const char *func, const char *file,
|
||||
size_t line) {
|
||||
void *nptr = mem->calloc(nmemb, size, mem->user_data);
|
||||
|
||||
fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
|
||||
size, func, file, line);
|
||||
|
||||
return nptr;
|
||||
}
|
||||
|
||||
void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
|
||||
const char *func, const char *file,
|
||||
size_t line) {
|
||||
void *nptr = mem->realloc(ptr, size, mem->user_data);
|
||||
|
||||
fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
|
||||
size, func, file, line);
|
||||
|
||||
return nptr;
|
||||
}
|
||||
#endif /* MEMDEBUG */
|
||||
+37
-2
@@ -35,11 +35,46 @@
|
||||
|
||||
/* Convenient wrapper functions to call allocator function in
|
||||
|mem|. */
|
||||
#ifndef MEMDEBUG
|
||||
void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size);
|
||||
void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr);
|
||||
void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr,
|
||||
void *mem_user_data);
|
||||
void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size);
|
||||
void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size);
|
||||
#else /* MEMDEBUG */
|
||||
void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
|
||||
const char *func, const char *file, size_t line);
|
||||
|
||||
# define nghttp3_mem_malloc(MEM, SIZE) \
|
||||
nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
|
||||
|
||||
void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
|
||||
const char *file, size_t line);
|
||||
|
||||
# define nghttp3_mem_free(MEM, PTR) \
|
||||
nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
|
||||
|
||||
void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data,
|
||||
const char *func, const char *file, size_t line);
|
||||
|
||||
# define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA) \
|
||||
nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__, \
|
||||
__FILE__, __LINE__)
|
||||
|
||||
void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
|
||||
size_t size, const char *func, const char *file,
|
||||
size_t line);
|
||||
|
||||
# define nghttp3_mem_calloc(MEM, NMEMB, SIZE) \
|
||||
nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
|
||||
__LINE__)
|
||||
|
||||
void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
|
||||
const char *func, const char *file,
|
||||
size_t line);
|
||||
|
||||
# define nghttp3_mem_realloc(MEM, PTR, SIZE) \
|
||||
nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, \
|
||||
__LINE__)
|
||||
#endif /* MEMDEBUG */
|
||||
|
||||
#endif /* NGHTTP3_MEM_H */
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* nghttp3
|
||||
*
|
||||
* Copyright (c) 2022 nghttp3 contributors
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "nghttp3_objalloc.h"
|
||||
|
||||
void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
|
||||
const nghttp3_mem *mem) {
|
||||
nghttp3_balloc_init(&objalloc->balloc, blklen, mem);
|
||||
nghttp3_opl_init(&objalloc->opl);
|
||||
}
|
||||
|
||||
void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) {
|
||||
nghttp3_balloc_free(&objalloc->balloc);
|
||||
}
|
||||
|
||||
void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) {
|
||||
nghttp3_opl_clear(&objalloc->opl);
|
||||
nghttp3_balloc_clear(&objalloc->balloc);
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* nghttp3
|
||||
*
|
||||
* Copyright (c) 2022 nghttp3 contributors
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGHTTP3_OBJALLOC_H
|
||||
#define NGHTTP3_OBJALLOC_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
#include "nghttp3_balloc.h"
|
||||
#include "nghttp3_opl.h"
|
||||
#include "nghttp3_macro.h"
|
||||
#include "nghttp3_mem.h"
|
||||
|
||||
/*
|
||||
* nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and
|
||||
* provides an object pool with the custom allocator to reduce the
|
||||
* allocation and deallocation overheads for small objects.
|
||||
*/
|
||||
typedef struct nghttp3_objalloc {
|
||||
nghttp3_balloc balloc;
|
||||
nghttp3_opl opl;
|
||||
} nghttp3_objalloc;
|
||||
|
||||
/*
|
||||
* nghttp3_objalloc_init initializes |objalloc|. |blklen| is directly
|
||||
* passed to nghttp3_balloc_init.
|
||||
*/
|
||||
void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
* nghttp3_objalloc_free releases all allocated resources.
|
||||
*/
|
||||
void nghttp3_objalloc_free(nghttp3_objalloc *objalloc);
|
||||
|
||||
/*
|
||||
* nghttp3_objalloc_clear releases all allocated resources and
|
||||
* initializes its state.
|
||||
*/
|
||||
void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc);
|
||||
|
||||
#ifndef NOMEMPOOL
|
||||
# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
|
||||
inline static void nghttp3_objalloc_##NAME##_init( \
|
||||
nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
|
||||
nghttp3_objalloc_init( \
|
||||
objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *nghttp3_objalloc_##NAME##_get( \
|
||||
nghttp3_objalloc *objalloc) { \
|
||||
nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
|
||||
TYPE *obj; \
|
||||
int rv; \
|
||||
\
|
||||
if (!oplent) { \
|
||||
rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, \
|
||||
sizeof(TYPE)); \
|
||||
if (rv != 0) { \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
return obj; \
|
||||
} \
|
||||
\
|
||||
return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
|
||||
nghttp3_objalloc *objalloc, size_t len) { \
|
||||
nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
|
||||
TYPE *obj; \
|
||||
int rv; \
|
||||
\
|
||||
if (!oplent) { \
|
||||
rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len); \
|
||||
if (rv != 0) { \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
return obj; \
|
||||
} \
|
||||
\
|
||||
return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
|
||||
} \
|
||||
\
|
||||
inline static void nghttp3_objalloc_##NAME##_release( \
|
||||
nghttp3_objalloc *objalloc, TYPE *obj) { \
|
||||
nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
|
||||
}
|
||||
#else /* NOMEMPOOL */
|
||||
# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
|
||||
inline static void nghttp3_objalloc_##NAME##_init( \
|
||||
nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
|
||||
nghttp3_objalloc_init( \
|
||||
objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *nghttp3_objalloc_##NAME##_get( \
|
||||
nghttp3_objalloc *objalloc) { \
|
||||
return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
|
||||
nghttp3_objalloc *objalloc, size_t len) { \
|
||||
return nghttp3_mem_malloc(objalloc->balloc.mem, len); \
|
||||
} \
|
||||
\
|
||||
inline static void nghttp3_objalloc_##NAME##_release( \
|
||||
nghttp3_objalloc *objalloc, TYPE *obj) { \
|
||||
nghttp3_mem_free(objalloc->balloc.mem, obj); \
|
||||
}
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
#endif /* NGHTTP3_OBJALLOC_H */
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* nghttp3
|
||||
*
|
||||
* Copyright (c) 2022 nghttp3 contributors
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "nghttp3_opl.h"
|
||||
|
||||
void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; }
|
||||
|
||||
void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) {
|
||||
ent->next = opl->head;
|
||||
opl->head = ent;
|
||||
}
|
||||
|
||||
nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) {
|
||||
nghttp3_opl_entry *ent = opl->head;
|
||||
|
||||
if (!ent) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
opl->head = ent->next;
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; }
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* nghttp3
|
||||
*
|
||||
* Copyright (c) 2022 nghttp3 contributors
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGHTTP3_OPL_H
|
||||
#define NGHTTP3_OPL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
typedef struct nghttp3_opl_entry nghttp3_opl_entry;
|
||||
|
||||
struct nghttp3_opl_entry {
|
||||
nghttp3_opl_entry *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* nghttp3_opl is an object memory pool.
|
||||
*/
|
||||
typedef struct nghttp3_opl {
|
||||
nghttp3_opl_entry *head;
|
||||
} nghttp3_opl;
|
||||
|
||||
/*
|
||||
* nghttp3_opl_init initializes |opl|.
|
||||
*/
|
||||
void nghttp3_opl_init(nghttp3_opl *opl);
|
||||
|
||||
/*
|
||||
* nghttp3_opl_push inserts |ent| to |opl| head.
|
||||
*/
|
||||
void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent);
|
||||
|
||||
/*
|
||||
* nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and
|
||||
* returns it. If |opl| does not have any entry, it returns NULL.
|
||||
*/
|
||||
nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl);
|
||||
|
||||
void nghttp3_opl_clear(nghttp3_opl *opl);
|
||||
|
||||
#endif /* NGHTTP3_OPL_H */
|
||||
Vendored
+1
-1
@@ -111,7 +111,7 @@ size_t nghttp3_pq_size(const nghttp3_pq *pq);
|
||||
typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg);
|
||||
|
||||
/*
|
||||
* Applys |fun| to each item in |pq|. The |arg| is passed as arg
|
||||
* Applies |fun| to each item in |pq|. The |arg| is passed as arg
|
||||
* parameter to callback function. This function must not change the
|
||||
* ordering key. If the return value from callback is nonzero, this
|
||||
* function returns 1 immediately without iterating remaining items.
|
||||
|
||||
+144
-138
@@ -168,8 +168,8 @@ static nghttp3_qpack_static_entry token_stable[] = {
|
||||
/* Make scalar initialization form of nghttp3_qpack_static_entry */
|
||||
#define MAKE_STATIC_HD(N, V, T) \
|
||||
{ \
|
||||
{NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
|
||||
{NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \
|
||||
{NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
|
||||
{NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \
|
||||
}
|
||||
|
||||
static nghttp3_qpack_static_header stable[] = {
|
||||
@@ -775,12 +775,12 @@ static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
|
||||
/*
|
||||
* qpack_context_can_reference returns nonzero if dynamic table entry
|
||||
* at |absidx| can be referenced. In other words, it is within
|
||||
* ctx->max_dtable_size.
|
||||
* ctx->max_dtable_capacity.
|
||||
*/
|
||||
static int qpack_context_can_reference(nghttp3_qpack_context *ctx,
|
||||
uint64_t absidx) {
|
||||
nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
|
||||
return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size;
|
||||
return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity;
|
||||
}
|
||||
|
||||
/* |*ppb_match| (post-base match), if it is not NULL, is always exact
|
||||
@@ -824,8 +824,14 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
|
||||
}
|
||||
|
||||
/*
|
||||
* qpack_context_init initializes |ctx|. |max_dtable_size| is the
|
||||
* maximum size of dynamic table. |mem| is a memory allocator.
|
||||
* qpack_context_init initializes |ctx|. |hard_max_dtable_capacity|
|
||||
* is the upper bound of the dynamic table capacity. |mem| is a
|
||||
* memory allocator.
|
||||
*
|
||||
* The maximum dynamic table size is governed by
|
||||
* ctx->max_dtable_capacity and it is initialized to 0.
|
||||
* |hard_max_dtable_capacity| is the upper bound of
|
||||
* ctx->max_dtable_capacity.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -834,7 +840,8 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
|
||||
* Out of memory.
|
||||
*/
|
||||
static int qpack_context_init(nghttp3_qpack_context *ctx,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
size_t max_blocked_streams,
|
||||
const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD;
|
||||
@@ -852,9 +859,9 @@ static int qpack_context_init(nghttp3_qpack_context *ctx,
|
||||
ctx->mem = mem;
|
||||
ctx->dtable_size = 0;
|
||||
ctx->dtable_sum = 0;
|
||||
ctx->hard_max_dtable_size = max_dtable_size;
|
||||
ctx->max_dtable_size = 0;
|
||||
ctx->max_blocked = max_blocked;
|
||||
ctx->hard_max_dtable_capacity = hard_max_dtable_capacity;
|
||||
ctx->max_dtable_capacity = 0;
|
||||
ctx->max_blocked_streams = max_blocked_streams;
|
||||
ctx->next_absidx = 0;
|
||||
ctx->bad = 0;
|
||||
|
||||
@@ -896,25 +903,19 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs,
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
|
||||
rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem);
|
||||
rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = nghttp3_map_init(&encoder->streams, mem);
|
||||
if (rv != 0) {
|
||||
goto streams_init_fail;
|
||||
}
|
||||
nghttp3_map_init(&encoder->streams, mem);
|
||||
|
||||
rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
|
||||
sizeof(nghttp3_blocked_streams_key), mem);
|
||||
if (rv != 0) {
|
||||
goto blocked_streams_init_fail;
|
||||
}
|
||||
nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
|
||||
sizeof(nghttp3_blocked_streams_key), mem);
|
||||
|
||||
qpack_map_init(&encoder->dtable_map);
|
||||
nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem);
|
||||
@@ -929,19 +930,11 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
|
||||
nghttp3_qpack_read_state_reset(&encoder->rstate);
|
||||
|
||||
return 0;
|
||||
|
||||
blocked_streams_init_fail:
|
||||
nghttp3_map_free(&encoder->streams);
|
||||
streams_init_fail:
|
||||
qpack_context_free(&encoder->ctx);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int map_stream_free(nghttp3_map_entry *entry, void *ptr) {
|
||||
static int map_stream_free(void *data, void *ptr) {
|
||||
const nghttp3_mem *mem = ptr;
|
||||
nghttp3_qpack_stream *stream =
|
||||
nghttp3_struct_of(entry, nghttp3_qpack_stream, me);
|
||||
nghttp3_qpack_stream *stream = data;
|
||||
nghttp3_qpack_stream_del(stream, mem);
|
||||
return 0;
|
||||
}
|
||||
@@ -955,49 +948,27 @@ void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) {
|
||||
qpack_context_free(&encoder->ctx);
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder,
|
||||
size_t max_dtable_size) {
|
||||
if (encoder->ctx.hard_max_dtable_size < max_dtable_size) {
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
void nghttp3_qpack_encoder_set_max_dtable_capacity(
|
||||
nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) {
|
||||
max_dtable_capacity =
|
||||
nghttp3_min(max_dtable_capacity, encoder->ctx.hard_max_dtable_capacity);
|
||||
|
||||
if (encoder->ctx.max_dtable_size == max_dtable_size) {
|
||||
return 0;
|
||||
if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
|
||||
|
||||
if (encoder->min_dtable_update > max_dtable_size) {
|
||||
encoder->min_dtable_update = max_dtable_size;
|
||||
encoder->ctx.max_dtable_size = max_dtable_size;
|
||||
if (encoder->min_dtable_update > max_dtable_capacity) {
|
||||
encoder->min_dtable_update = max_dtable_capacity;
|
||||
encoder->ctx.max_dtable_capacity = max_dtable_capacity;
|
||||
}
|
||||
encoder->last_max_dtable_update = max_dtable_size;
|
||||
|
||||
return 0;
|
||||
encoder->last_max_dtable_update = max_dtable_capacity;
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_set_hard_max_dtable_size(
|
||||
nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) {
|
||||
/* TODO This is not ideal. */
|
||||
if (encoder->ctx.hard_max_dtable_size) {
|
||||
return NGHTTP3_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
encoder->ctx.hard_max_dtable_size = hard_max_dtable_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder,
|
||||
size_t max_blocked) {
|
||||
/* TODO This is not ideal. */
|
||||
if (encoder->ctx.max_blocked) {
|
||||
return NGHTTP3_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
encoder->ctx.max_blocked = max_blocked;
|
||||
|
||||
return 0;
|
||||
void nghttp3_qpack_encoder_set_max_blocked_streams(
|
||||
nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) {
|
||||
encoder->ctx.max_blocked_streams = max_blocked_streams;
|
||||
}
|
||||
|
||||
uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) {
|
||||
@@ -1015,7 +986,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
|
||||
size_t len;
|
||||
nghttp3_qpack_entry *ent;
|
||||
|
||||
if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) {
|
||||
if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1023,7 +994,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
|
||||
min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
|
||||
}
|
||||
|
||||
for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) {
|
||||
for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_capacity;) {
|
||||
len = nghttp3_ringbuf_len(dtable);
|
||||
ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
|
||||
if (ent->absidx + 1 == min_cnt) {
|
||||
@@ -1069,7 +1040,8 @@ static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder,
|
||||
assert(rv == NGHTTP3_ERR_NOMEM);
|
||||
return rv;
|
||||
}
|
||||
rv = nghttp3_map_insert(&encoder->streams, &stream->me);
|
||||
rv = nghttp3_map_insert(&encoder->streams,
|
||||
(nghttp3_map_key_type)stream->stream_id, stream);
|
||||
if (rv != 0) {
|
||||
assert(rv == NGHTTP3_ERR_NOMEM);
|
||||
nghttp3_qpack_stream_del(stream, mem);
|
||||
@@ -1110,7 +1082,8 @@ static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder,
|
||||
size_t i, len;
|
||||
nghttp3_qpack_header_block_ref *ref;
|
||||
|
||||
nghttp3_map_remove(&encoder->streams, stream->me.key);
|
||||
nghttp3_map_remove(&encoder->streams,
|
||||
(nghttp3_map_key_type)stream->stream_id);
|
||||
|
||||
len = nghttp3_ringbuf_len(&stream->refs);
|
||||
for (i = 0; i < len; ++i) {
|
||||
@@ -1190,8 +1163,8 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder,
|
||||
blocked_stream =
|
||||
stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream);
|
||||
allow_blocking =
|
||||
blocked_stream ||
|
||||
encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams);
|
||||
blocked_stream || encoder->ctx.max_blocked_streams >
|
||||
nghttp3_ksl_len(&encoder->blocked_streams);
|
||||
|
||||
DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id,
|
||||
blocked_stream, allow_blocking);
|
||||
@@ -1265,7 +1238,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
|
||||
|
||||
nghttp3_qpack_encoder_shrink_dtable(encoder);
|
||||
|
||||
if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size ||
|
||||
if (encoder->ctx.max_dtable_capacity < encoder->ctx.dtable_size ||
|
||||
!(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1286,7 +1259,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
|
||||
|
||||
encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
|
||||
encoder->min_dtable_update = SIZE_MAX;
|
||||
encoder->ctx.max_dtable_size = encoder->last_max_dtable_update;
|
||||
encoder->ctx.max_dtable_capacity = encoder->last_max_dtable_update;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1300,9 +1273,7 @@ int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
|
||||
nghttp3_qpack_stream *
|
||||
nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
|
||||
int64_t stream_id) {
|
||||
nghttp3_map_entry *me =
|
||||
nghttp3_map_find(&encoder->streams, (uint64_t)stream_id);
|
||||
return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me);
|
||||
return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id);
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
|
||||
@@ -1352,7 +1323,7 @@ qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder,
|
||||
}
|
||||
|
||||
if (table_space(nv->namelen, nv->valuelen) >
|
||||
encoder->ctx.max_dtable_size * 3 / 4) {
|
||||
encoder->ctx.max_dtable_capacity * 3 / 4) {
|
||||
return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
|
||||
}
|
||||
|
||||
@@ -1372,8 +1343,8 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
|
||||
nghttp3_qpack_entry *min_ent, *last_ent;
|
||||
nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
|
||||
|
||||
if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) {
|
||||
avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size;
|
||||
if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) {
|
||||
avail = encoder->ctx.max_dtable_capacity - encoder->ctx.dtable_size;
|
||||
if (need <= avail) {
|
||||
return 1;
|
||||
}
|
||||
@@ -1385,7 +1356,7 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
|
||||
}
|
||||
|
||||
if (min_cnt == UINT64_MAX) {
|
||||
return encoder->ctx.max_dtable_size >= need;
|
||||
return encoder->ctx.max_dtable_capacity >= need;
|
||||
}
|
||||
|
||||
min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1);
|
||||
@@ -1434,8 +1405,8 @@ static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder,
|
||||
*/
|
||||
static int qpack_context_check_draining(nghttp3_qpack_context *ctx,
|
||||
uint64_t absidx) {
|
||||
const size_t safe =
|
||||
ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8);
|
||||
const size_t safe = ctx->max_dtable_capacity -
|
||||
nghttp3_min(512, ctx->max_dtable_capacity * 1 / 8);
|
||||
nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
|
||||
|
||||
return ctx->dtable_sum - ent->sum > safe;
|
||||
@@ -1714,7 +1685,7 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
|
||||
|
||||
nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem);
|
||||
|
||||
stream->me.key = (uint64_t)stream_id;
|
||||
stream->stream_id = stream_id;
|
||||
|
||||
*pstream = stream;
|
||||
|
||||
@@ -2059,9 +2030,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
|
||||
|
||||
space = table_space(qnv->name->len, qnv->value->len);
|
||||
|
||||
assert(space <= ctx->max_dtable_size);
|
||||
assert(space <= ctx->max_dtable_capacity);
|
||||
|
||||
while (ctx->dtable_size + space > ctx->max_dtable_size) {
|
||||
while (ctx->dtable_size + space > ctx->max_dtable_capacity) {
|
||||
i = nghttp3_ringbuf_len(&ctx->dtable);
|
||||
assert(i);
|
||||
ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
|
||||
@@ -2265,7 +2236,7 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
|
||||
nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
|
||||
nghttp3_qpack_header_block_ref, max_cnts_pe)
|
||||
->max_cnt,
|
||||
stream->me.key};
|
||||
(uint64_t)stream->stream_id};
|
||||
|
||||
return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream);
|
||||
}
|
||||
@@ -2276,7 +2247,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
|
||||
nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
|
||||
nghttp3_qpack_header_block_ref, max_cnts_pe)
|
||||
->max_cnt,
|
||||
stream->me.key};
|
||||
(uint64_t)stream->stream_id};
|
||||
nghttp3_ksl_it it;
|
||||
|
||||
/* This is purely debugging purpose only */
|
||||
@@ -2285,7 +2256,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
|
||||
assert(!nghttp3_ksl_it_end(&it));
|
||||
assert(nghttp3_ksl_it_get(&it) == stream);
|
||||
|
||||
nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk);
|
||||
nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk);
|
||||
}
|
||||
|
||||
void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
|
||||
@@ -2297,21 +2268,19 @@ void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
|
||||
|
||||
for (; !nghttp3_ksl_it_end(&it);) {
|
||||
bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it);
|
||||
nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk);
|
||||
nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk);
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
|
||||
int64_t stream_id) {
|
||||
int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
|
||||
int64_t stream_id) {
|
||||
nghttp3_qpack_stream *stream =
|
||||
nghttp3_qpack_encoder_find_stream(encoder, stream_id);
|
||||
const nghttp3_mem *mem = encoder->ctx.mem;
|
||||
nghttp3_qpack_header_block_ref *ref;
|
||||
|
||||
if (stream == NULL) {
|
||||
/* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we
|
||||
don't create stream which does not use dynamic table. */
|
||||
return;
|
||||
return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
|
||||
}
|
||||
|
||||
assert(nghttp3_ringbuf_len(&stream->refs));
|
||||
@@ -2338,16 +2307,17 @@ void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
|
||||
nghttp3_qpack_header_block_ref_del(ref, mem);
|
||||
|
||||
if (nghttp3_ringbuf_len(&stream->refs)) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
qpack_encoder_remove_stream(encoder, stream);
|
||||
|
||||
nghttp3_qpack_stream_del(stream, mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder,
|
||||
uint64_t n) {
|
||||
int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) {
|
||||
if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) {
|
||||
return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
|
||||
}
|
||||
@@ -2365,6 +2335,7 @@ void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) {
|
||||
nghttp3_pq_clear(&encoder->min_cnts);
|
||||
nghttp3_map_each_free(&encoder->streams, map_stream_free,
|
||||
(void *)encoder->ctx.mem);
|
||||
nghttp3_map_clear(&encoder->streams);
|
||||
}
|
||||
|
||||
void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
|
||||
@@ -2386,7 +2357,8 @@ void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
|
||||
nghttp3_qpack_stream_del(stream, mem);
|
||||
}
|
||||
|
||||
size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) {
|
||||
size_t
|
||||
nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder) {
|
||||
return nghttp3_ksl_len(&encoder->blocked_streams);
|
||||
}
|
||||
|
||||
@@ -2394,7 +2366,7 @@ int nghttp3_qpack_encoder_write_field_section_prefix(
|
||||
nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
|
||||
uint64_t base) {
|
||||
size_t max_ents =
|
||||
encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD;
|
||||
encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
|
||||
uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1;
|
||||
int sign = base < ricnt;
|
||||
uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt;
|
||||
@@ -2563,15 +2535,17 @@ nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder,
|
||||
|
||||
switch (encoder->opcode) {
|
||||
case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT:
|
||||
rv = nghttp3_qpack_encoder_add_insert_count(encoder,
|
||||
encoder->rstate.left);
|
||||
rv = nghttp3_qpack_encoder_add_icnt(encoder, encoder->rstate.left);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK:
|
||||
nghttp3_qpack_encoder_ack_header(encoder,
|
||||
(int64_t)encoder->rstate.left);
|
||||
rv = nghttp3_qpack_encoder_ack_header(encoder,
|
||||
(int64_t)encoder->rstate.left);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL:
|
||||
nghttp3_qpack_encoder_cancel_stream(encoder,
|
||||
@@ -2661,11 +2635,13 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) {
|
||||
}
|
||||
|
||||
int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
size_t max_blocked_streams,
|
||||
const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
|
||||
rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem);
|
||||
rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity,
|
||||
max_blocked_streams, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -2741,7 +2717,7 @@ static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate,
|
||||
/*
|
||||
* qpack_decoder_validate_index checks rstate->absidx is acceptable.
|
||||
*
|
||||
* It returns 0 if it suceeds, or one of the following negative error
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGHTTP3_ERR_QPACK_FATAL
|
||||
@@ -2843,14 +2819,14 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
|
||||
}
|
||||
|
||||
if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) {
|
||||
if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) {
|
||||
DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n",
|
||||
decoder->rstate.left);
|
||||
rv = nghttp3_qpack_decoder_set_max_dtable_capacity(
|
||||
decoder, (size_t)decoder->rstate.left);
|
||||
if (rv != 0) {
|
||||
rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n",
|
||||
decoder->rstate.left);
|
||||
nghttp3_qpack_decoder_set_dtable_cap(decoder,
|
||||
(size_t)decoder->rstate.left);
|
||||
|
||||
decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
|
||||
nghttp3_qpack_read_state_reset(&decoder->rstate);
|
||||
@@ -3036,6 +3012,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
|
||||
default:
|
||||
/* Unreachable */
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
@@ -3070,6 +3047,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
|
||||
default:
|
||||
/* Unreachable */
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
@@ -3088,16 +3066,20 @@ fail:
|
||||
return rv;
|
||||
}
|
||||
|
||||
void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
|
||||
size_t cap) {
|
||||
int nghttp3_qpack_decoder_set_max_dtable_capacity(
|
||||
nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) {
|
||||
nghttp3_qpack_entry *ent;
|
||||
size_t i;
|
||||
nghttp3_qpack_context *ctx = &decoder->ctx;
|
||||
const nghttp3_mem *mem = ctx->mem;
|
||||
|
||||
ctx->max_dtable_size = cap;
|
||||
if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) {
|
||||
return NGHTTP3_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
while (ctx->dtable_size > cap) {
|
||||
ctx->max_dtable_capacity = max_dtable_capacity;
|
||||
|
||||
while (ctx->dtable_size > max_dtable_capacity) {
|
||||
i = nghttp3_ringbuf_len(&ctx->dtable);
|
||||
assert(i);
|
||||
ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
|
||||
@@ -3108,6 +3090,8 @@ void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_entry_free(ent);
|
||||
nghttp3_mem_free(mem, ent);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) {
|
||||
@@ -3131,7 +3115,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) {
|
||||
shd = &stable[decoder->rstate.absidx];
|
||||
|
||||
if (table_space(shd->name.len, decoder->rstate.value->len) >
|
||||
decoder->ctx.max_dtable_size) {
|
||||
decoder->ctx.max_dtable_capacity) {
|
||||
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
|
||||
}
|
||||
|
||||
@@ -3155,7 +3139,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) {
|
||||
ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
|
||||
|
||||
if (table_space(ent->nv.name->len, decoder->rstate.value->len) >
|
||||
decoder->ctx.max_dtable_size) {
|
||||
decoder->ctx.max_dtable_capacity) {
|
||||
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
|
||||
}
|
||||
|
||||
@@ -3185,7 +3169,7 @@ int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) {
|
||||
ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
|
||||
|
||||
if (table_space(ent->nv.name->len, ent->nv.value->len) >
|
||||
decoder->ctx.max_dtable_size) {
|
||||
decoder->ctx.max_dtable_capacity) {
|
||||
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
|
||||
}
|
||||
|
||||
@@ -3210,7 +3194,7 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) {
|
||||
(int)decoder->rstate.value->len, decoder->rstate.value->base);
|
||||
|
||||
if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) >
|
||||
decoder->ctx.max_dtable_size) {
|
||||
decoder->ctx.max_dtable_capacity) {
|
||||
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
|
||||
}
|
||||
|
||||
@@ -3328,7 +3312,7 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
|
||||
}
|
||||
|
||||
if (sctx->dbase_sign) {
|
||||
if (sctx->ricnt < sctx->rstate.left) {
|
||||
if (sctx->ricnt <= sctx->rstate.left) {
|
||||
rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
|
||||
goto fail;
|
||||
}
|
||||
@@ -3595,7 +3579,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
|
||||
switch (sctx->opcode) {
|
||||
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
|
||||
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
|
||||
nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
|
||||
rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
|
||||
nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
|
||||
@@ -3629,7 +3617,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
|
||||
switch (sctx->opcode) {
|
||||
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
|
||||
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
|
||||
nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
|
||||
rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
|
||||
nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
|
||||
@@ -3733,6 +3725,7 @@ void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
|
||||
uint8_t *p;
|
||||
uint64_t n = 0;
|
||||
size_t len = 0;
|
||||
(void)len;
|
||||
|
||||
if (decoder->written_icnt < decoder->ctx.next_absidx) {
|
||||
n = decoder->ctx.next_absidx - decoder->written_icnt;
|
||||
@@ -3789,7 +3782,8 @@ int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
|
||||
return 0;
|
||||
}
|
||||
|
||||
max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD;
|
||||
max_ents =
|
||||
decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
|
||||
full = 2 * max_ents;
|
||||
|
||||
if (encricnt > full) {
|
||||
@@ -3938,13 +3932,19 @@ qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
sctx->rstate.value = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
nghttp3_qpack_nv *nv) {
|
||||
nghttp3_qpack_entry *ent =
|
||||
nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
|
||||
(void)decoder;
|
||||
nghttp3_qpack_entry *ent;
|
||||
|
||||
/* A broken encoder might change dtable capacity while processing
|
||||
request stream instruction. Check the absidx again. */
|
||||
if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) {
|
||||
return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
|
||||
}
|
||||
|
||||
ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
|
||||
|
||||
nv->name = ent->nv.name;
|
||||
nv->value = sctx->rstate.value;
|
||||
@@ -3955,11 +3955,13 @@ qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_rcbuf_incref(nv->name);
|
||||
|
||||
sctx->rstate.value = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
nghttp3_qpack_nv *nv) {
|
||||
int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
nghttp3_qpack_nv *nv) {
|
||||
(void)decoder;
|
||||
|
||||
DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n",
|
||||
@@ -3967,10 +3969,12 @@ void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
(int)sctx->rstate.value->len, sctx->rstate.value->base);
|
||||
|
||||
if (sctx->rstate.dynamic) {
|
||||
qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv);
|
||||
} else {
|
||||
qpack_decoder_emit_static_indexed_name(decoder, sctx, nv);
|
||||
return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv);
|
||||
}
|
||||
|
||||
qpack_decoder_emit_static_indexed_name(decoder, sctx, nv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
|
||||
@@ -3993,7 +3997,7 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
|
||||
}
|
||||
|
||||
int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
nghttp3_qpack_encoder *p;
|
||||
@@ -4003,7 +4007,7 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
|
||||
rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem);
|
||||
rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -4057,7 +4061,8 @@ void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) {
|
||||
}
|
||||
|
||||
int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
size_t max_blocked_streams,
|
||||
const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
nghttp3_qpack_decoder *p;
|
||||
@@ -4067,7 +4072,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
|
||||
rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem);
|
||||
rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity,
|
||||
max_blocked_streams, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
+60
-25
@@ -118,7 +118,7 @@ void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
typedef struct nghttp3_qpack_stream {
|
||||
nghttp3_map_entry me;
|
||||
int64_t stream_id;
|
||||
/* refs is an array of pointer to nghttp3_qpack_header_block_ref in
|
||||
the order of the time they are encoded. HTTP/3 allows multiple
|
||||
header blocks (e.g., non-final response headers, final response
|
||||
@@ -154,18 +154,15 @@ typedef struct nghttp3_qpack_context {
|
||||
NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */
|
||||
size_t dtable_size;
|
||||
size_t dtable_sum;
|
||||
/* hard_max_dtable_size is the maximum size of dynamic table. In
|
||||
HTTP/3, it is notified by decoder as
|
||||
SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal
|
||||
to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has
|
||||
the authority to decide how many entries are inserted into
|
||||
dynamic table. */
|
||||
size_t hard_max_dtable_size;
|
||||
/* max_dtable_size is the effective maximum size of dynamic table. */
|
||||
size_t max_dtable_size;
|
||||
/* max_blocked is the maximum number of stream which can be
|
||||
/* hard_max_dtable_capacity is the upper bound of
|
||||
max_dtable_capacity. */
|
||||
size_t hard_max_dtable_capacity;
|
||||
/* max_dtable_capacity is the maximum capacity of the dynamic
|
||||
table. */
|
||||
size_t max_dtable_capacity;
|
||||
/* max_blocked_streams is the maximum number of stream which can be
|
||||
blocked. */
|
||||
size_t max_blocked;
|
||||
size_t max_blocked_streams;
|
||||
/* next_absidx is the next absolute index for nghttp3_qpack_entry.
|
||||
It is equivalent to insert count. */
|
||||
uint64_t next_absidx;
|
||||
@@ -218,10 +215,10 @@ typedef enum nghttp3_qpack_decoder_stream_opcode {
|
||||
/* QPACK encoder flags */
|
||||
|
||||
/* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00
|
||||
#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u
|
||||
/* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that
|
||||
Set Dynamic Table Capacity is required. */
|
||||
#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01
|
||||
#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u
|
||||
|
||||
struct nghttp3_qpack_encoder {
|
||||
nghttp3_qpack_context ctx;
|
||||
@@ -260,9 +257,8 @@ struct nghttp3_qpack_encoder {
|
||||
|
||||
/*
|
||||
* nghttp3_qpack_encoder_init initializes |encoder|.
|
||||
* |max_dtable_size| is the maximum size of dynamic table.
|
||||
* |max_blocked| is the maximum number of stream which can be blocked.
|
||||
* |mem| is a memory allocator.
|
||||
* |hard_max_dtable_capacity| is the upper bound of the dynamic table
|
||||
* capacity. |mem| is a memory allocator.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -271,7 +267,7 @@ struct nghttp3_qpack_encoder {
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
@@ -628,6 +624,44 @@ int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
|
||||
const nghttp3_nv *nv,
|
||||
int32_t token, uint32_t hash);
|
||||
|
||||
/*
|
||||
* `nghttp3_qpack_encoder_ack_header` tells |encoder| that header
|
||||
* block for a stream denoted by |stream_id| was acknowledged by
|
||||
* decoder.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
|
||||
* Section Acknowledgement for a stream denoted by |stream_id| is
|
||||
* unexpected.
|
||||
*/
|
||||
int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
|
||||
int64_t stream_id);
|
||||
|
||||
/*
|
||||
* `nghttp3_qpack_encoder_add_icnt` increments known received count of
|
||||
* |encoder| by |n|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* :macro:`NGHTTP3_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
* :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
|
||||
* |n| is too large.
|
||||
*/
|
||||
int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n);
|
||||
|
||||
/*
|
||||
* `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream
|
||||
* denoted by |stream_id| is cancelled. This function is provided for
|
||||
* debugging purpose only. In HTTP/3, |encoder| knows this by reading
|
||||
* decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
|
||||
*/
|
||||
void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
|
||||
int64_t stream_id);
|
||||
|
||||
/*
|
||||
* nghttp3_qpack_context_dtable_get returns dynamic table entry whose
|
||||
* absolute index is |absidx|. This function assumes that such entry
|
||||
@@ -749,9 +783,9 @@ struct nghttp3_qpack_decoder {
|
||||
|
||||
/*
|
||||
* nghttp3_qpack_decoder_init initializes |decoder|.
|
||||
* |max_dtable_size| is the maximum size of dynamic table.
|
||||
* |max_blocked| is the maximum number of stream which can be blocked.
|
||||
* |mem| is a memory allocator.
|
||||
* |hard_max_dtable_capacity| is the upper bound of the dynamic table
|
||||
* capacity. |max_blocked_streams| is the maximum number of stream
|
||||
* which can be blocked. |mem| is a memory allocator.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -760,7 +794,8 @@ struct nghttp3_qpack_decoder {
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
|
||||
size_t max_dtable_size, size_t max_blocked,
|
||||
size_t hard_max_dtable_capacity,
|
||||
size_t max_blocked_streams,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
/*
|
||||
@@ -935,9 +970,9 @@ void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
nghttp3_qpack_nv *nv);
|
||||
|
||||
void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
nghttp3_qpack_nv *nv);
|
||||
int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
nghttp3_qpack_nv *nv);
|
||||
|
||||
void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
|
||||
nghttp3_qpack_stream_context *sctx,
|
||||
|
||||
+2
-3
@@ -41,8 +41,7 @@ int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
|
||||
|
||||
*rcbuf_ptr = (void *)p;
|
||||
|
||||
(*rcbuf_ptr)->mem_user_data = mem->mem_user_data;
|
||||
(*rcbuf_ptr)->free = mem->free;
|
||||
(*rcbuf_ptr)->mem = mem;
|
||||
(*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf);
|
||||
(*rcbuf_ptr)->len = size;
|
||||
(*rcbuf_ptr)->ref = 1;
|
||||
@@ -76,7 +75,7 @@ int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
|
||||
* Frees |rcbuf| itself, regardless of its reference cout.
|
||||
*/
|
||||
void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) {
|
||||
nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data);
|
||||
nghttp3_mem_free(rcbuf->mem, rcbuf);
|
||||
}
|
||||
|
||||
void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) {
|
||||
|
||||
+3
-4
@@ -33,10 +33,9 @@
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
struct nghttp3_rcbuf {
|
||||
/* custom memory allocator belongs to the mem parameter when
|
||||
creating this object. */
|
||||
void *mem_user_data;
|
||||
nghttp3_free free;
|
||||
/* mem is the memory allocator that allocates memory for this
|
||||
object. */
|
||||
const nghttp3_mem *mem;
|
||||
/* The pointer to the underlying buffer */
|
||||
uint8_t *base;
|
||||
/* Size of buffer pointed by |base|. */
|
||||
|
||||
+124
-160
@@ -34,6 +34,7 @@
|
||||
#include "nghttp3_conn.h"
|
||||
#include "nghttp3_str.h"
|
||||
#include "nghttp3_http.h"
|
||||
#include "nghttp3_vec.h"
|
||||
|
||||
/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which
|
||||
makes a copy to outq. */
|
||||
@@ -44,45 +45,36 @@
|
||||
|
||||
int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
|
||||
uint64_t seq, const nghttp3_stream_callbacks *callbacks,
|
||||
nghttp3_objalloc *out_chunk_objalloc,
|
||||
nghttp3_objalloc *stream_objalloc,
|
||||
const nghttp3_mem *mem) {
|
||||
int rv;
|
||||
nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream));
|
||||
nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc);
|
||||
nghttp3_node_id nid;
|
||||
|
||||
if (stream == NULL) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
|
||||
stream->out_chunk_objalloc = out_chunk_objalloc;
|
||||
stream->stream_objalloc = stream_objalloc;
|
||||
|
||||
nghttp3_tnode_init(
|
||||
&stream->node,
|
||||
nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq,
|
||||
NGHTTP3_DEFAULT_URGENCY);
|
||||
|
||||
rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem);
|
||||
if (rv != 0) {
|
||||
goto frq_init_fail;
|
||||
}
|
||||
|
||||
rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem);
|
||||
if (rv != 0) {
|
||||
goto chunks_init_fail;
|
||||
}
|
||||
|
||||
rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem);
|
||||
if (rv != 0) {
|
||||
goto outq_init_fail;
|
||||
}
|
||||
|
||||
rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem);
|
||||
if (rv != 0) {
|
||||
goto inq_init_fail;
|
||||
}
|
||||
nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem);
|
||||
nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem);
|
||||
nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem);
|
||||
nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem);
|
||||
|
||||
nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem);
|
||||
|
||||
stream->me.key = (key_type)stream_id;
|
||||
stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
|
||||
stream->mem = mem;
|
||||
stream->tx.offset = 0;
|
||||
stream->rx.http.status_code = -1;
|
||||
stream->rx.http.content_length = -1;
|
||||
stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY;
|
||||
@@ -95,17 +87,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
|
||||
*pstream = stream;
|
||||
|
||||
return 0;
|
||||
|
||||
inq_init_fail:
|
||||
nghttp3_ringbuf_free(&stream->outq);
|
||||
outq_init_fail:
|
||||
nghttp3_ringbuf_free(&stream->chunks);
|
||||
chunks_init_fail:
|
||||
nghttp3_ringbuf_free(&stream->frq);
|
||||
frq_init_fail:
|
||||
nghttp3_mem_free(mem, stream);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) {
|
||||
@@ -134,6 +115,27 @@ static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) {
|
||||
nghttp3_ringbuf_free(chunks);
|
||||
}
|
||||
|
||||
static void delete_out_chunks(nghttp3_ringbuf *chunks,
|
||||
nghttp3_objalloc *out_chunk_objalloc,
|
||||
const nghttp3_mem *mem) {
|
||||
nghttp3_buf *buf;
|
||||
size_t i, len = nghttp3_ringbuf_len(chunks);
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
buf = nghttp3_ringbuf_get(chunks, i);
|
||||
|
||||
if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
|
||||
nghttp3_objalloc_chunk_release(out_chunk_objalloc,
|
||||
(nghttp3_chunk *)(void *)buf->begin);
|
||||
continue;
|
||||
}
|
||||
|
||||
nghttp3_buf_free(buf, mem);
|
||||
}
|
||||
|
||||
nghttp3_ringbuf_free(chunks);
|
||||
}
|
||||
|
||||
static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
|
||||
nghttp3_frame_entry *frent;
|
||||
size_t i, len = nghttp3_ringbuf_len(frq);
|
||||
@@ -144,9 +146,6 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
|
||||
case NGHTTP3_FRAME_HEADERS:
|
||||
nghttp3_frame_headers_free(&frent->fr.headers, mem);
|
||||
break;
|
||||
case NGHTTP3_FRAME_PUSH_PROMISE:
|
||||
nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -163,11 +162,11 @@ void nghttp3_stream_del(nghttp3_stream *stream) {
|
||||
nghttp3_qpack_stream_context_free(&stream->qpack_sctx);
|
||||
delete_chunks(&stream->inq, stream->mem);
|
||||
delete_outq(&stream->outq, stream->mem);
|
||||
delete_chunks(&stream->chunks, stream->mem);
|
||||
delete_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem);
|
||||
delete_frq(&stream->frq, stream->mem);
|
||||
nghttp3_tnode_free(&stream->node);
|
||||
|
||||
nghttp3_mem_free(stream->mem, stream);
|
||||
nghttp3_objalloc_stream_release(stream->stream_objalloc, stream);
|
||||
}
|
||||
|
||||
void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) {
|
||||
@@ -267,19 +266,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
|
||||
}
|
||||
nghttp3_frame_headers_free(&frent->fr.headers, stream->mem);
|
||||
break;
|
||||
case NGHTTP3_FRAME_PUSH_PROMISE:
|
||||
rv = nghttp3_stream_write_push_promise(stream, frent);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem);
|
||||
break;
|
||||
case NGHTTP3_FRAME_CANCEL_PUSH:
|
||||
rv = nghttp3_stream_write_cancel_push(stream, frent);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
case NGHTTP3_FRAME_DATA:
|
||||
rv = nghttp3_stream_write_data(stream, &data_eof, frent);
|
||||
if (rv != 0) {
|
||||
@@ -292,11 +278,14 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case NGHTTP3_FRAME_MAX_PUSH_ID:
|
||||
if (stream->conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) {
|
||||
break;
|
||||
case NGHTTP3_FRAME_GOAWAY:
|
||||
rv = nghttp3_stream_write_goaway(stream, frent);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
rv = nghttp3_stream_write_max_push_id(stream, frent);
|
||||
break;
|
||||
case NGHTTP3_FRAME_PRIORITY_UPDATE:
|
||||
rv = nghttp3_stream_write_priority_update(stream, frent);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -338,34 +327,6 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
|
||||
return nghttp3_stream_outq_add(stream, &tbuf);
|
||||
}
|
||||
|
||||
int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) {
|
||||
size_t len;
|
||||
nghttp3_buf *chunk;
|
||||
nghttp3_typed_buf tbuf;
|
||||
int rv;
|
||||
nghttp3_push_promise *pp = stream->pp;
|
||||
|
||||
assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH);
|
||||
assert(pp);
|
||||
|
||||
len = nghttp3_put_varint_len((int64_t)stream->type) +
|
||||
nghttp3_put_varint_len(pp->node.nid.id);
|
||||
|
||||
rv = nghttp3_stream_ensure_chunk(stream, len);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
chunk = nghttp3_stream_get_chunk(stream);
|
||||
typed_buf_shared_init(&tbuf, chunk);
|
||||
|
||||
chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type);
|
||||
chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id);
|
||||
tbuf.buf.last = chunk->last;
|
||||
|
||||
return nghttp3_stream_outq_add(stream, &tbuf);
|
||||
}
|
||||
|
||||
int nghttp3_stream_write_settings(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent) {
|
||||
size_t len;
|
||||
@@ -386,10 +347,16 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
|
||||
iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE;
|
||||
iv[0].value = local_settings->max_field_section_size;
|
||||
iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
|
||||
iv[1].value = local_settings->qpack_max_table_capacity;
|
||||
iv[1].value = local_settings->qpack_max_dtable_capacity;
|
||||
iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
|
||||
iv[2].value = local_settings->qpack_blocked_streams;
|
||||
|
||||
if (local_settings->enable_connect_protocol) {
|
||||
++fr.settings.niv;
|
||||
iv[3].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
|
||||
iv[3].value = 1;
|
||||
}
|
||||
|
||||
len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings);
|
||||
|
||||
rv = nghttp3_stream_ensure_chunk(stream, len);
|
||||
@@ -407,15 +374,15 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
|
||||
return nghttp3_stream_outq_add(stream, &tbuf);
|
||||
}
|
||||
|
||||
int nghttp3_stream_write_cancel_push(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent) {
|
||||
nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push;
|
||||
int nghttp3_stream_write_goaway(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent) {
|
||||
nghttp3_frame_goaway *fr = &frent->fr.goaway;
|
||||
size_t len;
|
||||
int rv;
|
||||
nghttp3_buf *chunk;
|
||||
nghttp3_typed_buf tbuf;
|
||||
|
||||
len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr);
|
||||
len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr);
|
||||
|
||||
rv = nghttp3_stream_ensure_chunk(stream, len);
|
||||
if (rv != 0) {
|
||||
@@ -425,30 +392,22 @@ int nghttp3_stream_write_cancel_push(nghttp3_stream *stream,
|
||||
chunk = nghttp3_stream_get_chunk(stream);
|
||||
typed_buf_shared_init(&tbuf, chunk);
|
||||
|
||||
chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr);
|
||||
chunk->last = nghttp3_frame_write_goaway(chunk->last, fr);
|
||||
|
||||
tbuf.buf.last = chunk->last;
|
||||
|
||||
return nghttp3_stream_outq_add(stream, &tbuf);
|
||||
}
|
||||
|
||||
int nghttp3_stream_write_max_push_id(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent) {
|
||||
nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id;
|
||||
nghttp3_conn *conn = stream->conn;
|
||||
int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent) {
|
||||
nghttp3_frame_priority_update *fr = &frent->fr.priority_update;
|
||||
size_t len;
|
||||
int rv;
|
||||
nghttp3_buf *chunk;
|
||||
nghttp3_typed_buf tbuf;
|
||||
|
||||
assert(conn);
|
||||
assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED);
|
||||
|
||||
fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1;
|
||||
conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes;
|
||||
conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED;
|
||||
|
||||
len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr);
|
||||
len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr);
|
||||
|
||||
rv = nghttp3_stream_ensure_chunk(stream, len);
|
||||
if (rv != 0) {
|
||||
@@ -458,7 +417,7 @@ int nghttp3_stream_write_max_push_id(nghttp3_stream *stream,
|
||||
chunk = nghttp3_stream_get_chunk(stream);
|
||||
typed_buf_shared_init(&tbuf, chunk);
|
||||
|
||||
chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr);
|
||||
chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr);
|
||||
|
||||
tbuf.buf.last = chunk->last;
|
||||
|
||||
@@ -474,35 +433,21 @@ int nghttp3_stream_write_headers(nghttp3_stream *stream,
|
||||
|
||||
return nghttp3_stream_write_header_block(
|
||||
stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf,
|
||||
&conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen);
|
||||
}
|
||||
|
||||
int nghttp3_stream_write_push_promise(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent) {
|
||||
nghttp3_frame_push_promise *fr = &frent->fr.push_promise;
|
||||
nghttp3_conn *conn = stream->conn;
|
||||
|
||||
assert(conn);
|
||||
|
||||
return nghttp3_stream_write_header_block(
|
||||
stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf,
|
||||
&conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva,
|
||||
fr->nvlen);
|
||||
&conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, fr->nva, fr->nvlen);
|
||||
}
|
||||
|
||||
int nghttp3_stream_write_header_block(nghttp3_stream *stream,
|
||||
nghttp3_qpack_encoder *qenc,
|
||||
nghttp3_stream *qenc_stream,
|
||||
nghttp3_buf *rbuf, nghttp3_buf *ebuf,
|
||||
int64_t frame_type, int64_t push_id,
|
||||
const nghttp3_nv *nva, size_t nvlen) {
|
||||
int64_t frame_type, const nghttp3_nv *nva,
|
||||
size_t nvlen) {
|
||||
nghttp3_buf pbuf;
|
||||
int rv;
|
||||
size_t len;
|
||||
nghttp3_buf *chunk;
|
||||
nghttp3_typed_buf tbuf;
|
||||
nghttp3_frame_hd hd;
|
||||
size_t push_idlen = 0;
|
||||
uint8_t raw_pbuf[16];
|
||||
size_t pbuflen, rbuflen, ebuflen;
|
||||
|
||||
@@ -518,14 +463,10 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
|
||||
rbuflen = nghttp3_buf_len(rbuf);
|
||||
ebuflen = nghttp3_buf_len(ebuf);
|
||||
|
||||
if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) {
|
||||
push_idlen = nghttp3_put_varint_len(push_id);
|
||||
}
|
||||
|
||||
hd.type = frame_type;
|
||||
hd.length = (int64_t)(pbuflen + rbuflen + push_idlen);
|
||||
hd.length = (int64_t)(pbuflen + rbuflen);
|
||||
|
||||
len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen;
|
||||
len = nghttp3_frame_write_hd_len(&hd) + pbuflen;
|
||||
|
||||
if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) {
|
||||
len += rbuflen;
|
||||
@@ -541,10 +482,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
|
||||
|
||||
chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
|
||||
|
||||
if (push_idlen) {
|
||||
chunk->last = nghttp3_put_varint(chunk->last, push_id);
|
||||
}
|
||||
|
||||
chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen);
|
||||
nghttp3_buf_init(&pbuf);
|
||||
|
||||
@@ -623,7 +560,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
|
||||
nghttp3_buf *chunk;
|
||||
nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data;
|
||||
nghttp3_conn *conn = stream->conn;
|
||||
size_t datalen;
|
||||
int64_t datalen;
|
||||
uint32_t flags = 0;
|
||||
nghttp3_frame_hd hd;
|
||||
nghttp3_vec vec[8];
|
||||
@@ -647,7 +584,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
datalen = nghttp3_vec_len(vec, (size_t)sveccnt);
|
||||
datalen = nghttp3_vec_len_varint(vec, (size_t)sveccnt);
|
||||
if (datalen == -1) {
|
||||
return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF);
|
||||
|
||||
@@ -677,7 +617,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
|
||||
}
|
||||
|
||||
hd.type = NGHTTP3_FRAME_DATA;
|
||||
hd.length = (int64_t)datalen;
|
||||
hd.length = datalen;
|
||||
|
||||
len = nghttp3_frame_write_hd_len(&hd);
|
||||
|
||||
@@ -762,8 +702,14 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream,
|
||||
int rv;
|
||||
nghttp3_typed_buf *dest;
|
||||
size_t len = nghttp3_ringbuf_len(outq);
|
||||
size_t buflen = nghttp3_buf_len(&tbuf->buf);
|
||||
|
||||
stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf);
|
||||
if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) {
|
||||
return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
stream->tx.offset += buflen;
|
||||
stream->unsent_bytes += buflen;
|
||||
|
||||
if (len) {
|
||||
dest = nghttp3_ringbuf_get(outq, len - 1);
|
||||
@@ -816,7 +762,12 @@ int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) {
|
||||
for (; n < need; n *= 2)
|
||||
;
|
||||
|
||||
p = nghttp3_mem_malloc(stream->mem, n);
|
||||
if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
|
||||
p = (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc,
|
||||
n);
|
||||
} else {
|
||||
p = nghttp3_mem_malloc(stream->mem, n);
|
||||
}
|
||||
if (p == NULL) {
|
||||
return NGHTTP3_ERR_NOMEM;
|
||||
}
|
||||
@@ -863,7 +814,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
|
||||
nghttp3_ringbuf *outq = &stream->outq;
|
||||
size_t len = nghttp3_ringbuf_len(outq);
|
||||
size_t i;
|
||||
size_t offset = stream->outq_offset;
|
||||
uint64_t offset = stream->outq_offset;
|
||||
size_t buflen;
|
||||
nghttp3_vec *vbegin = vec, *vend = vec + veccnt;
|
||||
nghttp3_typed_buf *tbuf;
|
||||
@@ -879,7 +830,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
|
||||
}
|
||||
|
||||
vec->base = tbuf->buf.pos + offset;
|
||||
vec->len = buflen - offset;
|
||||
vec->len = (size_t)(buflen - offset);
|
||||
++vec;
|
||||
++i;
|
||||
break;
|
||||
@@ -903,7 +854,7 @@ int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) {
|
||||
nghttp3_ringbuf *outq = &stream->outq;
|
||||
size_t i;
|
||||
size_t len = nghttp3_ringbuf_len(outq);
|
||||
size_t offset = stream->outq_offset + n;
|
||||
uint64_t offset = stream->outq_offset + n;
|
||||
size_t buflen;
|
||||
nghttp3_typed_buf *tbuf;
|
||||
|
||||
@@ -945,7 +896,7 @@ static int stream_pop_outq_entry(nghttp3_stream *stream,
|
||||
break;
|
||||
case NGHTTP3_BUF_TYPE_ALIEN:
|
||||
break;
|
||||
default:
|
||||
case NGHTTP3_BUF_TYPE_SHARED:
|
||||
assert(nghttp3_ringbuf_len(chunks));
|
||||
|
||||
chunk = nghttp3_ringbuf_get(chunks, 0);
|
||||
@@ -954,9 +905,18 @@ static int stream_pop_outq_entry(nghttp3_stream *stream,
|
||||
assert(chunk->end == tbuf->buf.end);
|
||||
|
||||
if (chunk->last == tbuf->buf.last) {
|
||||
nghttp3_buf_free(chunk, stream->mem);
|
||||
if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
|
||||
nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc,
|
||||
(nghttp3_chunk *)(void *)chunk->begin);
|
||||
} else {
|
||||
nghttp3_buf_free(chunk, stream->mem);
|
||||
}
|
||||
nghttp3_ringbuf_pop_front(chunks);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
};
|
||||
|
||||
nghttp3_ringbuf_pop_front(&stream->outq);
|
||||
@@ -969,7 +929,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
|
||||
uint64_t offset = stream->ack_offset + n;
|
||||
size_t buflen;
|
||||
size_t npopped = 0;
|
||||
size_t nack;
|
||||
uint64_t nack;
|
||||
nghttp3_typed_buf *tbuf;
|
||||
int rv;
|
||||
|
||||
@@ -978,7 +938,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
|
||||
buflen = nghttp3_buf_len(&tbuf->buf);
|
||||
|
||||
if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) {
|
||||
nack = (size_t)nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done;
|
||||
nack = nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done;
|
||||
if (stream->callbacks.acked_data) {
|
||||
rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack,
|
||||
stream->user_data);
|
||||
@@ -1110,16 +1070,16 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
|
||||
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
|
||||
}
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_MSG_END:
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
|
||||
return 0;
|
||||
default:
|
||||
@@ -1141,13 +1101,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
|
||||
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_MSG_END:
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_MSG_END:
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
|
||||
return 0;
|
||||
default:
|
||||
@@ -1165,6 +1125,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
spec. */
|
||||
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
|
||||
}
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_STATE_REQ_END:
|
||||
@@ -1192,10 +1156,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
stream->rx.http.status_code / 100 == 2) {
|
||||
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
|
||||
}
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
|
||||
@@ -1205,6 +1165,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_MSG_END:
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
|
||||
return 0;
|
||||
default:
|
||||
@@ -1226,13 +1190,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
stream->rx.http.status_code / 100 == 2) {
|
||||
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_MSG_END:
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_EVENT_MSG_END:
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
|
||||
return 0;
|
||||
default:
|
||||
@@ -1248,12 +1212,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
|
||||
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
|
||||
}
|
||||
rv = nghttp3_http_on_remote_end_stream(stream);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
|
||||
return 0;
|
||||
case NGHTTP3_HTTP_STATE_RESP_END:
|
||||
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,11 +1236,6 @@ int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) {
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) {
|
||||
return (!nghttp3_stream_uni(stream->node.nid.id) ||
|
||||
stream->type == NGHTTP3_STREAM_TYPE_PUSH);
|
||||
}
|
||||
|
||||
int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; }
|
||||
|
||||
int nghttp3_client_stream_bidi(int64_t stream_id) {
|
||||
|
||||
+89
-100
@@ -37,6 +37,7 @@
|
||||
#include "nghttp3_buf.h"
|
||||
#include "nghttp3_frame.h"
|
||||
#include "nghttp3_qpack.h"
|
||||
#include "nghttp3_objalloc.h"
|
||||
|
||||
#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256
|
||||
|
||||
@@ -60,13 +61,14 @@ typedef enum nghttp3_stream_type {
|
||||
typedef enum nghttp3_ctrl_stream_state {
|
||||
NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE,
|
||||
NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH,
|
||||
NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH,
|
||||
NGHTTP3_CTRL_STREAM_STATE_SETTINGS,
|
||||
NGHTTP3_CTRL_STREAM_STATE_GOAWAY,
|
||||
NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID,
|
||||
NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME,
|
||||
NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID,
|
||||
NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE,
|
||||
NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID,
|
||||
NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE,
|
||||
} nghttp3_ctrl_stream_state;
|
||||
|
||||
typedef enum nghttp3_req_stream_state {
|
||||
@@ -74,23 +76,10 @@ typedef enum nghttp3_req_stream_state {
|
||||
NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH,
|
||||
NGHTTP3_REQ_STREAM_STATE_DATA,
|
||||
NGHTTP3_REQ_STREAM_STATE_HEADERS,
|
||||
NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID,
|
||||
NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE,
|
||||
NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE,
|
||||
NGHTTP3_REQ_STREAM_STATE_IGN_FRAME,
|
||||
NGHTTP3_REQ_STREAM_STATE_IGN_REST,
|
||||
} nghttp3_req_stream_state;
|
||||
|
||||
typedef enum nghttp3_push_stream_state {
|
||||
NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE,
|
||||
NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH,
|
||||
NGHTTP3_PUSH_STREAM_STATE_DATA,
|
||||
NGHTTP3_PUSH_STREAM_STATE_HEADERS,
|
||||
NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME,
|
||||
NGHTTP3_PUSH_STREAM_STATE_PUSH_ID,
|
||||
NGHTTP3_PUSH_STREAM_STATE_IGN_REST,
|
||||
} nghttp3_push_stream_state;
|
||||
|
||||
typedef struct nghttp3_varint_read_state {
|
||||
int64_t acc;
|
||||
size_t left;
|
||||
@@ -104,38 +93,46 @@ typedef struct nghttp3_stream_read_state {
|
||||
} nghttp3_stream_read_state;
|
||||
|
||||
/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGHTTP3_STREAM_FLAG_NONE 0x0000
|
||||
#define NGHTTP3_STREAM_FLAG_NONE 0x0000u
|
||||
/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional
|
||||
stream type is identified. */
|
||||
#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001
|
||||
#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u
|
||||
/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by
|
||||
QUIC flow control. */
|
||||
#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002
|
||||
#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u
|
||||
/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is
|
||||
temporarily unable to provide data. */
|
||||
#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004
|
||||
#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u
|
||||
/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application
|
||||
finished to feed outgoing data. */
|
||||
#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008
|
||||
#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u
|
||||
/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is
|
||||
blocked due to QPACK decoding. */
|
||||
#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010
|
||||
#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u
|
||||
/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent
|
||||
fin. */
|
||||
#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020
|
||||
#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u
|
||||
/* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed.
|
||||
nghttp3_stream object can still alive because it might be blocked
|
||||
by QPACK decoder. */
|
||||
#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040
|
||||
/* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is
|
||||
blocked because the corresponding PUSH_PROMISE has not been
|
||||
received yet. */
|
||||
#define NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED 0x0080
|
||||
#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u
|
||||
/* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write
|
||||
operation to a stream is prohibited. */
|
||||
#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100
|
||||
/* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */
|
||||
#define NGHTTP3_STREAM_FLAG_RESET 0x0200
|
||||
#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u
|
||||
/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is
|
||||
closed abruptly and any incoming and pending stream data is just
|
||||
discarded for a stream. */
|
||||
#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u
|
||||
/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server
|
||||
overrides stream priority. */
|
||||
#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u
|
||||
/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server
|
||||
received PRIORITY_UPDATE frame for this stream. */
|
||||
#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u
|
||||
/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that
|
||||
NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while
|
||||
processing incoming HTTP fields. */
|
||||
#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u
|
||||
|
||||
typedef enum nghttp3_stream_http_state {
|
||||
NGHTTP3_HTTP_STATE_NONE,
|
||||
@@ -164,15 +161,11 @@ typedef enum nghttp3_stream_http_event {
|
||||
NGHTTP3_HTTP_EVENT_DATA_END,
|
||||
NGHTTP3_HTTP_EVENT_HEADERS_BEGIN,
|
||||
NGHTTP3_HTTP_EVENT_HEADERS_END,
|
||||
NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN,
|
||||
NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END,
|
||||
NGHTTP3_HTTP_EVENT_MSG_END,
|
||||
} nghttp3_stream_http_event;
|
||||
|
||||
typedef struct nghttp3_stream nghttp3_stream;
|
||||
|
||||
typedef struct nghttp3_push_promise nghttp3_push_promise;
|
||||
|
||||
/*
|
||||
* nghttp3_stream_acked_data is a callback function which is invoked
|
||||
* when data sent on stream denoted by |stream_id| supplied from
|
||||
@@ -185,7 +178,7 @@ typedef struct nghttp3_push_promise nghttp3_push_promise;
|
||||
* NGHTTP3_ERR_CALLBACK_FAILURE.
|
||||
*/
|
||||
typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream,
|
||||
int64_t stream_id, size_t datalen,
|
||||
int64_t stream_id, uint64_t datalen,
|
||||
void *user_data);
|
||||
|
||||
typedef struct nghttp3_stream_callbacks {
|
||||
@@ -202,65 +195,70 @@ typedef struct nghttp3_http_state {
|
||||
/* recv_content_length is the number of body bytes received so
|
||||
far. */
|
||||
int64_t recv_content_length;
|
||||
uint16_t flags;
|
||||
uint32_t flags;
|
||||
/* pri is a stream priority produced by nghttp3_pri_to_uint8. */
|
||||
uint8_t pri;
|
||||
} nghttp3_http_state;
|
||||
|
||||
struct nghttp3_stream {
|
||||
const nghttp3_mem *mem;
|
||||
nghttp3_map_entry me;
|
||||
/* node is a node in dependency tree. For server initiated
|
||||
unidirectional stream (push), scheduling is done via
|
||||
corresponding nghttp3_push_promise object pointed by pp. */
|
||||
nghttp3_tnode node;
|
||||
nghttp3_pq_entry qpack_blocked_pe;
|
||||
nghttp3_stream_callbacks callbacks;
|
||||
nghttp3_ringbuf frq;
|
||||
nghttp3_ringbuf chunks;
|
||||
nghttp3_ringbuf outq;
|
||||
/* inq stores the stream raw data which cannot be read because
|
||||
stream is blocked by QPACK decoder. */
|
||||
nghttp3_ringbuf inq;
|
||||
nghttp3_qpack_stream_context qpack_sctx;
|
||||
/* conn is a reference to underlying connection. It could be NULL
|
||||
if stream is not a request/push stream. */
|
||||
nghttp3_conn *conn;
|
||||
void *user_data;
|
||||
/* unsent_bytes is the number of bytes in outq not written yet */
|
||||
size_t unsent_bytes;
|
||||
/* outq_idx is an index into outq where next write is made. */
|
||||
size_t outq_idx;
|
||||
/* outq_offset is write offset relative to the element at outq_idx
|
||||
in outq. */
|
||||
size_t outq_offset;
|
||||
/* ack_offset is offset acknowledged by peer relative to the first
|
||||
element in outq. */
|
||||
uint64_t ack_offset;
|
||||
/* ack_done is the number of bytes notified to an application that
|
||||
they are acknowledged inside the first outq element if it is of
|
||||
type NGHTTP3_BUF_TYPE_ALIEN. */
|
||||
size_t ack_done;
|
||||
size_t unscheduled_nwrite;
|
||||
nghttp3_stream_type type;
|
||||
nghttp3_stream_read_state rstate;
|
||||
/* pp is nghttp3_push_promise that this stream fulfills. */
|
||||
nghttp3_push_promise *pp;
|
||||
/* error_code indicates the reason of closure of this stream. */
|
||||
uint64_t error_code;
|
||||
union {
|
||||
struct {
|
||||
const nghttp3_mem *mem;
|
||||
nghttp3_objalloc *out_chunk_objalloc;
|
||||
nghttp3_objalloc *stream_objalloc;
|
||||
nghttp3_tnode node;
|
||||
nghttp3_pq_entry qpack_blocked_pe;
|
||||
nghttp3_stream_callbacks callbacks;
|
||||
nghttp3_ringbuf frq;
|
||||
nghttp3_ringbuf chunks;
|
||||
nghttp3_ringbuf outq;
|
||||
/* inq stores the stream raw data which cannot be read because
|
||||
stream is blocked by QPACK decoder. */
|
||||
nghttp3_ringbuf inq;
|
||||
nghttp3_qpack_stream_context qpack_sctx;
|
||||
/* conn is a reference to underlying connection. It could be NULL
|
||||
if stream is not a request stream. */
|
||||
nghttp3_conn *conn;
|
||||
void *user_data;
|
||||
/* unsent_bytes is the number of bytes in outq not written yet */
|
||||
uint64_t unsent_bytes;
|
||||
/* outq_idx is an index into outq where next write is made. */
|
||||
size_t outq_idx;
|
||||
/* outq_offset is write offset relative to the element at outq_idx
|
||||
in outq. */
|
||||
uint64_t outq_offset;
|
||||
/* ack_offset is offset acknowledged by peer relative to the first
|
||||
element in outq. */
|
||||
uint64_t ack_offset;
|
||||
/* ack_done is the number of bytes notified to an application that
|
||||
they are acknowledged inside the first outq element if it is of
|
||||
type NGHTTP3_BUF_TYPE_ALIEN. */
|
||||
uint64_t ack_done;
|
||||
uint64_t unscheduled_nwrite;
|
||||
nghttp3_stream_type type;
|
||||
nghttp3_stream_read_state rstate;
|
||||
/* error_code indicates the reason of closure of this stream. */
|
||||
uint64_t error_code;
|
||||
|
||||
struct {
|
||||
nghttp3_stream_http_state hstate;
|
||||
} tx;
|
||||
struct {
|
||||
uint64_t offset;
|
||||
nghttp3_stream_http_state hstate;
|
||||
} tx;
|
||||
|
||||
struct {
|
||||
nghttp3_stream_http_state hstate;
|
||||
nghttp3_http_state http;
|
||||
} rx;
|
||||
struct {
|
||||
nghttp3_stream_http_state hstate;
|
||||
nghttp3_http_state http;
|
||||
} rx;
|
||||
|
||||
uint16_t flags;
|
||||
uint16_t flags;
|
||||
};
|
||||
|
||||
nghttp3_opl_entry oplent;
|
||||
};
|
||||
};
|
||||
|
||||
nghttp3_objalloc_def(stream, nghttp3_stream, oplent);
|
||||
|
||||
typedef struct nghttp3_frame_entry {
|
||||
nghttp3_frame fr;
|
||||
union {
|
||||
@@ -275,6 +273,8 @@ typedef struct nghttp3_frame_entry {
|
||||
|
||||
int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
|
||||
uint64_t seq, const nghttp3_stream_callbacks *callbacks,
|
||||
nghttp3_objalloc *out_chunk_objalloc,
|
||||
nghttp3_objalloc *stream_objalloc,
|
||||
const nghttp3_mem *mem);
|
||||
|
||||
void nghttp3_stream_del(nghttp3_stream *stream);
|
||||
@@ -293,8 +293,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_stream_write_stream_type(nghttp3_stream *stream);
|
||||
|
||||
int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream);
|
||||
|
||||
nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
|
||||
nghttp3_vec *vec, size_t veccnt);
|
||||
|
||||
@@ -308,15 +306,12 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream,
|
||||
int nghttp3_stream_write_headers(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
|
||||
int nghttp3_stream_write_push_promise(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
|
||||
int nghttp3_stream_write_header_block(nghttp3_stream *stream,
|
||||
nghttp3_qpack_encoder *qenc,
|
||||
nghttp3_stream *qenc_stream,
|
||||
nghttp3_buf *rbuf, nghttp3_buf *ebuf,
|
||||
int64_t frame_type, int64_t push_id,
|
||||
const nghttp3_nv *nva, size_t nvlen);
|
||||
int64_t frame_type, const nghttp3_nv *nva,
|
||||
size_t nvlen);
|
||||
|
||||
int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
|
||||
nghttp3_frame_entry *frent);
|
||||
@@ -324,11 +319,11 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
|
||||
int nghttp3_stream_write_settings(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
|
||||
int nghttp3_stream_write_cancel_push(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
int nghttp3_stream_write_goaway(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
|
||||
int nghttp3_stream_write_max_push_id(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
|
||||
nghttp3_frame_entry *frent);
|
||||
|
||||
int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need);
|
||||
|
||||
@@ -374,12 +369,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
|
||||
|
||||
int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream);
|
||||
|
||||
/*
|
||||
* nghttp3_stream_bidi_or_push returns nonzero if |stream| is
|
||||
* bidirectional or push stream.
|
||||
*/
|
||||
int nghttp3_stream_bidi_or_push(nghttp3_stream *stream);
|
||||
|
||||
/*
|
||||
* nghttp3_stream_uni returns nonzero if stream identified by
|
||||
* |stream_id| is unidirectional.
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ static uint64_t pq_get_first_cycle(nghttp3_pq *pq) {
|
||||
}
|
||||
|
||||
int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
|
||||
size_t nwrite) {
|
||||
uint64_t nwrite) {
|
||||
uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN;
|
||||
|
||||
if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
|
||||
|
||||
+2
-1
@@ -72,7 +72,8 @@ void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq);
|
||||
* If |tnode| has already been scheduled, it is rescheduled by the
|
||||
* amount of |nwrite|.
|
||||
*/
|
||||
int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, size_t nwrite);
|
||||
int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
|
||||
uint64_t nwrite);
|
||||
|
||||
/*
|
||||
* nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled.
|
||||
|
||||
+19
-2
@@ -26,9 +26,9 @@
|
||||
#include "nghttp3_vec.h"
|
||||
#include "nghttp3_macro.h"
|
||||
|
||||
size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
|
||||
uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
|
||||
size_t i;
|
||||
size_t res = 0;
|
||||
uint64_t res = 0;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
res += vec[i].len;
|
||||
@@ -36,3 +36,20 @@ size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) {
|
||||
uint64_t res = 0;
|
||||
size_t len;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
len = vec[i].len;
|
||||
if (len > NGHTTP3_MAX_VARINT - res) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
res += len;
|
||||
}
|
||||
|
||||
return (int64_t)res;
|
||||
}
|
||||
+6
@@ -32,4 +32,10 @@
|
||||
|
||||
#include <nghttp3/nghttp3.h>
|
||||
|
||||
/*
|
||||
* nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it
|
||||
* returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT.
|
||||
*/
|
||||
int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n);
|
||||
|
||||
#endif /* NGHTTP3_VEC_H */
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM,
|
||||
NGHTTP3_VERSION};
|
||||
|
||||
nghttp3_info *nghttp3_version(int least_version) {
|
||||
const nghttp3_info *nghttp3_version(int least_version) {
|
||||
if (least_version > NGHTTP3_VERSION_NUM) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Vendored
+12
-1
@@ -6,6 +6,9 @@
|
||||
'ngtcp2_sources': [
|
||||
'ngtcp2/lib/ngtcp2_acktr.c',
|
||||
'ngtcp2/lib/ngtcp2_addr.c',
|
||||
'ngtcp2/lib/ngtcp2_balloc.c',
|
||||
'ngtcp2/lib/ngtcp2_bbr.c',
|
||||
'ngtcp2/lib/ngtcp2_bbr2.c',
|
||||
'ngtcp2/lib/ngtcp2_buf.c',
|
||||
'ngtcp2/lib/ngtcp2_cc.c',
|
||||
'ngtcp2/lib/ngtcp2_cid.c',
|
||||
@@ -19,8 +22,11 @@
|
||||
'ngtcp2/lib/ngtcp2_log.c',
|
||||
'ngtcp2/lib/ngtcp2_map.c',
|
||||
'ngtcp2/lib/ngtcp2_mem.c',
|
||||
'ngtcp2/lib/ngtcp2_objalloc.c',
|
||||
'ngtcp2/lib/ngtcp2_opl.c',
|
||||
'ngtcp2/lib/ngtcp2_path.c',
|
||||
'ngtcp2/lib/ngtcp2_pkt.c',
|
||||
'ngtcp2/lib/ngtcp2_pmtud.c',
|
||||
'ngtcp2/lib/ngtcp2_ppe.c',
|
||||
'ngtcp2/lib/ngtcp2_pq.c',
|
||||
'ngtcp2/lib/ngtcp2_pv.c',
|
||||
@@ -34,7 +40,8 @@
|
||||
'ngtcp2/lib/ngtcp2_strm.c',
|
||||
'ngtcp2/lib/ngtcp2_vec.c',
|
||||
'ngtcp2/lib/ngtcp2_version.c',
|
||||
'ngtcp2/crypto/shared.c',
|
||||
'ngtcp2/lib/ngtcp2_window_filter.c',
|
||||
'ngtcp2/crypto/shared.c'
|
||||
],
|
||||
'ngtcp2_sources_openssl': [
|
||||
'ngtcp2/crypto/openssl/openssl.c'
|
||||
@@ -43,6 +50,7 @@
|
||||
'ngtcp2/crypto/boringssl/boringssl.c'
|
||||
],
|
||||
'nghttp3_sources': [
|
||||
'nghttp3/lib/nghttp3_balloc.c',
|
||||
'nghttp3/lib/nghttp3_buf.c',
|
||||
'nghttp3/lib/nghttp3_conn.c',
|
||||
'nghttp3/lib/nghttp3_conv.c',
|
||||
@@ -55,6 +63,8 @@
|
||||
'nghttp3/lib/nghttp3_ksl.c',
|
||||
'nghttp3/lib/nghttp3_map.c',
|
||||
'nghttp3/lib/nghttp3_mem.c',
|
||||
'nghttp3/lib/nghttp3_objalloc.c',
|
||||
'nghttp3/lib/nghttp3_opl.c',
|
||||
'nghttp3/lib/nghttp3_pq.c',
|
||||
'nghttp3/lib/nghttp3_qpack.c',
|
||||
'nghttp3/lib/nghttp3_qpack_huffman.c',
|
||||
@@ -117,6 +127,7 @@
|
||||
'',
|
||||
'ngtcp2/lib/includes',
|
||||
'ngtcp2/crypto/includes',
|
||||
'ngtcp2/crypto',
|
||||
]
|
||||
},
|
||||
'sources': [
|
||||
|
||||
+190
-103
@@ -35,15 +35,15 @@
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hkdf.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/chacha.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
/* Define cipher types because BoringSSL does not implement EVP
|
||||
interface for chacha20. */
|
||||
typedef enum ngtcp2_crypto_boringssl_cipher_type {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR,
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR,
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128,
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256,
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
|
||||
} ngtcp2_crypto_boringssl_cipher_type;
|
||||
|
||||
@@ -51,22 +51,31 @@ typedef struct ngtcp2_crypto_boringssl_cipher {
|
||||
ngtcp2_crypto_boringssl_cipher_type type;
|
||||
} ngtcp2_crypto_boringssl_cipher;
|
||||
|
||||
static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_128_ctr = {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR,
|
||||
static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_128 = {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128,
|
||||
};
|
||||
|
||||
static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_256_ctr = {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR,
|
||||
static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_256 = {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256,
|
||||
};
|
||||
|
||||
static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
|
||||
};
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
|
||||
return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm());
|
||||
}
|
||||
|
||||
ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
|
||||
md->native_handle = (void *)EVP_sha256();
|
||||
return md;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
|
||||
ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aead_aes_128_gcm());
|
||||
ctx->md.native_handle = (void *)EVP_sha256();
|
||||
ctx->hp.native_handle = (void *)&crypto_cipher_evp_aes_128_ctr;
|
||||
ctx->hp.native_handle = (void *)&crypto_cipher_aes_128;
|
||||
ctx->max_encryption = 0;
|
||||
ctx->max_decryption_failure = 0;
|
||||
return ctx;
|
||||
@@ -123,9 +132,9 @@ static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) {
|
||||
static const ngtcp2_crypto_boringssl_cipher *crypto_ssl_get_hp(SSL *ssl) {
|
||||
switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
|
||||
case TLS1_CK_AES_128_GCM_SHA256:
|
||||
return &crypto_cipher_evp_aes_128_ctr;
|
||||
return &crypto_cipher_aes_128;
|
||||
case TLS1_CK_AES_256_GCM_SHA384:
|
||||
return &crypto_cipher_evp_aes_256_ctr;
|
||||
return &crypto_cipher_aes_256;
|
||||
case TLS1_CK_CHACHA20_POLY1305_SHA256:
|
||||
return &crypto_cipher_chacha20;
|
||||
default:
|
||||
@@ -216,19 +225,15 @@ void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum ngtcp2_crypto_boringssl_cipher_ctx_type {
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP,
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20,
|
||||
} ngtcp2_crypto_boringssl_cipher_ctx_type;
|
||||
|
||||
typedef struct ngtcp2_crypto_boringssl_cipher_ctx {
|
||||
ngtcp2_crypto_boringssl_cipher_ctx_type type;
|
||||
ngtcp2_crypto_boringssl_cipher_type type;
|
||||
union {
|
||||
/* ctx is EVP_CIPHER_CTX used when type ==
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP. */
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
/* aes_key is an encryption key when type is either
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128 or
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256. */
|
||||
AES_KEY aes_key;
|
||||
/* key contains an encryption key when type ==
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20. */
|
||||
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20. */
|
||||
uint8_t key[32];
|
||||
};
|
||||
} ngtcp2_crypto_boringssl_cipher_ctx;
|
||||
@@ -238,70 +243,41 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
|
||||
const uint8_t *key) {
|
||||
ngtcp2_crypto_boringssl_cipher *hp_cipher = cipher->native_handle;
|
||||
ngtcp2_crypto_boringssl_cipher_ctx *ctx;
|
||||
EVP_CIPHER_CTX *actx;
|
||||
const EVP_CIPHER *evp_cipher;
|
||||
|
||||
switch (hp_cipher->type) {
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR:
|
||||
evp_cipher = EVP_aes_128_ctr();
|
||||
break;
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR:
|
||||
evp_cipher = EVP_aes_256_ctr();
|
||||
break;
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
|
||||
ctx = malloc(sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20;
|
||||
memcpy(ctx->key, key, sizeof(ctx->key));
|
||||
|
||||
cipher_ctx->native_handle = ctx;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
assert(0);
|
||||
};
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!EVP_EncryptInit_ex(actx, evp_cipher, NULL, key, NULL)) {
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
}
|
||||
int rv;
|
||||
(void)rv;
|
||||
|
||||
ctx = malloc(sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP;
|
||||
ctx->ctx = actx;
|
||||
|
||||
ctx->type = hp_cipher->type;
|
||||
cipher_ctx->native_handle = ctx;
|
||||
|
||||
return 0;
|
||||
switch (hp_cipher->type) {
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128:
|
||||
rv = AES_set_encrypt_key(key, 128, &ctx->aes_key);
|
||||
assert(0 == rv);
|
||||
return 0;
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256:
|
||||
rv = AES_set_encrypt_key(key, 256, &ctx->aes_key);
|
||||
assert(0 == rv);
|
||||
return 0;
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
|
||||
memcpy(ctx->key, key, sizeof(ctx->key));
|
||||
return 0;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
};
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
|
||||
ngtcp2_crypto_boringssl_cipher_ctx *ctx;
|
||||
|
||||
if (!cipher_ctx->native_handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = cipher_ctx->native_handle;
|
||||
|
||||
if (ctx->type == NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP) {
|
||||
EVP_CIPHER_CTX_free(ctx->ctx);
|
||||
}
|
||||
|
||||
free(ctx);
|
||||
free(cipher_ctx->native_handle);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
|
||||
@@ -331,18 +307,32 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *info, size_t infolen) {
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
|
||||
if (HKDF(dest, destlen, prf, secret, secretlen, salt, saltlen, info,
|
||||
infolen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen) {
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
const EVP_AEAD *cipher = aead->native_handle;
|
||||
EVP_AEAD_CTX *actx = aead_ctx->native_handle;
|
||||
size_t max_outlen = plaintextlen + EVP_AEAD_max_overhead(cipher);
|
||||
size_t outlen;
|
||||
|
||||
if (EVP_AEAD_CTX_seal(actx, dest, &outlen, max_outlen, nonce, noncelen,
|
||||
plaintext, plaintextlen, ad, adlen) != 1) {
|
||||
plaintext, plaintextlen, aad, aadlen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -353,15 +343,21 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen) {
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
const EVP_AEAD *cipher = aead->native_handle;
|
||||
EVP_AEAD_CTX *actx = aead_ctx->native_handle;
|
||||
size_t max_outlen = ciphertextlen;
|
||||
size_t max_overhead = EVP_AEAD_max_overhead(cipher);
|
||||
size_t max_outlen;
|
||||
size_t outlen;
|
||||
|
||||
(void)aead;
|
||||
if (ciphertextlen < max_overhead) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_outlen = ciphertextlen - max_overhead;
|
||||
|
||||
if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen,
|
||||
ciphertext, ciphertextlen, ad, adlen) != 1) {
|
||||
ciphertext, ciphertextlen, aad, aadlen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -373,23 +369,16 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
|
||||
const uint8_t *sample) {
|
||||
static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
|
||||
ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle;
|
||||
EVP_CIPHER_CTX *actx;
|
||||
int len;
|
||||
uint32_t counter;
|
||||
|
||||
(void)hp;
|
||||
|
||||
switch (ctx->type) {
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP:
|
||||
actx = ctx->ctx;
|
||||
if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
|
||||
!EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT,
|
||||
sizeof(PLAINTEXT) - 1) ||
|
||||
!EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) {
|
||||
return -1;
|
||||
}
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128:
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256:
|
||||
AES_ecb_encrypt(sample, dest, &ctx->aes_key, 1);
|
||||
return 0;
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20:
|
||||
case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
counter = (uint32_t)sample[0] + (uint32_t)(sample[1] << 8) +
|
||||
(uint32_t)(sample[2] << 16) + (uint32_t)(sample[3] << 24);
|
||||
@@ -434,10 +423,7 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
|
||||
|
||||
SSL_reset_early_data_reject(ssl);
|
||||
|
||||
rv = ngtcp2_conn_early_data_rejected(conn);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
ngtcp2_conn_early_data_rejected(conn);
|
||||
|
||||
goto retry;
|
||||
default:
|
||||
@@ -472,24 +458,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
|
||||
|
||||
int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
|
||||
SSL *ssl = tls;
|
||||
ngtcp2_transport_params_type exttype =
|
||||
ngtcp2_conn_is_server(conn)
|
||||
? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
|
||||
: NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS;
|
||||
const uint8_t *tp;
|
||||
size_t tplen;
|
||||
ngtcp2_transport_params params;
|
||||
int rv;
|
||||
|
||||
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
|
||||
|
||||
rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms);
|
||||
rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return -1;
|
||||
@@ -520,6 +495,7 @@ ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level(
|
||||
return NGTCP2_CRYPTO_LEVEL_APPLICATION;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,5 +512,116 @@ enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
|
||||
return ssl_encryption_early_data;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
|
||||
void *user_data) {
|
||||
(void)conn;
|
||||
(void)user_data;
|
||||
|
||||
if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
|
||||
if (RAND_bytes(data, datalen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_read_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secretlen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
|
||||
(void)cipher;
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
|
||||
secret, secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int set_write_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secretlen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
|
||||
(void)cipher;
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
|
||||
secret, secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t bssl_level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
|
||||
int rv;
|
||||
|
||||
rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int flush_flight(SSL *ssl) {
|
||||
(void)ssl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int send_alert(SSL *ssl, enum ssl_encryption_level_t bssl_level,
|
||||
uint8_t alert) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
(void)bssl_level;
|
||||
|
||||
ngtcp2_conn_set_tls_alert(conn, alert);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SSL_QUIC_METHOD quic_method = {
|
||||
set_read_secret, set_write_secret, add_handshake_data,
|
||||
flush_flight, send_alert,
|
||||
};
|
||||
|
||||
static void crypto_boringssl_configure_context(SSL_CTX *ssl_ctx) {
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx) {
|
||||
crypto_boringssl_configure_context(ssl_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx) {
|
||||
crypto_boringssl_configure_context(ssl_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+328
-123
@@ -31,6 +31,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <ws2tcpip.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
@@ -55,22 +62,13 @@ extern "C" {
|
||||
*/
|
||||
#define NGTCP2_CRYPTO_INITIAL_IVLEN 12
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
|
||||
* encryption and decryption.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_crypto_ctx *
|
||||
ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated
|
||||
* ciphers and message digests from native TLS session
|
||||
* |tls_native_handle|. This is used for encrypting/decrypting
|
||||
* Handshake and Short packets.
|
||||
* Handshake and Short header packets.
|
||||
*
|
||||
* If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
|
||||
* a pointer to SSL object.
|
||||
@@ -92,33 +90,6 @@ NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
|
||||
NGTCP2_EXTERN ngtcp2_crypto_ctx *
|
||||
ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_aead_init` initializes |aead| with the provided
|
||||
* |aead_native_handle| which is an underlying AEAD object.
|
||||
*
|
||||
* If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
|
||||
* a pointer to EVP_CIPHER.
|
||||
*
|
||||
* If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
|
||||
* gnutls_cipher_algorithm_t casted to ``void *``.
|
||||
*
|
||||
* If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
|
||||
* be a pointer to EVP_AEAD.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_crypto_aead *
|
||||
ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_native_handle);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
|
||||
* AEAD_AES_128_GCM for Retry packet integrity protection.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_crypto_aead *
|
||||
ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -191,6 +162,20 @@ NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *info,
|
||||
size_t infolen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_hkdf` performs HKDF operation. The result is
|
||||
* |destlen| bytes long and is stored to the buffer pointed by |dest|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *info, size_t infolen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -235,39 +220,16 @@ typedef enum ngtcp2_crypto_side {
|
||||
NGTCP2_EXTERN size_t
|
||||
ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_derive_packet_protection_key` derives packet
|
||||
* protection key. This function writes packet protection key into
|
||||
* the buffer pointed by |key|. |key| must point to the buffer which
|
||||
* is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This
|
||||
* function writes packet protection IV into |iv|. |iv| must point to
|
||||
* the buffer which is at least
|
||||
* ngtcp2_crypto_packet_protection_ivlen(aead). |key| is
|
||||
* ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is
|
||||
* ngtcp2_crypto_packet_protection_ivlen(aead) bytes long.
|
||||
*
|
||||
* If |hp| is not NULL, this function also derives packet header
|
||||
* protection key and writes the key into the buffer pointed by |hp|.
|
||||
* The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long.
|
||||
* |hp|, if not NULL, must have enough capacity to store the key.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key(
|
||||
uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_encrypt` encrypts |plaintext| of length
|
||||
* |plaintextlen| and writes the ciphertext into the buffer pointed by
|
||||
* |dest|. The length of ciphertext is plaintextlen +
|
||||
* ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have
|
||||
* enough capacity to store the ciphertext. It is allowed to specify
|
||||
* the same value to |dest| and |plaintext|.
|
||||
* :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
|
||||
* bytes long. |dest| must have enough capacity to store the
|
||||
* ciphertext. It is allowed to specify the same value to |dest| and
|
||||
* |plaintext|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
@@ -277,14 +239,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest,
|
||||
const uint8_t *plaintext,
|
||||
size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen);
|
||||
const uint8_t *aad, size_t aadlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_encrypt_cb` is a wrapper function around
|
||||
* `ngtcp2_crypto_encrypt`. It can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.encrypt` field.
|
||||
* :member:`ngtcp2_callbacks.encrypt` field.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
|
||||
@@ -294,7 +256,7 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen);
|
||||
const uint8_t *aad, size_t aadlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
@@ -302,9 +264,10 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
* `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length
|
||||
* |ciphertextlen| and writes the plaintext into the buffer pointed by
|
||||
* |dest|. The length of plaintext is ciphertextlen -
|
||||
* ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have
|
||||
* enough capacity to store the plaintext. It is allowed to specify
|
||||
* the same value to |dest| and |ciphertext|.
|
||||
* :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
|
||||
* bytes long. |dest| must have enough capacity to store the
|
||||
* plaintext. It is allowed to specify the same value to |dest| and
|
||||
* |ciphertext|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
@@ -314,14 +277,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest,
|
||||
const uint8_t *ciphertext,
|
||||
size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen);
|
||||
const uint8_t *aad, size_t aadlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_decrypt_cb` is a wrapper function around
|
||||
* `ngtcp2_crypto_decrypt`. It can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.decrypt` field.
|
||||
* :member:`ngtcp2_callbacks.decrypt` field.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
* :macro:`NGTCP2_ERR_TLS_DECRYPT`.
|
||||
@@ -331,15 +294,19 @@ ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen);
|
||||
const uint8_t *aad, size_t aadlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_hp_mask` generates mask which is used in packet
|
||||
* header encryption. The mask is written to the buffer pointed by
|
||||
* |dest|. The length of mask is 5 bytes. |dest| must have enough
|
||||
* capacity to store the mask.
|
||||
* |dest|. The sample is passed as |sample| which is
|
||||
* :macro:`NGTCP2_HP_SAMPLELEN` bytes long. The length of mask must
|
||||
* be at least :macro:`NGTCP2_HP_MASKLEN`. The library only uses the
|
||||
* first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask. The
|
||||
* buffer pointed by |dest| must have at least
|
||||
* :macro:`NGTCP2_HP_SAMPLELEN` bytes available.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
@@ -353,7 +320,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest,
|
||||
*
|
||||
* `ngtcp2_crypto_hp_mask_cb` is a wrapper function around
|
||||
* `ngtcp2_crypto_hp_mask`. It can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.hp_mask` field.
|
||||
* :member:`ngtcp2_callbacks.hp_mask` field.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
|
||||
@@ -379,16 +346,30 @@ ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
|
||||
* |secretlen| specifies the length of |secret|.
|
||||
*
|
||||
* The length of packet protection key and header protection key is
|
||||
* ngtcp2_crypto_aead(ctx->aead), and the length of packet protection
|
||||
* IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx
|
||||
* can be obtained by `ngtcp2_crypto_ctx_tls`.
|
||||
* `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
|
||||
* and the length of packet protection IV is
|
||||
* `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
|
||||
* <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
|
||||
* `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
|
||||
* |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
|
||||
*
|
||||
* In the first call of this function, it calls
|
||||
* `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message
|
||||
* digest algorithm. After the successful call of this function,
|
||||
* application can use `ngtcp2_conn_get_crypto_ctx` to get the object.
|
||||
* It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag
|
||||
* length.
|
||||
* `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
|
||||
* if |level| ==
|
||||
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
|
||||
* negotiated AEAD and message digest algorithm. After the successful
|
||||
* call of this function, application can use
|
||||
* `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
|
||||
* if |level| ==
|
||||
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
|
||||
* :type:`ngtcp2_crypto_ctx`.
|
||||
*
|
||||
* If |conn| is initialized as client, and |level| is
|
||||
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
|
||||
* function retrieves a remote QUIC transport parameters extension
|
||||
* from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
|
||||
* sets it to |conn| by calling
|
||||
* `ngtcp2_conn_decode_remote_transport_params`.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
@@ -412,20 +393,30 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key(
|
||||
* |secretlen| specifies the length of |secret|.
|
||||
*
|
||||
* The length of packet protection key and header protection key is
|
||||
* ngtcp2_crypto_aead(ctx->aead), and the length of packet protection
|
||||
* IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx
|
||||
* can be obtained by `ngtcp2_crypto_ctx_tls`.
|
||||
* `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
|
||||
* and the length of packet protection IV is
|
||||
* `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
|
||||
* <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
|
||||
* `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
|
||||
* |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
|
||||
*
|
||||
* In the first call of this function, it calls
|
||||
* `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message
|
||||
* digest algorithm. After the successful call of this function,
|
||||
* application can use `ngtcp2_conn_get_crypto_ctx` to get the object.
|
||||
* It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag
|
||||
* length.
|
||||
* `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
|
||||
* if |level| ==
|
||||
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
|
||||
* negotiated AEAD and message digest algorithm. After the successful
|
||||
* call of this function, application can use
|
||||
* `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
|
||||
* if |level| ==
|
||||
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
|
||||
* :type:`ngtcp2_crypto_ctx`.
|
||||
*
|
||||
* If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a
|
||||
* remote QUIC transport parameters extension from |tls| and sets it
|
||||
* to |conn|.
|
||||
* If |conn| is initialized as server, and |level| is
|
||||
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
|
||||
* function retrieves a remote QUIC transport parameters extension
|
||||
* from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
|
||||
* sets it to |conn| by calling
|
||||
* `ngtcp2_conn_decode_remote_transport_params`.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
@@ -461,9 +452,11 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key(
|
||||
* length of |rx_secret| and |tx_secret|.
|
||||
*
|
||||
* The length of packet protection key and header protection key is
|
||||
* ngtcp2_crypto_aead(ctx->aead), and the length of packet protection
|
||||
* IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx
|
||||
* can be obtained by `ngtcp2_conn_get_crypto_ctx`.
|
||||
* `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
|
||||
* and the length of packet protection IV is
|
||||
* `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
|
||||
* <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
|
||||
* `ngtcp2_crypto_ctx_tls`.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
@@ -479,7 +472,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key(
|
||||
*
|
||||
* `ngtcp2_crypto_update_key_cb` is a wrapper function around
|
||||
* `ngtcp2_crypto_update_key`. It can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.update_key` field.
|
||||
* :member:`ngtcp2_callbacks.update_key` field.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
|
||||
@@ -498,8 +491,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb(
|
||||
* encryption keys and sets QUIC transport parameters.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.client_initial` field. It is only
|
||||
* used by client.
|
||||
* :member:`ngtcp2_callbacks.client_initial` field. It is only used
|
||||
* by client.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
|
||||
@@ -514,7 +507,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn,
|
||||
* response to incoming Retry packet.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.recv_retry` field. It is only used
|
||||
* :member:`ngtcp2_callbacks.recv_retry` field. It is only used
|
||||
* by client.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
@@ -532,7 +525,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn,
|
||||
* transport parameters.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.recv_client_initial` field. It is
|
||||
* :member:`ngtcp2_callbacks.recv_client_initial` field. It is
|
||||
* only used by server.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or
|
||||
@@ -549,8 +542,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
|
||||
* length |datalen| in encryption level |crypto_level| and may feed
|
||||
* outgoing CRYPTO data to |conn|. This function can drive handshake.
|
||||
* This function can be also used after handshake completes. It is
|
||||
* allowed to call this function with datalen == 0. In this case, no
|
||||
* additional read operation is done.
|
||||
* allowed to call this function with |datalen| == 0. In this case,
|
||||
* no additional read operation is done.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or a negative error code.
|
||||
* The generic error code is -1 if a specific error code is not
|
||||
@@ -563,34 +556,185 @@ ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
|
||||
ngtcp2_crypto_level crypto_level,
|
||||
const uint8_t *data, size_t datalen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around
|
||||
* `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed
|
||||
* to :member:`ngtcp2_callbacks.recv_crypto_data` field.
|
||||
*
|
||||
* If this function is used, the TLS implementation specific error
|
||||
* codes described in `ngtcp2_crypto_read_write_crypto_data` are
|
||||
* treated as if it returns -1. Do not use this function if an
|
||||
* application wishes to use the TLS implementation specific error
|
||||
* codes.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb(
|
||||
ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset,
|
||||
const uint8_t *data, size_t datalen, void *user_data);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_generate_stateless_reset_token` generates a
|
||||
* stateless reset token using HKDF extraction with |md| using the
|
||||
* given |cid| and static key |secret| as input. The token will be
|
||||
* written to the buffer pointed by |token| and it must have a
|
||||
* capacity of at least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN`
|
||||
* bytes.
|
||||
* stateless reset token using HKDF extraction using the given |cid|
|
||||
* and static key |secret| as input. The token will be written to
|
||||
* the buffer pointed by |token| and it must have a capacity of at
|
||||
* least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` bytes.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
|
||||
uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const ngtcp2_cid *cid);
|
||||
uint8_t *token, const uint8_t *secret, size_t secretlen,
|
||||
const ngtcp2_cid *cid);
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random
|
||||
* data added to a token generated by
|
||||
* `ngtcp2_crypto_generate_retry_token` or
|
||||
* `ngtcp2_crypto_generate_regular_token`.
|
||||
*/
|
||||
#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 32
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for
|
||||
* Retry token generated by `ngtcp2_crypto_generate_retry_token`.
|
||||
*/
|
||||
#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a
|
||||
* token generated by `ngtcp2_crypto_generate_regular_token`.
|
||||
*/
|
||||
#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of
|
||||
* a token generated by `ngtcp2_crypto_generate_retry_token`.
|
||||
*/
|
||||
#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN \
|
||||
(/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN + \
|
||||
sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
|
||||
NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length
|
||||
* of a token generated by `ngtcp2_crypto_generate_regular_token`.
|
||||
*/
|
||||
#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \
|
||||
(/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
|
||||
NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_generate_retry_token` generates a token in the
|
||||
* buffer pointed by |token| that is sent with Retry packet. The
|
||||
* buffer pointed by |token| must have at least
|
||||
* :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long. The
|
||||
* successfully generated token starts with
|
||||
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length
|
||||
* |secretlen| is an initial keying material to generate keys to
|
||||
* encrypt the token. |version| is QUIC version. |remote_addr| of
|
||||
* length |remote_addrlen| is an address of client. |retry_scid| is a
|
||||
* Source Connection ID chosen by server and set in Retry packet.
|
||||
* |odcid| is a Destination Connection ID in Initial packet sent by
|
||||
* client. |ts| is the timestamp when the token is generated.
|
||||
*
|
||||
* This function returns the length of generated token if it succeeds,
|
||||
* or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
|
||||
uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
|
||||
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in
|
||||
* the buffer pointed by |token| of length |tokenlen|. |secret| of
|
||||
* length |secretlen| is an initial keying material to generate keys
|
||||
* to decrypt the token. |version| is QUIC version of the Initial
|
||||
* packet that contains this token. |remote_addr| of length
|
||||
* |remote_addrlen| is an address of client. |dcid| is a Destination
|
||||
* Connection ID in Initial packet sent by client. |timeout| is the
|
||||
* period during which the token is valid. |ts| is the current
|
||||
* timestamp. When validation succeeds, the extracted Destination
|
||||
* Connection ID (which is the Destination Connection ID in Initial
|
||||
* packet sent by client that triggered Retry packet) is stored to the
|
||||
* buffer pointed by |odcid|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token(
|
||||
ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
|
||||
const uint8_t *secret, size_t secretlen, uint32_t version,
|
||||
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_generate_regular_token` generates a token in the
|
||||
* buffer pointed by |token| that is sent with NEW_TOKEN frame. The
|
||||
* buffer pointed by |token| must have at least
|
||||
* :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long. The
|
||||
* successfully generated token starts with
|
||||
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length
|
||||
* |secretlen| is an initial keying material to generate keys to
|
||||
* encrypt the token. |remote_addr| of length |remote_addrlen| is an
|
||||
* address of client. |ts| is the timestamp when the token is
|
||||
* generated.
|
||||
*
|
||||
* This function returns the length of generated token if it succeeds,
|
||||
* or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
|
||||
uint8_t *token, const uint8_t *secret, size_t secretlen,
|
||||
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_verify_regular_token` verifies a regular token
|
||||
* stored in the buffer pointed by |token| of length |tokenlen|.
|
||||
* |secret| of length |secretlen| is an initial keying material to
|
||||
* generate keys to decrypt the token. |remote_addr| of length
|
||||
* |remote_addrlen| is an address of client. |timeout| is the period
|
||||
* during which the token is valid. |ts| is the current timestamp.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
|
||||
const uint8_t *token, size_t tokenlen, const uint8_t *secret,
|
||||
size_t secretlen, const ngtcp2_sockaddr *remote_addr,
|
||||
ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_write_connection_close` writes Initial packet
|
||||
* containing CONNECTION_CLOSE with the given |error_code| to the
|
||||
* buffer pointed by |dest| of length |destlen|. This function is
|
||||
* designed for server to close connection without committing the
|
||||
* state when validating Retry token fails. This function must not be
|
||||
* used by client. The |dcid| must be the Source Connection ID in
|
||||
* Initial packet from client. The |scid| must be the Destination
|
||||
* Connection ID in Initial packet from client. |scid| is used to
|
||||
* derive initial keying materials.
|
||||
* containing CONNECTION_CLOSE with the given |error_code| and the
|
||||
* optional |reason| of length |reasonlen| to the buffer pointed by
|
||||
* |dest| of length |destlen|. This function is designed for server
|
||||
* to close connection without committing the state when validating
|
||||
* Retry token fails. This function must not be used by client. The
|
||||
* |dcid| must be the Source Connection ID in Initial packet from
|
||||
* client. The |scid| must be the Destination Connection ID in
|
||||
* Initial packet from client. |scid| is used to derive initial
|
||||
* keying materials.
|
||||
*
|
||||
* This function wraps around `ngtcp2_pkt_write_connection_close` for
|
||||
* easier use.
|
||||
@@ -599,7 +743,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close(
|
||||
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
|
||||
const ngtcp2_cid *scid, uint64_t error_code);
|
||||
const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
|
||||
size_t reasonlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
@@ -664,7 +809,7 @@ ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx);
|
||||
* `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.delete_crypto_aead_ctx` field.
|
||||
* :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field.
|
||||
*/
|
||||
NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
|
||||
ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data);
|
||||
@@ -676,11 +821,71 @@ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
|
||||
* |cipher_ctx|.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_conn_callbacks.delete_crypto_cipher_ctx` field.
|
||||
* :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field.
|
||||
*/
|
||||
NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
|
||||
ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable
|
||||
* sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data|
|
||||
* which is sent with PATH_CHALLENGE frame.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_callbacks.get_path_challenge_data` field.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn,
|
||||
uint8_t *data,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for
|
||||
* |version| which is negotiated or being negotiated. |client_dcid|
|
||||
* is the destination connection ID in first Initial packet of client.
|
||||
*
|
||||
* This function can be directly passed to
|
||||
* :member:`ngtcp2_callbacks.version_negotiation` field.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
|
||||
const ngtcp2_cid *client_dcid,
|
||||
void *user_data);
|
||||
|
||||
typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref;
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_crypto_get_conn` is a callback function to get a
|
||||
* pointer to :type:`ngtcp2_conn` from |conn_ref|. The implementation
|
||||
* must return non-NULL :type:`ngtcp2_conn` object.
|
||||
*/
|
||||
typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)(
|
||||
ngtcp2_crypto_conn_ref *conn_ref);
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to
|
||||
* :type:`ngtcp2_conn`. It is meant to be set to TLS native handle as
|
||||
* an application specific data (e.g. SSL_set_app_data in OpenSSL).
|
||||
*/
|
||||
typedef struct ngtcp2_crypto_conn_ref {
|
||||
/**
|
||||
* :member:`get_conn` is a callback function to get a pointer to
|
||||
* :type:`ngtcp2_conn` object.
|
||||
*/
|
||||
ngtcp2_crypto_get_conn get_conn;
|
||||
/**
|
||||
* :member:`user_data` is a pointer to arbitrary user data.
|
||||
*/
|
||||
void *user_data;
|
||||
} ngtcp2_crypto_conn_ref;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -55,6 +55,48 @@ NGTCP2_EXTERN enum ssl_encryption_level_t
|
||||
ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
|
||||
ngtcp2_crypto_level crypto_level);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_boringssl_configure_server_context` configures
|
||||
* |ssl_ctx| for server side QUIC connection. It performs the
|
||||
* following modifications:
|
||||
*
|
||||
* - Set minimum and maximum TLS version to TLSv1.3.
|
||||
* - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* SSL object by calling SSL_set_app_data, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_boringssl_configure_client_context` configures
|
||||
* |ssl_ctx| for client side QUIC connection. It performs the
|
||||
* following modifications:
|
||||
*
|
||||
* - Set minimum and maximum TLS version to TLSv1.3.
|
||||
* - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* SSL object by calling SSL_set_app_data, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -83,6 +83,48 @@ NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL
|
||||
ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
|
||||
ngtcp2_crypto_level crypto_level);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_openssl_configure_server_context` configures
|
||||
* |ssl_ctx| for server side QUIC connection. It performs the
|
||||
* following modifications:
|
||||
*
|
||||
* - Set minimum and maximum TLS version to TLSv1.3.
|
||||
* - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* SSL object by calling SSL_set_app_data, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_openssl_configure_client_context` configures
|
||||
* |ssl_ctx| for client side QUIC connection. It performs the
|
||||
* following modifications:
|
||||
*
|
||||
* - Set minimum and maximum TLS version to TLSv1.3.
|
||||
* - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* SSL object by calling SSL_set_app_data, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_CRYPTO_PICOTLS_H
|
||||
#define NGTCP2_CRYPTO_PICOTLS_H
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include <picotls.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state
|
||||
* of Picotls objects and must be an object to bet set to
|
||||
* `ngtcp2_conn_set_tls_native_handle`.
|
||||
*/
|
||||
typedef struct ngtcp2_crypto_picotls_ctx {
|
||||
/**
|
||||
* :member:`ptls` is a pointer to ptls_t object.
|
||||
*/
|
||||
ptls_t *ptls;
|
||||
/**
|
||||
* :member:`handshake_properties` is a set of configurations used
|
||||
* during this particular TLS handshake.
|
||||
*/
|
||||
ptls_handshake_properties_t handshake_properties;
|
||||
} ngtcp2_crypto_picotls_ctx;
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_ctx_init` initializes the object pointed by
|
||||
* |cptls|. |cptls| must not be NULL.
|
||||
*/
|
||||
NGTCP2_EXTERN void
|
||||
ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_from_epoch` translates |epoch| to
|
||||
* :type:`ngtcp2_crypto_level`. This function is only available for
|
||||
* Picotls backend.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_crypto_level
|
||||
ngtcp2_crypto_picotls_from_epoch(size_t epoch);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_from_ngtcp2_crypto_level` translates
|
||||
* |crypto_level| to epoch. This function is only available for
|
||||
* Picotls backend.
|
||||
*/
|
||||
NGTCP2_EXTERN size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(
|
||||
ngtcp2_crypto_level crypto_level);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_configure_server_context` configures |ctx|
|
||||
* for server side QUIC connection. It performs the following
|
||||
* modifications:
|
||||
*
|
||||
* - Set max_early_data_size to UINT32_MAX.
|
||||
* - Set omit_end_of_early_data to 1.
|
||||
* - Set update_traffic_key callback.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* ptls_t object by assigning the pointer using ptls_get_data_ptr, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_configure_client_context` configures |ctx|
|
||||
* for client side QUIC connection. It performs the following
|
||||
* modifications:
|
||||
*
|
||||
* - Set omit_end_of_early_data to 1.
|
||||
* - Set update_traffic_key callback.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* ptls_t object by assigning the pointer using ptls_get_data_ptr, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_configure_server_session` configures |cptls|
|
||||
* for server side QUIC connection. It performs the following
|
||||
* modifications:
|
||||
*
|
||||
* - Set handshake_properties.collect_extension to
|
||||
* `ngtcp2_crypto_picotls_collect_extension`.
|
||||
* - Set handshake_properties.collected_extensions to
|
||||
* `ngtcp2_crypto_picotls_collected_extensions`.
|
||||
*
|
||||
* The callbacks set by this function only handle QUIC Transport
|
||||
* Parameters TLS extension. If an application needs to handle the
|
||||
* other TLS extensions, set its own callbacks and call
|
||||
* `ngtcp2_crypto_picotls_collect_extension` and
|
||||
* `ngtcp2_crypto_picotls_collected_extensions` form them.
|
||||
*
|
||||
* During the QUIC handshake, the first element of
|
||||
* handshake_properties.additional_extensions is assigned to send QUIC
|
||||
* Transport Parameter TLS extension. Therefore, an application must
|
||||
* allocate at least 2 elements for
|
||||
* handshake_properties.additional_extensions.
|
||||
*
|
||||
* Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the
|
||||
* resources.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* ptls_t object by assigning the pointer using ptls_get_data_ptr, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_picotls_configure_server_session(
|
||||
ngtcp2_crypto_picotls_ctx *cptls);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_configure_client_session` configures |cptls|
|
||||
* for client side QUIC connection. It performs the following
|
||||
* modifications:
|
||||
*
|
||||
* - Set handshake_properties.max_early_data_size to a pointer to
|
||||
* uint32_t, which is allocated dynamically by this function.
|
||||
* - Set handshake_properties.collect_extension to
|
||||
* `ngtcp2_crypto_picotls_collect_extension`.
|
||||
* - Set handshake_properties.collected_extensions to
|
||||
* `ngtcp2_crypto_picotls_collected_extensions`.
|
||||
* - Set handshake_properties.additional_extensions[0].data to the
|
||||
* dynamically allocated buffer which contains QUIC Transport
|
||||
* Parameters TLS extension. An application must allocate at least
|
||||
* 2 elements for handshake_properties.additional_extensions.
|
||||
*
|
||||
* The callbacks set by this function only handle QUIC Transport
|
||||
* Parameters TLS extension. If an application needs to handle the
|
||||
* other TLS extensions, set its own callbacks and call
|
||||
* `ngtcp2_crypto_picotls_collect_extension` and
|
||||
* `ngtcp2_crypto_picotls_collected_extensions` form them.
|
||||
*
|
||||
* Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the
|
||||
* resources.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* ptls_t object by assigning the pointer using ptls_get_data_ptr, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_picotls_configure_client_session(ngtcp2_crypto_picotls_ctx *cptls,
|
||||
ngtcp2_conn *conn);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_deconfigure_session` frees the resources
|
||||
* allocated for |cptls| during QUIC connection. It frees the
|
||||
* following data using :manpage:`free(3)`.
|
||||
*
|
||||
* - handshake_properties.max_early_data_size
|
||||
* - handshake_properties.additional_extensions[0].data.base
|
||||
*
|
||||
* If |cptls| is NULL, this function does nothing.
|
||||
*/
|
||||
NGTCP2_EXTERN void
|
||||
ngtcp2_crypto_picotls_deconfigure_session(ngtcp2_crypto_picotls_ctx *cptls);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_collect_extension` is a callback function
|
||||
* which only returns nonzero if |type| ==
|
||||
* :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_picotls_collect_extension(
|
||||
ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
|
||||
uint16_t type);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_picotls_collected_extensions` is a callback function
|
||||
* which only handles the extension of type
|
||||
* :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. The other
|
||||
* extensions are ignored.
|
||||
*/
|
||||
NGTCP2_EXTERN int ngtcp2_crypto_picotls_collected_extensions(
|
||||
ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
|
||||
ptls_raw_extension_t *extensions);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NGTCP2_CRYPTO_PICOTLS_H */
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_CRYPTO_WOLFSSL_H
|
||||
#define NGTCP2_CRYPTO_WOLFSSL_H
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
#include <wolfssl/quic.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level` translates
|
||||
* |wolfssl_level| to :type:`ngtcp2_crypto_level`. This function is only
|
||||
* available for wolfSSL backend.
|
||||
*/
|
||||
NGTCP2_EXTERN ngtcp2_crypto_level
|
||||
ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(
|
||||
WOLFSSL_ENCRYPTION_LEVEL wolfssl_level);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level` translates
|
||||
* |crypto_level| to WOLFSSL_ENCRYPTION_LEVEL. This function is only
|
||||
* available for wolfSSL backend.
|
||||
*/
|
||||
NGTCP2_EXTERN WOLFSSL_ENCRYPTION_LEVEL
|
||||
ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(
|
||||
ngtcp2_crypto_level crypto_level);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_wolfssl_configure_server_context` configures
|
||||
* |ssl_ctx| for server side QUIC connection. It performs the
|
||||
* following modifications:
|
||||
*
|
||||
* - Set minimum and maximum TLS version to TLSv1.3.
|
||||
* - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* WOLFSSL object by calling wolfSSL_set_app_data, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_wolfssl_configure_client_context` configures
|
||||
* |ssl_ctx| for client side QUIC connection. It performs the
|
||||
* following modifications:
|
||||
*
|
||||
* - Set minimum and maximum TLS version to TLSv1.3.
|
||||
* - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method.
|
||||
*
|
||||
* Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
|
||||
* SSL object by calling wolfSSL_set_app_data, and
|
||||
* :type:`ngtcp2_crypto_conn_ref` object must have
|
||||
* :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
|
||||
* :type:`ngtcp2_conn`.
|
||||
*
|
||||
* It returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
NGTCP2_EXTERN int
|
||||
ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NGTCP2_CRYPTO_WOLFSSL_H */
|
||||
+303
-22
@@ -34,6 +34,11 @@
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
# include <openssl/core_names.h>
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
@@ -48,9 +53,19 @@ static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) {
|
||||
return EVP_CCM_TLS_TAG_LEN;
|
||||
default:
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
|
||||
return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm());
|
||||
}
|
||||
|
||||
ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
|
||||
md->native_handle = (void *)EVP_sha256();
|
||||
return md;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
|
||||
ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aes_128_gcm());
|
||||
ctx->md.native_handle = (void *)EVP_sha256();
|
||||
@@ -187,18 +202,37 @@ int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const EVP_CIPHER *cipher = aead->native_handle;
|
||||
int cipher_nid = EVP_CIPHER_nid(cipher);
|
||||
EVP_CIPHER_CTX *actx;
|
||||
size_t taglen = crypto_aead_max_overhead(cipher);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
OSSL_PARAM params[3];
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen);
|
||||
|
||||
if (cipher_nid == NID_aes_128_ccm) {
|
||||
params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
|
||||
NULL, taglen);
|
||||
params[2] = OSSL_PARAM_construct_end();
|
||||
} else {
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
}
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
!EVP_CIPHER_CTX_set_params(actx, params) ||
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen,
|
||||
NULL) ||
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG,
|
||||
(int)crypto_aead_max_overhead(cipher), NULL)) ||
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) ||
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
!EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) {
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
@@ -215,18 +249,37 @@ int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const EVP_CIPHER *cipher = aead->native_handle;
|
||||
int cipher_nid = EVP_CIPHER_nid(cipher);
|
||||
EVP_CIPHER_CTX *actx;
|
||||
size_t taglen = crypto_aead_max_overhead(cipher);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
OSSL_PARAM params[3];
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen);
|
||||
|
||||
if (cipher_nid == NID_aes_128_ccm) {
|
||||
params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
|
||||
NULL, taglen);
|
||||
params[2] = OSSL_PARAM_construct_end();
|
||||
} else {
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
}
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
!EVP_CIPHER_CTX_set_params(actx, params) ||
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen,
|
||||
NULL) ||
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG,
|
||||
(int)crypto_aead_max_overhead(cipher), NULL)) ||
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) ||
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
!EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) {
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
@@ -272,6 +325,33 @@ void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
|
||||
int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
|
||||
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
|
||||
int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode),
|
||||
OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
|
||||
(char *)EVP_MD_get0_name(prf), 0),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
|
||||
secretlen),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt,
|
||||
saltlen),
|
||||
OSSL_PARAM_construct_end(),
|
||||
};
|
||||
int rv = 0;
|
||||
|
||||
EVP_KDF_free(kdf);
|
||||
|
||||
if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) {
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kctx);
|
||||
|
||||
return rv;
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
int rv = 0;
|
||||
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
@@ -293,12 +373,40 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return rv;
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *info,
|
||||
size_t infolen) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
|
||||
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
|
||||
int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode),
|
||||
OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
|
||||
(char *)EVP_MD_get0_name(prf), 0),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
|
||||
secretlen),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info,
|
||||
infolen),
|
||||
OSSL_PARAM_construct_end(),
|
||||
};
|
||||
int rv = 0;
|
||||
|
||||
EVP_KDF_free(kdf);
|
||||
|
||||
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kctx);
|
||||
|
||||
return rv;
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
int rv = 0;
|
||||
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
@@ -309,7 +417,7 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
if (EVP_PKEY_derive_init(pctx) != 1 ||
|
||||
EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 ||
|
||||
EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)"", 0) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
|
||||
EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 ||
|
||||
EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
|
||||
@@ -319,29 +427,97 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return rv;
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *info, size_t infolen) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
|
||||
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
|
||||
(char *)EVP_MD_get0_name(prf), 0),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
|
||||
secretlen),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt,
|
||||
saltlen),
|
||||
OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info,
|
||||
infolen),
|
||||
OSSL_PARAM_construct_end(),
|
||||
};
|
||||
int rv = 0;
|
||||
|
||||
EVP_KDF_free(kdf);
|
||||
|
||||
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kctx);
|
||||
|
||||
return rv;
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
const EVP_MD *prf = md->native_handle;
|
||||
int rv = 0;
|
||||
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
if (pctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive_init(pctx) != 1 ||
|
||||
EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) !=
|
||||
1 ||
|
||||
EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
|
||||
EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 ||
|
||||
EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return rv;
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen) {
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
const EVP_CIPHER *cipher = aead->native_handle;
|
||||
size_t taglen = crypto_aead_max_overhead(cipher);
|
||||
int cipher_nid = EVP_CIPHER_nid(cipher);
|
||||
EVP_CIPHER_CTX *actx = aead_ctx->native_handle;
|
||||
int len;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
|
||||
dest + plaintextlen, taglen),
|
||||
OSSL_PARAM_construct_end(),
|
||||
};
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
(void)noncelen;
|
||||
|
||||
if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) ||
|
||||
!EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) ||
|
||||
!EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) ||
|
||||
!EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) ||
|
||||
!EVP_EncryptFinal_ex(actx, dest + len, &len) ||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
!EVP_CIPHER_CTX_get_params(actx, params)
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen,
|
||||
dest + plaintextlen)) {
|
||||
dest + plaintextlen)
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -352,13 +528,16 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen) {
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
const EVP_CIPHER *cipher = aead->native_handle;
|
||||
size_t taglen = crypto_aead_max_overhead(cipher);
|
||||
int cipher_nid = EVP_CIPHER_nid(cipher);
|
||||
EVP_CIPHER_CTX *actx = aead_ctx->native_handle;
|
||||
int len;
|
||||
const uint8_t *tag;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
OSSL_PARAM params[2];
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
(void)noncelen;
|
||||
|
||||
@@ -369,12 +548,22 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
ciphertextlen -= taglen;
|
||||
tag = ciphertext + ciphertextlen;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
|
||||
(void *)tag, taglen);
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
|
||||
|
||||
if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
!EVP_CIPHER_CTX_set_params(actx, params) ||
|
||||
#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen,
|
||||
(uint8_t *)tag) ||
|
||||
#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) ||
|
||||
!EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) ||
|
||||
!EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) ||
|
||||
!EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) ||
|
||||
(cipher_nid != NID_aes_128_ccm &&
|
||||
!EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) {
|
||||
@@ -457,24 +646,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
|
||||
|
||||
int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
|
||||
SSL *ssl = tls;
|
||||
ngtcp2_transport_params_type exttype =
|
||||
ngtcp2_conn_is_server(conn)
|
||||
? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
|
||||
: NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS;
|
||||
const uint8_t *tp;
|
||||
size_t tplen;
|
||||
ngtcp2_transport_params params;
|
||||
int rv;
|
||||
|
||||
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
|
||||
|
||||
rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms);
|
||||
rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return -1;
|
||||
@@ -505,6 +683,7 @@ ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level(
|
||||
return NGTCP2_CRYPTO_LEVEL_APPLICATION;
|
||||
default:
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,5 +701,107 @@ ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
|
||||
return ssl_encryption_early_data;
|
||||
default:
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
|
||||
void *user_data) {
|
||||
(void)conn;
|
||||
(void)user_data;
|
||||
|
||||
if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
|
||||
if (RAND_bytes(data, (int)datalen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
|
||||
const uint8_t *rx_secret,
|
||||
const uint8_t *tx_secret, size_t secretlen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
|
||||
|
||||
if (rx_secret &&
|
||||
ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
|
||||
rx_secret, secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tx_secret &&
|
||||
ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
|
||||
tx_secret, secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
|
||||
int rv;
|
||||
|
||||
rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int flush_flight(SSL *ssl) {
|
||||
(void)ssl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int send_alert(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
uint8_t alert) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
(void)level;
|
||||
|
||||
ngtcp2_conn_set_tls_alert(conn, alert);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SSL_QUIC_METHOD quic_method = {
|
||||
set_encryption_secrets,
|
||||
add_handshake_data,
|
||||
flush_flight,
|
||||
send_alert,
|
||||
};
|
||||
|
||||
static void crypto_openssl_configure_context(SSL_CTX *ssl_ctx) {
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx) {
|
||||
crypto_openssl_configure_context(ssl_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx) {
|
||||
crypto_openssl_configure_context(ssl_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+697
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ngtcp2/ngtcp2_crypto.h>
|
||||
#include <ngtcp2/ngtcp2_crypto_picotls.h>
|
||||
|
||||
#include <picotls.h>
|
||||
#include <picotls/openssl.h>
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
|
||||
return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm);
|
||||
}
|
||||
|
||||
ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
|
||||
md->native_handle = (void *)&ptls_openssl_sha256;
|
||||
return md;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
|
||||
ngtcp2_crypto_aead_init(&ctx->aead, (void *)&ptls_openssl_aes128gcm);
|
||||
ctx->md.native_handle = (void *)&ptls_openssl_sha256;
|
||||
ctx->hp.native_handle = (void *)&ptls_openssl_aes128ctr;
|
||||
ctx->max_encryption = 0;
|
||||
ctx->max_decryption_failure = 0;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
|
||||
void *aead_native_handle) {
|
||||
ptls_aead_algorithm_t *alg = aead_native_handle;
|
||||
|
||||
aead->native_handle = aead_native_handle;
|
||||
aead->max_overhead = alg->tag_size;
|
||||
return aead;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
|
||||
return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm);
|
||||
}
|
||||
|
||||
static const ptls_aead_algorithm_t *crypto_ptls_get_aead(ptls_t *ptls) {
|
||||
ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
|
||||
|
||||
return cs->aead;
|
||||
}
|
||||
|
||||
static uint64_t crypto_ptls_get_aead_max_encryption(ptls_t *ptls) {
|
||||
ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
|
||||
|
||||
if (cs->aead == &ptls_openssl_aes128gcm ||
|
||||
cs->aead == &ptls_openssl_aes256gcm) {
|
||||
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
|
||||
}
|
||||
|
||||
if (cs->aead == &ptls_openssl_chacha20poly1305) {
|
||||
return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t crypto_ptls_get_aead_max_decryption_failure(ptls_t *ptls) {
|
||||
ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
|
||||
|
||||
if (cs->aead == &ptls_openssl_aes128gcm ||
|
||||
cs->aead == &ptls_openssl_aes256gcm) {
|
||||
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
|
||||
}
|
||||
|
||||
if (cs->aead == &ptls_openssl_chacha20poly1305) {
|
||||
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const ptls_cipher_algorithm_t *crypto_ptls_get_hp(ptls_t *ptls) {
|
||||
ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
|
||||
|
||||
if (cs->aead == &ptls_openssl_aes128gcm) {
|
||||
return &ptls_openssl_aes128ctr;
|
||||
}
|
||||
|
||||
if (cs->aead == &ptls_openssl_aes256gcm) {
|
||||
return &ptls_openssl_aes256ctr;
|
||||
}
|
||||
|
||||
if (cs->aead == &ptls_openssl_chacha20poly1305) {
|
||||
return &ptls_openssl_chacha20;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const ptls_hash_algorithm_t *crypto_ptls_get_md(ptls_t *ptls) {
|
||||
ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
|
||||
|
||||
return cs->hash;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
|
||||
void *tls_native_handle) {
|
||||
ngtcp2_crypto_picotls_ctx *cptls = tls_native_handle;
|
||||
ngtcp2_crypto_aead_init(&ctx->aead,
|
||||
(void *)crypto_ptls_get_aead(cptls->ptls));
|
||||
ctx->md.native_handle = (void *)crypto_ptls_get_md(cptls->ptls);
|
||||
ctx->hp.native_handle = (void *)crypto_ptls_get_hp(cptls->ptls);
|
||||
ctx->max_encryption = crypto_ptls_get_aead_max_encryption(cptls->ptls);
|
||||
ctx->max_decryption_failure =
|
||||
crypto_ptls_get_aead_max_decryption_failure(cptls->ptls);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
|
||||
void *tls_native_handle) {
|
||||
return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
|
||||
}
|
||||
|
||||
static size_t crypto_md_hashlen(const ptls_hash_algorithm_t *md) {
|
||||
return md->digest_size;
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
|
||||
return crypto_md_hashlen(md->native_handle);
|
||||
}
|
||||
|
||||
static size_t crypto_aead_keylen(const ptls_aead_algorithm_t *aead) {
|
||||
return aead->key_size;
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
|
||||
return crypto_aead_keylen(aead->native_handle);
|
||||
}
|
||||
|
||||
static size_t crypto_aead_noncelen(const ptls_aead_algorithm_t *aead) {
|
||||
return aead->iv_size;
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
|
||||
return crypto_aead_noncelen(aead->native_handle);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const ngtcp2_crypto_aead *aead,
|
||||
const uint8_t *key, size_t noncelen) {
|
||||
const ptls_aead_algorithm_t *cipher = aead->native_handle;
|
||||
size_t keylen = crypto_aead_keylen(cipher);
|
||||
ptls_aead_context_t *actx;
|
||||
static const uint8_t iv[PTLS_MAX_IV_SIZE] = {0};
|
||||
|
||||
(void)noncelen;
|
||||
(void)keylen;
|
||||
|
||||
actx = ptls_aead_new_direct(cipher, /* is_enc = */ 1, key, iv);
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aead_ctx->native_handle = actx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const ngtcp2_crypto_aead *aead,
|
||||
const uint8_t *key, size_t noncelen) {
|
||||
const ptls_aead_algorithm_t *cipher = aead->native_handle;
|
||||
size_t keylen = crypto_aead_keylen(cipher);
|
||||
ptls_aead_context_t *actx;
|
||||
const uint8_t iv[PTLS_MAX_IV_SIZE] = {0};
|
||||
|
||||
(void)noncelen;
|
||||
(void)keylen;
|
||||
|
||||
actx = ptls_aead_new_direct(cipher, /* is_enc = */ 0, key, iv);
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aead_ctx->native_handle = actx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
|
||||
if (aead_ctx->native_handle) {
|
||||
ptls_aead_free(aead_ctx->native_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
|
||||
const ngtcp2_crypto_cipher *cipher,
|
||||
const uint8_t *key) {
|
||||
ptls_cipher_context_t *actx;
|
||||
|
||||
actx = ptls_cipher_new(cipher->native_handle, /* is_enc = */ 1, key);
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cipher_ctx->native_handle = actx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
|
||||
if (cipher_ctx->native_handle) {
|
||||
ptls_cipher_free(cipher_ctx->native_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen) {
|
||||
ptls_iovec_t saltv, ikm;
|
||||
|
||||
saltv = ptls_iovec_init(salt, saltlen);
|
||||
ikm = ptls_iovec_init(secret, secretlen);
|
||||
|
||||
if (ptls_hkdf_extract(md->native_handle, dest, saltv, ikm) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *info,
|
||||
size_t infolen) {
|
||||
ptls_iovec_t prk, infov;
|
||||
|
||||
prk = ptls_iovec_init(secret, secretlen);
|
||||
infov = ptls_iovec_init(info, infolen);
|
||||
|
||||
if (ptls_hkdf_expand(md->native_handle, dest, destlen, prk, infov) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *info, size_t infolen) {
|
||||
ptls_iovec_t saltv, ikm, prk, infov;
|
||||
uint8_t prkbuf[PTLS_MAX_DIGEST_SIZE];
|
||||
ptls_hash_algorithm_t *algo = md->native_handle;
|
||||
|
||||
saltv = ptls_iovec_init(salt, saltlen);
|
||||
ikm = ptls_iovec_init(secret, secretlen);
|
||||
|
||||
if (ptls_hkdf_extract(algo, prkbuf, saltv, ikm) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
prk = ptls_iovec_init(prkbuf, algo->digest_size);
|
||||
infov = ptls_iovec_init(info, infolen);
|
||||
|
||||
if (ptls_hkdf_expand(algo, dest, destlen, prk, infov) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
ptls_aead_context_t *actx = aead_ctx->native_handle;
|
||||
|
||||
(void)aead;
|
||||
|
||||
ptls_aead_xor_iv(actx, nonce, noncelen);
|
||||
|
||||
ptls_aead_encrypt(actx, dest, plaintext, plaintextlen, 0, aad, aadlen);
|
||||
|
||||
/* zero-out static iv once again */
|
||||
ptls_aead_xor_iv(actx, nonce, noncelen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
ptls_aead_context_t *actx = aead_ctx->native_handle;
|
||||
|
||||
(void)aead;
|
||||
|
||||
ptls_aead_xor_iv(actx, nonce, noncelen);
|
||||
|
||||
if (ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad,
|
||||
aadlen) == SIZE_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* zero-out static iv once again */
|
||||
ptls_aead_xor_iv(actx, nonce, noncelen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
|
||||
const ngtcp2_crypto_cipher_ctx *hp_ctx,
|
||||
const uint8_t *sample) {
|
||||
ptls_cipher_context_t *actx = hp_ctx->native_handle;
|
||||
static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
|
||||
|
||||
(void)hp;
|
||||
|
||||
ptls_cipher_init(actx, sample);
|
||||
ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT) - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
|
||||
ngtcp2_crypto_level crypto_level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
ngtcp2_crypto_picotls_ctx *cptls = ngtcp2_conn_get_tls_native_handle(conn);
|
||||
ptls_buffer_t sendbuf;
|
||||
size_t epoch_offsets[5] = {0};
|
||||
size_t epoch = ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(crypto_level);
|
||||
size_t epoch_datalen;
|
||||
size_t i;
|
||||
int rv;
|
||||
|
||||
ptls_buffer_init(&sendbuf, (void *)"", 0);
|
||||
|
||||
assert(epoch == ptls_get_read_epoch(cptls->ptls));
|
||||
|
||||
rv = ptls_handle_message(cptls->ptls, &sendbuf, epoch_offsets, epoch, data,
|
||||
datalen, &cptls->handshake_properties);
|
||||
if (rv != 0 && rv != PTLS_ERROR_IN_PROGRESS) {
|
||||
if (PTLS_ERROR_GET_CLASS(rv) == PTLS_ERROR_CLASS_SELF_ALERT) {
|
||||
ngtcp2_conn_set_tls_alert(conn, (uint8_t)PTLS_ERROR_TO_ALERT(rv));
|
||||
}
|
||||
|
||||
rv = -1;
|
||||
goto fin;
|
||||
}
|
||||
|
||||
if (!ngtcp2_conn_is_server(conn) &&
|
||||
cptls->handshake_properties.client.early_data_acceptance ==
|
||||
PTLS_EARLY_DATA_REJECTED) {
|
||||
ngtcp2_conn_early_data_rejected(conn);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
epoch_datalen = epoch_offsets[i + 1] - epoch_offsets[i];
|
||||
if (epoch_datalen == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(i != 1);
|
||||
|
||||
if (ngtcp2_conn_submit_crypto_data(
|
||||
conn, ngtcp2_crypto_picotls_from_epoch(i),
|
||||
sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) {
|
||||
rv = -1;
|
||||
goto fin;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
ngtcp2_conn_handshake_completed(conn);
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
|
||||
fin:
|
||||
ptls_buffer_dispose(&sendbuf);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
|
||||
(void)conn;
|
||||
(void)tls;
|
||||
|
||||
/* The remote transport parameters will be set via picotls
|
||||
collected_extensions callback */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
|
||||
size_t len) {
|
||||
(void)tls;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
|
||||
/* The local transport parameters will be set in an external
|
||||
call. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_level ngtcp2_crypto_picotls_from_epoch(size_t epoch) {
|
||||
switch (epoch) {
|
||||
case 0:
|
||||
return NGTCP2_CRYPTO_LEVEL_INITIAL;
|
||||
case 1:
|
||||
return NGTCP2_CRYPTO_LEVEL_EARLY;
|
||||
case 2:
|
||||
return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
|
||||
case 3:
|
||||
return NGTCP2_CRYPTO_LEVEL_APPLICATION;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(
|
||||
ngtcp2_crypto_level crypto_level) {
|
||||
switch (crypto_level) {
|
||||
case NGTCP2_CRYPTO_LEVEL_INITIAL:
|
||||
return 0;
|
||||
case NGTCP2_CRYPTO_LEVEL_EARLY:
|
||||
return 1;
|
||||
case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
|
||||
return 2;
|
||||
case NGTCP2_CRYPTO_LEVEL_APPLICATION:
|
||||
return 3;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
|
||||
void *user_data) {
|
||||
(void)conn;
|
||||
(void)user_data;
|
||||
|
||||
ptls_openssl_random_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
|
||||
ptls_openssl_random_bytes(data, datalen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls) {
|
||||
cptls->ptls = NULL;
|
||||
memset(&cptls->handshake_properties, 0, sizeof(cptls->handshake_properties));
|
||||
}
|
||||
|
||||
static int set_additional_extensions(ptls_handshake_properties_t *hsprops,
|
||||
ngtcp2_conn *conn) {
|
||||
const size_t buflen = 256;
|
||||
uint8_t *buf;
|
||||
ngtcp2_ssize nwrite;
|
||||
ptls_raw_extension_t *exts = hsprops->additional_extensions;
|
||||
|
||||
assert(exts);
|
||||
|
||||
buf = malloc(buflen);
|
||||
if (buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, buflen);
|
||||
if (nwrite < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
exts[0].type = NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1;
|
||||
exts[0].data.base = buf;
|
||||
exts[0].data.len = (size_t)nwrite;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_picotls_collect_extension(
|
||||
ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
|
||||
uint16_t type) {
|
||||
(void)ptls;
|
||||
(void)properties;
|
||||
|
||||
return type == NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_picotls_collected_extensions(
|
||||
ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
|
||||
ptls_raw_extension_t *extensions) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref;
|
||||
ngtcp2_conn *conn;
|
||||
int rv;
|
||||
|
||||
(void)properties;
|
||||
|
||||
for (; extensions->type != UINT16_MAX; ++extensions) {
|
||||
if (extensions->type != NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
conn_ref = *ptls_get_data_ptr(ptls);
|
||||
conn = conn_ref->get_conn(conn_ref);
|
||||
|
||||
rv = ngtcp2_conn_decode_remote_transport_params(conn, extensions->data.base,
|
||||
extensions->data.len);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_traffic_key_server_cb(ptls_update_traffic_key_t *self,
|
||||
ptls_t *ptls, int is_enc, size_t epoch,
|
||||
const void *secret) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch);
|
||||
ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls);
|
||||
size_t secretlen = cipher->hash->digest_size;
|
||||
ngtcp2_crypto_picotls_ctx *cptls;
|
||||
|
||||
(void)self;
|
||||
|
||||
if (is_enc) {
|
||||
if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
|
||||
secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_HANDSHAKE) {
|
||||
/* libngtcp2 allows an application to change QUIC transport
|
||||
* parameters before installing Handshake tx key. We need to
|
||||
* wait for the key to get the correct local transport
|
||||
* parameters from ngtcp2_conn.
|
||||
*/
|
||||
cptls = ngtcp2_conn_get_tls_native_handle(conn);
|
||||
|
||||
if (set_additional_extensions(&cptls->handshake_properties, conn) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
|
||||
secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ptls_update_traffic_key_t update_traffic_key_server = {
|
||||
update_traffic_key_server_cb,
|
||||
};
|
||||
|
||||
static int update_traffic_key_cb(ptls_update_traffic_key_t *self, ptls_t *ptls,
|
||||
int is_enc, size_t epoch, const void *secret) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch);
|
||||
ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls);
|
||||
size_t secretlen = cipher->hash->digest_size;
|
||||
|
||||
(void)self;
|
||||
|
||||
if (is_enc) {
|
||||
if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
|
||||
secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
|
||||
secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ptls_update_traffic_key_t update_traffic_key = {update_traffic_key_cb};
|
||||
|
||||
int ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx) {
|
||||
ctx->max_early_data_size = UINT32_MAX;
|
||||
ctx->omit_end_of_early_data = 1;
|
||||
ctx->update_traffic_key = &update_traffic_key_server;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx) {
|
||||
ctx->omit_end_of_early_data = 1;
|
||||
ctx->update_traffic_key = &update_traffic_key;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_picotls_configure_server_session(
|
||||
ngtcp2_crypto_picotls_ctx *cptls) {
|
||||
ptls_handshake_properties_t *hsprops = &cptls->handshake_properties;
|
||||
|
||||
hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension;
|
||||
hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_picotls_configure_client_session(
|
||||
ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) {
|
||||
ptls_handshake_properties_t *hsprops = &cptls->handshake_properties;
|
||||
|
||||
hsprops->client.max_early_data_size = calloc(1, sizeof(uint32_t));
|
||||
if (hsprops->client.max_early_data_size == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_additional_extensions(hsprops, conn) != 0) {
|
||||
free(hsprops->client.max_early_data_size);
|
||||
hsprops->client.max_early_data_size = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension;
|
||||
hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_picotls_deconfigure_session(
|
||||
ngtcp2_crypto_picotls_ctx *cptls) {
|
||||
ptls_handshake_properties_t *hsprops;
|
||||
ptls_raw_extension_t *exts;
|
||||
|
||||
if (cptls == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
hsprops = &cptls->handshake_properties;
|
||||
|
||||
free(hsprops->client.max_early_data_size);
|
||||
|
||||
exts = hsprops->additional_extensions;
|
||||
if (exts) {
|
||||
free(hsprops->additional_extensions[0].data.base);
|
||||
}
|
||||
}
|
||||
Vendored
+675
-88
@@ -24,10 +24,18 @@
|
||||
*/
|
||||
#include "shared.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#else
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "ngtcp2_macro.h"
|
||||
#include "ngtcp2_net.h"
|
||||
|
||||
ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
|
||||
void *md_native_handle) {
|
||||
@@ -78,10 +86,16 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
|
||||
|
||||
ngtcp2_crypto_ctx_initial(&ctx);
|
||||
|
||||
if (version == NGTCP2_PROTO_VER_V1) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V1:
|
||||
salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
|
||||
saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
|
||||
} else {
|
||||
break;
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT;
|
||||
saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1;
|
||||
break;
|
||||
default:
|
||||
salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT;
|
||||
saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1;
|
||||
}
|
||||
@@ -119,27 +133,55 @@ size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) {
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_derive_packet_protection_key(
|
||||
uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) {
|
||||
static const uint8_t KEY_LABEL[] = "quic key";
|
||||
static const uint8_t IV_LABEL[] = "quic iv";
|
||||
static const uint8_t HP_KEY_LABEL[] = "quic hp";
|
||||
uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version,
|
||||
const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret, size_t secretlen) {
|
||||
static const uint8_t KEY_LABEL_V1[] = "quic key";
|
||||
static const uint8_t IV_LABEL_V1[] = "quic iv";
|
||||
static const uint8_t HP_KEY_LABEL_V1[] = "quic hp";
|
||||
static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key";
|
||||
static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv";
|
||||
static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp";
|
||||
size_t keylen = ngtcp2_crypto_aead_keylen(aead);
|
||||
size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
|
||||
const uint8_t *key_label;
|
||||
size_t key_labellen;
|
||||
const uint8_t *iv_label;
|
||||
size_t iv_labellen;
|
||||
const uint8_t *hp_key_label;
|
||||
size_t hp_key_labellen;
|
||||
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
key_label = KEY_LABEL_V2_DRAFT;
|
||||
key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1;
|
||||
iv_label = IV_LABEL_V2_DRAFT;
|
||||
iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1;
|
||||
hp_key_label = HP_KEY_LABEL_V2_DRAFT;
|
||||
hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1;
|
||||
break;
|
||||
default:
|
||||
key_label = KEY_LABEL_V1;
|
||||
key_labellen = sizeof(KEY_LABEL_V1) - 1;
|
||||
iv_label = IV_LABEL_V1;
|
||||
iv_labellen = sizeof(IV_LABEL_V1) - 1;
|
||||
hp_key_label = HP_KEY_LABEL_V1;
|
||||
hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
|
||||
KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) {
|
||||
key_label, key_labellen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen,
|
||||
IV_LABEL, sizeof(IV_LABEL) - 1) != 0) {
|
||||
iv_label, iv_labellen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label(
|
||||
hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL,
|
||||
sizeof(HP_KEY_LABEL) - 1) != 0) {
|
||||
if (hp_key != NULL &&
|
||||
ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen,
|
||||
hp_key_label, hp_key_labellen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -176,6 +218,7 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
size_t ivlen;
|
||||
int rv;
|
||||
ngtcp2_crypto_ctx cctx;
|
||||
uint32_t version;
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) {
|
||||
return 0;
|
||||
@@ -191,12 +234,25 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
hp_key = hp_keybuf;
|
||||
}
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_EARLY) {
|
||||
switch (level) {
|
||||
case NGTCP2_CRYPTO_LEVEL_EARLY:
|
||||
ngtcp2_crypto_ctx_tls_early(&cctx, tls);
|
||||
ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
|
||||
ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
|
||||
} else {
|
||||
version = ngtcp2_conn_get_client_chosen_version(conn);
|
||||
break;
|
||||
case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
|
||||
if (ngtcp2_conn_is_server(conn) &&
|
||||
!ngtcp2_conn_get_negotiated_version(conn)) {
|
||||
rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
ctx = ngtcp2_conn_get_crypto_ctx(conn);
|
||||
version = ngtcp2_conn_get_negotiated_version(conn);
|
||||
|
||||
if (!ctx->aead.native_handle) {
|
||||
ngtcp2_crypto_ctx_tls(&cctx, tls);
|
||||
@@ -210,8 +266,8 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
hp = &ctx->hp;
|
||||
ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md,
|
||||
secret, secretlen) != 0) {
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
|
||||
md, secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -272,17 +328,10 @@ fail:
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) {
|
||||
ngtcp2_transport_params_type exttype =
|
||||
ngtcp2_conn_is_server(conn)
|
||||
? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
|
||||
: NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO;
|
||||
ngtcp2_transport_params params;
|
||||
ngtcp2_ssize nwrite;
|
||||
uint8_t buf[256];
|
||||
|
||||
ngtcp2_conn_get_local_transport_params(conn, ¶ms);
|
||||
|
||||
nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, ¶ms);
|
||||
nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf));
|
||||
if (nwrite < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -310,6 +359,7 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
size_t ivlen;
|
||||
int rv;
|
||||
ngtcp2_crypto_ctx cctx;
|
||||
uint32_t version;
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) {
|
||||
return 0;
|
||||
@@ -325,12 +375,25 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
hp_key = hp_keybuf;
|
||||
}
|
||||
|
||||
if (level == NGTCP2_CRYPTO_LEVEL_EARLY) {
|
||||
switch (level) {
|
||||
case NGTCP2_CRYPTO_LEVEL_EARLY:
|
||||
ngtcp2_crypto_ctx_tls_early(&cctx, tls);
|
||||
ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
|
||||
ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
|
||||
} else {
|
||||
version = ngtcp2_conn_get_client_chosen_version(conn);
|
||||
break;
|
||||
case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
|
||||
if (ngtcp2_conn_is_server(conn) &&
|
||||
!ngtcp2_conn_get_negotiated_version(conn)) {
|
||||
rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
ctx = ngtcp2_conn_get_crypto_ctx(conn);
|
||||
version = ngtcp2_conn_get_negotiated_version(conn);
|
||||
|
||||
if (!ctx->aead.native_handle) {
|
||||
ngtcp2_crypto_ctx_tls(&cctx, tls);
|
||||
@@ -344,8 +407,8 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
hp = &ctx->hp;
|
||||
ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md,
|
||||
secret, secretlen) != 0) {
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
|
||||
md, secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -371,15 +434,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ngtcp2_conn_is_server(conn)) {
|
||||
rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (crypto_set_local_transport_params(conn, tls) != 0) {
|
||||
return rv;
|
||||
}
|
||||
if (ngtcp2_conn_is_server(conn) &&
|
||||
crypto_set_local_transport_params(conn, tls) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -408,7 +465,7 @@ int ngtcp2_crypto_derive_and_install_initial_key(
|
||||
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
|
||||
uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
|
||||
uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
|
||||
const ngtcp2_cid *client_dcid) {
|
||||
uint32_t version, const ngtcp2_cid *client_dcid) {
|
||||
uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
@@ -427,7 +484,6 @@ int ngtcp2_crypto_derive_and_install_initial_key(
|
||||
ngtcp2_crypto_aead_ctx retry_aead_ctx = {0};
|
||||
int rv;
|
||||
int server = ngtcp2_conn_is_server(conn);
|
||||
uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
|
||||
const uint8_t *retry_key;
|
||||
size_t retry_noncelen;
|
||||
|
||||
@@ -472,13 +528,13 @@ int ngtcp2_crypto_derive_and_install_initial_key(
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret,
|
||||
rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret,
|
||||
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
|
||||
tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
|
||||
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -506,10 +562,16 @@ int ngtcp2_crypto_derive_and_install_initial_key(
|
||||
if (!server && !ngtcp2_conn_after_retry(conn)) {
|
||||
ngtcp2_crypto_aead_retry(&retry_aead);
|
||||
|
||||
if (ngtcp2_conn_get_negotiated_version(conn) == NGTCP2_PROTO_VER_V1) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V1:
|
||||
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
|
||||
retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
|
||||
} else {
|
||||
break;
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
|
||||
retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
|
||||
break;
|
||||
default:
|
||||
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
|
||||
retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
|
||||
}
|
||||
@@ -543,6 +605,114 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_derive_and_install_vneg_initial_key(
|
||||
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
|
||||
uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
|
||||
uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
|
||||
uint32_t version, const ngtcp2_cid *client_dcid) {
|
||||
uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
|
||||
uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
|
||||
uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
|
||||
uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
|
||||
uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
|
||||
uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
|
||||
const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn);
|
||||
ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
|
||||
ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
|
||||
ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
|
||||
ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
|
||||
int rv;
|
||||
int server = ngtcp2_conn_is_server(conn);
|
||||
|
||||
if (!rx_secret) {
|
||||
rx_secret = rx_secretbuf;
|
||||
}
|
||||
if (!tx_secret) {
|
||||
tx_secret = tx_secretbuf;
|
||||
}
|
||||
if (!initial_secret) {
|
||||
initial_secret = initial_secretbuf;
|
||||
}
|
||||
|
||||
if (!rx_key) {
|
||||
rx_key = rx_keybuf;
|
||||
}
|
||||
if (!rx_iv) {
|
||||
rx_iv = rx_ivbuf;
|
||||
}
|
||||
if (!rx_hp_key) {
|
||||
rx_hp_key = rx_hp_keybuf;
|
||||
}
|
||||
if (!tx_key) {
|
||||
tx_key = tx_keybuf;
|
||||
}
|
||||
if (!tx_iv) {
|
||||
tx_iv = tx_ivbuf;
|
||||
}
|
||||
if (!tx_hp_key) {
|
||||
tx_hp_key = tx_hp_keybuf;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_initial_secrets(
|
||||
version, rx_secret, tx_secret, initial_secret, client_dcid,
|
||||
server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
|
||||
0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret,
|
||||
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret,
|
||||
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key,
|
||||
NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) !=
|
||||
0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key,
|
||||
NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) !=
|
||||
0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rv = ngtcp2_conn_install_vneg_initial_key(
|
||||
conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv,
|
||||
&tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
|
||||
ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
|
||||
ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
|
||||
ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_update_key(
|
||||
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
|
||||
ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
|
||||
@@ -553,14 +723,15 @@ int ngtcp2_crypto_update_key(
|
||||
const ngtcp2_crypto_aead *aead = &ctx->aead;
|
||||
const ngtcp2_crypto_md *md = &ctx->md;
|
||||
size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
|
||||
uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
|
||||
|
||||
if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret,
|
||||
secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md,
|
||||
rx_secret, secretlen) != 0) {
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -569,8 +740,8 @@ int ngtcp2_crypto_update_key(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md,
|
||||
tx_secret, secretlen) != 0) {
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -593,9 +764,9 @@ int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen) {
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen,
|
||||
nonce, noncelen, ad, adlen) != 0) {
|
||||
nonce, noncelen, aad, aadlen) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
@@ -605,10 +776,10 @@ int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen) {
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen,
|
||||
nonce, noncelen, ad, adlen) != 0) {
|
||||
return NGTCP2_ERR_TLS_DECRYPT;
|
||||
nonce, noncelen, aad, aadlen) != 0) {
|
||||
return NGTCP2_ERR_DECRYPT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -643,32 +814,413 @@ int ngtcp2_crypto_update_key_cb(
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token,
|
||||
const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen,
|
||||
const ngtcp2_cid *cid) {
|
||||
uint8_t buf[64];
|
||||
int rv;
|
||||
static const uint8_t info[] = "stateless_reset";
|
||||
ngtcp2_crypto_md md;
|
||||
|
||||
assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf));
|
||||
assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf));
|
||||
|
||||
rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data,
|
||||
cid->datalen);
|
||||
if (rv != 0) {
|
||||
if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN,
|
||||
ngtcp2_crypto_md_sha256(&md), secret, secretlen,
|
||||
cid->data, cid->datalen, info,
|
||||
sizeof(info) - 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen,
|
||||
uint32_t version,
|
||||
const ngtcp2_cid *dcid,
|
||||
const ngtcp2_cid *scid,
|
||||
uint64_t error_code) {
|
||||
static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
|
||||
size_t ivlen, const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *info_prefix,
|
||||
size_t info_prefixlen) {
|
||||
static const uint8_t key_info_suffix[] = " key";
|
||||
static const uint8_t iv_info_suffix[] = " iv";
|
||||
uint8_t intsecret[32];
|
||||
uint8_t info[32];
|
||||
uint8_t *p;
|
||||
|
||||
assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret));
|
||||
assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info));
|
||||
assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info));
|
||||
|
||||
if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt,
|
||||
saltlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(info, info_prefix, info_prefixlen);
|
||||
p = info + info_prefixlen;
|
||||
|
||||
memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1);
|
||||
p += sizeof(key_info_suffix) - 1;
|
||||
|
||||
if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret),
|
||||
info, (size_t)(p - info)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = info + info_prefixlen;
|
||||
|
||||
memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1);
|
||||
p += sizeof(iv_info_suffix) - 1;
|
||||
|
||||
if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret),
|
||||
info, (size_t)(p - info)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version,
|
||||
const ngtcp2_sockaddr *sa,
|
||||
ngtcp2_socklen salen,
|
||||
const ngtcp2_cid *retry_scid) {
|
||||
uint8_t *p = dest;
|
||||
|
||||
version = ngtcp2_htonl(version);
|
||||
memcpy(p, &version, sizeof(version));
|
||||
memcpy(p, sa, (size_t)salen);
|
||||
p += salen;
|
||||
memcpy(p, retry_scid->data, retry_scid->datalen);
|
||||
p += retry_scid->datalen;
|
||||
|
||||
return (size_t)(p - dest);
|
||||
}
|
||||
|
||||
static const uint8_t retry_token_info_prefix[] = "retry_token";
|
||||
|
||||
ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
|
||||
uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
|
||||
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) {
|
||||
uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
|
||||
uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
|
||||
uint8_t key[32];
|
||||
uint8_t iv[32];
|
||||
size_t keylen;
|
||||
size_t ivlen;
|
||||
ngtcp2_crypto_aead aead;
|
||||
ngtcp2_crypto_md md;
|
||||
ngtcp2_crypto_aead_ctx aead_ctx;
|
||||
size_t plaintextlen;
|
||||
uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
|
||||
NGTCP2_MAX_CIDLEN];
|
||||
size_t aadlen;
|
||||
uint8_t *p = plaintext;
|
||||
ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
|
||||
int rv;
|
||||
|
||||
memset(plaintext, 0, sizeof(plaintext));
|
||||
|
||||
*p++ = (uint8_t)odcid->datalen;
|
||||
memcpy(p, odcid->data, odcid->datalen);
|
||||
p += NGTCP2_MAX_CIDLEN;
|
||||
memcpy(p, &ts_be, sizeof(ts_be));
|
||||
p += sizeof(ts_be);
|
||||
|
||||
plaintextlen = (size_t)(p - plaintext);
|
||||
|
||||
if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead_aes_128_gcm(&aead);
|
||||
ngtcp2_crypto_md_sha256(&md);
|
||||
|
||||
keylen = ngtcp2_crypto_aead_keylen(&aead);
|
||||
ivlen = ngtcp2_crypto_aead_noncelen(&aead);
|
||||
|
||||
assert(sizeof(key) >= keylen);
|
||||
assert(sizeof(iv) >= ivlen);
|
||||
|
||||
if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
|
||||
rand_data, sizeof(rand_data),
|
||||
retry_token_info_prefix,
|
||||
sizeof(retry_token_info_prefix) - 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
|
||||
remote_addrlen, retry_scid);
|
||||
|
||||
p = token;
|
||||
*p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY;
|
||||
|
||||
if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
|
||||
ivlen, aad, aadlen);
|
||||
|
||||
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += plaintextlen + aead.max_overhead;
|
||||
memcpy(p, rand_data, sizeof(rand_data));
|
||||
p += sizeof(rand_data);
|
||||
|
||||
return p - token;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_verify_retry_token(
|
||||
ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
|
||||
const uint8_t *secret, size_t secretlen, uint32_t version,
|
||||
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
|
||||
uint8_t
|
||||
plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
|
||||
uint8_t key[32];
|
||||
uint8_t iv[32];
|
||||
size_t keylen;
|
||||
size_t ivlen;
|
||||
ngtcp2_crypto_aead_ctx aead_ctx;
|
||||
ngtcp2_crypto_aead aead;
|
||||
ngtcp2_crypto_md md;
|
||||
uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
|
||||
NGTCP2_MAX_CIDLEN];
|
||||
size_t aadlen;
|
||||
const uint8_t *rand_data;
|
||||
const uint8_t *ciphertext;
|
||||
size_t ciphertextlen;
|
||||
size_t cil;
|
||||
int rv;
|
||||
ngtcp2_tstamp gen_ts;
|
||||
|
||||
if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN ||
|
||||
token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
|
||||
ciphertext = token + 1;
|
||||
ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
|
||||
|
||||
ngtcp2_crypto_aead_aes_128_gcm(&aead);
|
||||
ngtcp2_crypto_md_sha256(&md);
|
||||
|
||||
keylen = ngtcp2_crypto_aead_keylen(&aead);
|
||||
ivlen = ngtcp2_crypto_aead_noncelen(&aead);
|
||||
|
||||
if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
|
||||
rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
|
||||
retry_token_info_prefix,
|
||||
sizeof(retry_token_info_prefix) - 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
|
||||
remote_addrlen, dcid);
|
||||
|
||||
if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
|
||||
ciphertextlen, iv, ivlen, aad, aadlen);
|
||||
|
||||
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cil = plaintext[0];
|
||||
|
||||
assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN));
|
||||
|
||||
memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN,
|
||||
sizeof(gen_ts));
|
||||
|
||||
gen_ts = ngtcp2_ntohl64(gen_ts);
|
||||
if (gen_ts + timeout <= ts) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t crypto_generate_regular_token_aad(uint8_t *dest,
|
||||
const ngtcp2_sockaddr *sa) {
|
||||
const uint8_t *addr;
|
||||
size_t addrlen;
|
||||
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr;
|
||||
addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr =
|
||||
(const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr;
|
||||
addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
memcpy(dest, addr, addrlen);
|
||||
|
||||
return addrlen;
|
||||
}
|
||||
|
||||
static const uint8_t regular_token_info_prefix[] = "regular_token";
|
||||
|
||||
ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
|
||||
uint8_t *token, const uint8_t *secret, size_t secretlen,
|
||||
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
|
||||
ngtcp2_tstamp ts) {
|
||||
uint8_t plaintext[sizeof(ngtcp2_tstamp)];
|
||||
uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
|
||||
uint8_t key[32];
|
||||
uint8_t iv[32];
|
||||
size_t keylen;
|
||||
size_t ivlen;
|
||||
ngtcp2_crypto_aead aead;
|
||||
ngtcp2_crypto_md md;
|
||||
ngtcp2_crypto_aead_ctx aead_ctx;
|
||||
size_t plaintextlen;
|
||||
uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
|
||||
size_t aadlen;
|
||||
uint8_t *p = plaintext;
|
||||
ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
|
||||
int rv;
|
||||
(void)remote_addrlen;
|
||||
|
||||
memcpy(p, &ts_be, sizeof(ts_be));
|
||||
p += sizeof(ts_be);
|
||||
|
||||
plaintextlen = (size_t)(p - plaintext);
|
||||
|
||||
if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead_aes_128_gcm(&aead);
|
||||
ngtcp2_crypto_md_sha256(&md);
|
||||
|
||||
keylen = ngtcp2_crypto_aead_keylen(&aead);
|
||||
ivlen = ngtcp2_crypto_aead_noncelen(&aead);
|
||||
|
||||
assert(sizeof(key) >= keylen);
|
||||
assert(sizeof(iv) >= ivlen);
|
||||
|
||||
if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
|
||||
rand_data, sizeof(rand_data),
|
||||
regular_token_info_prefix,
|
||||
sizeof(regular_token_info_prefix) - 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
|
||||
|
||||
p = token;
|
||||
*p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR;
|
||||
|
||||
if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
|
||||
ivlen, aad, aadlen);
|
||||
|
||||
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += plaintextlen + aead.max_overhead;
|
||||
memcpy(p, rand_data, sizeof(rand_data));
|
||||
p += sizeof(rand_data);
|
||||
|
||||
return p - token;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const ngtcp2_sockaddr *remote_addr,
|
||||
ngtcp2_socklen remote_addrlen,
|
||||
ngtcp2_duration timeout,
|
||||
ngtcp2_tstamp ts) {
|
||||
uint8_t plaintext[sizeof(ngtcp2_tstamp)];
|
||||
uint8_t key[32];
|
||||
uint8_t iv[32];
|
||||
size_t keylen;
|
||||
size_t ivlen;
|
||||
ngtcp2_crypto_aead_ctx aead_ctx;
|
||||
ngtcp2_crypto_aead aead;
|
||||
ngtcp2_crypto_md md;
|
||||
uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
|
||||
size_t aadlen;
|
||||
const uint8_t *rand_data;
|
||||
const uint8_t *ciphertext;
|
||||
size_t ciphertextlen;
|
||||
int rv;
|
||||
ngtcp2_tstamp gen_ts;
|
||||
(void)remote_addrlen;
|
||||
|
||||
if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
|
||||
token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
|
||||
ciphertext = token + 1;
|
||||
ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
|
||||
|
||||
ngtcp2_crypto_aead_aes_128_gcm(&aead);
|
||||
ngtcp2_crypto_md_sha256(&md);
|
||||
|
||||
keylen = ngtcp2_crypto_aead_keylen(&aead);
|
||||
ivlen = ngtcp2_crypto_aead_noncelen(&aead);
|
||||
|
||||
if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
|
||||
rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
|
||||
regular_token_info_prefix,
|
||||
sizeof(regular_token_info_prefix) - 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
|
||||
|
||||
if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
|
||||
ciphertextlen, iv, ivlen, aad, aadlen);
|
||||
|
||||
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&gen_ts, plaintext, sizeof(gen_ts));
|
||||
|
||||
gen_ts = ngtcp2_ntohl64(gen_ts);
|
||||
if (gen_ts + timeout <= ts) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ngtcp2_ssize ngtcp2_crypto_write_connection_close(
|
||||
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
|
||||
const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
|
||||
size_t reasonlen) {
|
||||
uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
|
||||
@@ -689,7 +1241,7 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen,
|
||||
}
|
||||
|
||||
if (ngtcp2_crypto_derive_packet_protection_key(
|
||||
tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
|
||||
tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
|
||||
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -706,8 +1258,9 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen,
|
||||
}
|
||||
|
||||
spktlen = ngtcp2_pkt_write_connection_close(
|
||||
dest, destlen, version, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb,
|
||||
&ctx.aead, &aead_ctx, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx);
|
||||
dest, destlen, version, dcid, scid, error_code, reason, reasonlen,
|
||||
ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv,
|
||||
ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx);
|
||||
if (spktlen < 0) {
|
||||
spktlen = -1;
|
||||
}
|
||||
@@ -732,10 +1285,16 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
|
||||
|
||||
ngtcp2_crypto_aead_retry(&aead);
|
||||
|
||||
if (version == NGTCP2_PROTO_VER_V1) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V1:
|
||||
key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
|
||||
} else {
|
||||
break;
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
|
||||
break;
|
||||
default:
|
||||
key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
|
||||
}
|
||||
@@ -757,22 +1316,14 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
|
||||
return spktlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* crypto_setup_initial_crypto establishes the initial secrets and
|
||||
* encryption keys, and prepares local QUIC transport parameters.
|
||||
*/
|
||||
static int crypto_setup_initial_crypto(ngtcp2_conn *conn,
|
||||
const ngtcp2_cid *dcid) {
|
||||
return ngtcp2_crypto_derive_and_install_initial_key(
|
||||
conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) {
|
||||
const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn);
|
||||
void *tls = ngtcp2_conn_get_tls_native_handle(conn);
|
||||
(void)user_data;
|
||||
|
||||
if (crypto_setup_initial_crypto(conn, dcid) != 0) {
|
||||
if (ngtcp2_crypto_derive_and_install_initial_key(
|
||||
conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
@@ -792,9 +1343,9 @@ int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
|
||||
void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
&hd->scid) != 0) {
|
||||
if (ngtcp2_crypto_derive_and_install_initial_key(
|
||||
conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
@@ -806,7 +1357,23 @@ int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
|
||||
void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
if (crypto_setup_initial_crypto(conn, dcid) != 0) {
|
||||
if (ngtcp2_crypto_derive_and_install_initial_key(
|
||||
conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
|
||||
const ngtcp2_cid *client_dcid,
|
||||
void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
if (ngtcp2_crypto_derive_and_install_vneg_initial_key(
|
||||
conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version,
|
||||
client_dcid) != 0) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
@@ -829,3 +1396,23 @@ void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
|
||||
|
||||
ngtcp2_crypto_cipher_ctx_free(cipher_ctx);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn,
|
||||
ngtcp2_crypto_level crypto_level,
|
||||
uint64_t offset, const uint8_t *data,
|
||||
size_t datalen, void *user_data) {
|
||||
int rv;
|
||||
(void)offset;
|
||||
(void)user_data;
|
||||
|
||||
if (ngtcp2_crypto_read_write_crypto_data(conn, crypto_level, data, datalen) !=
|
||||
0) {
|
||||
rv = ngtcp2_conn_get_tls_error(conn);
|
||||
if (rv) {
|
||||
return rv;
|
||||
}
|
||||
return NGTCP2_ERR_CRYPTO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+140
-1
@@ -51,6 +51,16 @@
|
||||
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \
|
||||
"\x7f\x0a"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to
|
||||
* derive initial secret. It is used for QUIC v2 draft.
|
||||
*/
|
||||
#define NGTCP2_INITIAL_SALT_V2_DRAFT \
|
||||
"\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3" \
|
||||
"\xe5\xd3"
|
||||
|
||||
/* Maximum key usage (encryption) limits */
|
||||
#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
|
||||
#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
|
||||
@@ -62,6 +72,40 @@
|
||||
#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36)
|
||||
#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL)
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
|
||||
* encryption and decryption.
|
||||
*/
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_aead_init` initializes |aead| with the provided
|
||||
* |aead_native_handle| which is an underlying AEAD object.
|
||||
*
|
||||
* If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
|
||||
* a pointer to EVP_CIPHER.
|
||||
*
|
||||
* If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
|
||||
* gnutls_cipher_algorithm_t casted to ``void *``.
|
||||
*
|
||||
* If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
|
||||
* be a pointer to EVP_AEAD.
|
||||
*/
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
|
||||
void *aead_native_handle);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
|
||||
* AEAD_AES_128_GCM for Retry packet integrity protection.
|
||||
*/
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -84,6 +128,34 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
|
||||
const ngtcp2_cid *client_dcid,
|
||||
ngtcp2_crypto_side side);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_derive_packet_protection_key` derives packet
|
||||
* protection key. This function writes packet protection key into
|
||||
* the buffer pointed by |key|. The length of derived key is
|
||||
* `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>`
|
||||
* bytes. |key| must have enough capacity to store the key. This
|
||||
* function writes packet protection IV into |iv|. The length of
|
||||
* derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead)
|
||||
* <ngtcp2_crypto_packet_protection_ivlen>` bytes. |iv| must have
|
||||
* enough capacity to store the IV.
|
||||
*
|
||||
* If |hp| is not NULL, this function also derives packet header
|
||||
* protection key and writes the key into the buffer pointed by |hp|.
|
||||
* The length of derived key is `ngtcp2_crypto_aead_keylen(aead)
|
||||
* <ngtcp2_crypto_aead_keylen>` bytes. |hp|, if not NULL, must have
|
||||
* enough capacity to store the key.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv,
|
||||
uint8_t *hp, uint32_t version,
|
||||
const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -183,7 +255,58 @@ int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls);
|
||||
int ngtcp2_crypto_derive_and_install_initial_key(
|
||||
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
|
||||
uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
|
||||
uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp,
|
||||
uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
|
||||
const ngtcp2_cid *client_dcid);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial
|
||||
* keying materials and installs keys to |conn|. This function is
|
||||
* dedicated to install keys for |version| which is negotiated, or
|
||||
* being negotiated.
|
||||
*
|
||||
* If |rx_secret| is not NULL, the secret for decryption is written to
|
||||
* the buffer pointed by |rx_secret|. The length of secret is 32
|
||||
* bytes, and |rx_secret| must point to the buffer which has enough
|
||||
* capacity.
|
||||
*
|
||||
* If |tx_secret| is not NULL, the secret for encryption is written to
|
||||
* the buffer pointed by |tx_secret|. The length of secret is 32
|
||||
* bytes, and |tx_secret| must point to the buffer which has enough
|
||||
* capacity.
|
||||
*
|
||||
* If |initial_secret| is not NULL, the initial secret is written to
|
||||
* the buffer pointed by |initial_secret|. The length of secret is 32
|
||||
* bytes, and |initial_secret| must point to the buffer which has
|
||||
* enough capacity.
|
||||
*
|
||||
* |client_dcid| is the destination connection ID in first Initial
|
||||
* packet of client.
|
||||
*
|
||||
* If |rx_key| is not NULL, the derived packet protection key for
|
||||
* decryption is written to the buffer pointed by |rx_key|. If
|
||||
* |rx_iv| is not NULL, the derived packet protection IV for
|
||||
* decryption is written to the buffer pointed by |rx_iv|. If |rx_hp|
|
||||
* is not NULL, the derived header protection key for decryption is
|
||||
* written to the buffer pointed by |rx_hp|.
|
||||
*
|
||||
* If |tx_key| is not NULL, the derived packet protection key for
|
||||
* encryption is written to the buffer pointed by |tx_key|. If
|
||||
* |tx_iv| is not NULL, the derived packet protection IV for
|
||||
* encryption is written to the buffer pointed by |tx_iv|. If |tx_hp|
|
||||
* is not NULL, the derived header protection key for encryption is
|
||||
* written to the buffer pointed by |tx_hp|.
|
||||
*
|
||||
* The length of packet protection key and header protection key is 16
|
||||
* bytes long. The length of packet protection IV is 12 bytes long.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int ngtcp2_crypto_derive_and_install_vneg_initial_key(
|
||||
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
|
||||
uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
|
||||
uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
|
||||
const ngtcp2_cid *client_dcid);
|
||||
|
||||
/**
|
||||
@@ -208,4 +331,20 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
|
||||
*/
|
||||
void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx);
|
||||
|
||||
/*
|
||||
* `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message
|
||||
* digest algorithm and returns |md|.
|
||||
*/
|
||||
ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md);
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead);
|
||||
|
||||
/*
|
||||
* `ngtcp2_crypto_random` writes cryptographically-secure random
|
||||
* |datalen| bytes into the buffer pointed by |data|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or -1.
|
||||
*/
|
||||
int ngtcp2_crypto_random(uint8_t *data, size_t datalen);
|
||||
|
||||
#endif /* NGTCP2_SHARED_H */
|
||||
+524
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <ngtcp2/ngtcp2_crypto.h>
|
||||
#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
|
||||
|
||||
#include <wolfssl/ssl.h>
|
||||
#include <wolfssl/quic.h>
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
#define PRINTF_DEBUG 0
|
||||
#if PRINTF_DEBUG
|
||||
# define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
# define DEBUG_MSG(...) (void)0
|
||||
#endif
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
|
||||
return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm());
|
||||
}
|
||||
|
||||
ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
|
||||
md->native_handle = (void *)wolfSSL_EVP_sha256();
|
||||
return md;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
|
||||
ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_EVP_aes_128_gcm());
|
||||
ctx->md.native_handle = (void *)wolfSSL_EVP_sha256();
|
||||
ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ctr();
|
||||
ctx->max_encryption = 0;
|
||||
ctx->max_decryption_failure = 0;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
|
||||
void *aead_native_handle) {
|
||||
aead->native_handle = aead_native_handle;
|
||||
aead->max_overhead = wolfSSL_quic_get_aead_tag_len(
|
||||
(const WOLFSSL_EVP_CIPHER *)(aead_native_handle));
|
||||
return aead;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
|
||||
return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm());
|
||||
}
|
||||
|
||||
static uint64_t crypto_wolfssl_get_aead_max_encryption(WOLFSSL *ssl) {
|
||||
const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl);
|
||||
|
||||
if (wolfSSL_quic_aead_is_gcm(aead)) {
|
||||
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
|
||||
}
|
||||
if (wolfSSL_quic_aead_is_chacha20(aead)) {
|
||||
return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
|
||||
}
|
||||
if (wolfSSL_quic_aead_is_ccm(aead)) {
|
||||
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t crypto_wolfssl_get_aead_max_decryption_failure(WOLFSSL *ssl) {
|
||||
const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl);
|
||||
|
||||
if (wolfSSL_quic_aead_is_gcm(aead)) {
|
||||
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
|
||||
}
|
||||
if (wolfSSL_quic_aead_is_chacha20(aead)) {
|
||||
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
|
||||
}
|
||||
if (wolfSSL_quic_aead_is_ccm(aead)) {
|
||||
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
|
||||
void *tls_native_handle) {
|
||||
WOLFSSL *ssl = tls_native_handle;
|
||||
|
||||
ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_quic_get_aead(ssl));
|
||||
ctx->md.native_handle = (void *)wolfSSL_quic_get_md(ssl);
|
||||
ctx->hp.native_handle = (void *)wolfSSL_quic_get_hp(ssl);
|
||||
ctx->max_encryption = crypto_wolfssl_get_aead_max_encryption(ssl);
|
||||
ctx->max_decryption_failure =
|
||||
crypto_wolfssl_get_aead_max_decryption_failure(ssl);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
|
||||
void *tls_native_handle) {
|
||||
return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
|
||||
}
|
||||
|
||||
static size_t crypto_md_hashlen(const WOLFSSL_EVP_MD *md) {
|
||||
return (size_t)wolfSSL_EVP_MD_size(md);
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
|
||||
return crypto_md_hashlen(md->native_handle);
|
||||
}
|
||||
|
||||
static size_t crypto_aead_keylen(const WOLFSSL_EVP_CIPHER *aead) {
|
||||
return (size_t)wolfSSL_EVP_Cipher_key_length(aead);
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
|
||||
return crypto_aead_keylen(aead->native_handle);
|
||||
}
|
||||
|
||||
static size_t crypto_aead_noncelen(const WOLFSSL_EVP_CIPHER *aead) {
|
||||
return (size_t)wolfSSL_EVP_CIPHER_iv_length(aead);
|
||||
}
|
||||
|
||||
size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
|
||||
return crypto_aead_noncelen(aead->native_handle);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const ngtcp2_crypto_aead *aead,
|
||||
const uint8_t *key, size_t noncelen) {
|
||||
const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle;
|
||||
WOLFSSL_EVP_CIPHER_CTX *actx;
|
||||
static const uint8_t iv[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
(void)noncelen;
|
||||
actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 1);
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aead_ctx->native_handle = actx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const ngtcp2_crypto_aead *aead,
|
||||
const uint8_t *key, size_t noncelen) {
|
||||
const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle;
|
||||
WOLFSSL_EVP_CIPHER_CTX *actx;
|
||||
static const uint8_t iv[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
(void)noncelen;
|
||||
actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 0);
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aead_ctx->native_handle = actx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
|
||||
if (aead_ctx->native_handle) {
|
||||
wolfSSL_EVP_CIPHER_CTX_free(aead_ctx->native_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
|
||||
const ngtcp2_crypto_cipher *cipher,
|
||||
const uint8_t *key) {
|
||||
WOLFSSL_EVP_CIPHER_CTX *actx;
|
||||
|
||||
actx =
|
||||
wolfSSL_quic_crypt_new(cipher->native_handle, key, NULL, /* encrypt */ 1);
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cipher_ctx->native_handle = actx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
|
||||
if (cipher_ctx->native_handle) {
|
||||
wolfSSL_EVP_CIPHER_CTX_free(cipher_ctx->native_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen) {
|
||||
if (wolfSSL_quic_hkdf_extract(dest, md->native_handle, secret, secretlen,
|
||||
salt, saltlen) != WOLFSSL_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *info,
|
||||
size_t infolen) {
|
||||
if (wolfSSL_quic_hkdf_expand(dest, destlen, md->native_handle, secret,
|
||||
secretlen, info, infolen) != WOLFSSL_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_crypto_md *md, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *info, size_t infolen) {
|
||||
if (wolfSSL_quic_hkdf(dest, destlen, md->native_handle, secret, secretlen,
|
||||
salt, saltlen, info, infolen) != WOLFSSL_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
(void)aead;
|
||||
(void)noncelen;
|
||||
if (wolfSSL_quic_aead_encrypt(dest, aead_ctx->native_handle, plaintext,
|
||||
plaintextlen, nonce, aad,
|
||||
aadlen) != WOLFSSL_SUCCESS) {
|
||||
DEBUG_MSG("WOLFSSL: encrypt FAILED\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *aad, size_t aadlen) {
|
||||
(void)aead;
|
||||
(void)noncelen;
|
||||
if (wolfSSL_quic_aead_decrypt(dest, aead_ctx->native_handle, ciphertext,
|
||||
ciphertextlen, nonce, aad,
|
||||
aadlen) != WOLFSSL_SUCCESS) {
|
||||
|
||||
DEBUG_MSG("WOLFSSL: decrypt FAILED\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
|
||||
const ngtcp2_crypto_cipher_ctx *hp_ctx,
|
||||
const uint8_t *sample) {
|
||||
static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
|
||||
WOLFSSL_EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
|
||||
int len;
|
||||
|
||||
(void)hp;
|
||||
|
||||
if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) !=
|
||||
WOLFSSL_SUCCESS ||
|
||||
wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT,
|
||||
sizeof(PLAINTEXT) - 1) != WOLFSSL_SUCCESS ||
|
||||
wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len) !=
|
||||
WOLFSSL_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
|
||||
ngtcp2_crypto_level crypto_level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
WOLFSSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
|
||||
WOLFSSL_ENCRYPTION_LEVEL level =
|
||||
ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(crypto_level);
|
||||
int rv;
|
||||
int err;
|
||||
|
||||
DEBUG_MSG("WOLFSSL: read/write crypto data, level=%d len=%lu\n", level,
|
||||
datalen);
|
||||
if (datalen > 0) {
|
||||
rv = wolfSSL_provide_quic_data(ssl, level, data, datalen);
|
||||
if (rv != WOLFSSL_SUCCESS) {
|
||||
DEBUG_MSG("WOLFSSL: read/write crypto data FAILED, rv=%d\n", rv);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ngtcp2_conn_get_handshake_completed(conn)) {
|
||||
rv = wolfSSL_quic_do_handshake(ssl);
|
||||
DEBUG_MSG("WOLFSSL: do_handshake, rv=%d\n", rv);
|
||||
if (rv <= 0) {
|
||||
err = wolfSSL_get_error(ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return 0;
|
||||
case SSL_ERROR_SSL:
|
||||
return -1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_MSG("WOLFSSL: handshake done\n");
|
||||
ngtcp2_conn_handshake_completed(conn);
|
||||
}
|
||||
|
||||
rv = wolfSSL_process_quic_post_handshake(ssl);
|
||||
DEBUG_MSG("WOLFSSL: process post handshake, rv=%d\n", rv);
|
||||
if (rv != 1) {
|
||||
err = wolfSSL_get_error(ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return 0;
|
||||
case SSL_ERROR_SSL:
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return -1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
|
||||
WOLFSSL *ssl = tls;
|
||||
const uint8_t *tp;
|
||||
size_t tplen;
|
||||
int rv;
|
||||
|
||||
wolfSSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
|
||||
DEBUG_MSG("WOLFSSL: get peer transport params, len=%lu\n", tplen);
|
||||
|
||||
rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
|
||||
if (rv != 0) {
|
||||
DEBUG_MSG("WOLFSSL: decode peer transport params failed, rv=%d\n", rv);
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
|
||||
size_t len) {
|
||||
WOLFSSL *ssl = tls;
|
||||
DEBUG_MSG("WOLFSSL: set local peer transport params, len=%lu\n", len);
|
||||
if (wolfSSL_set_quic_transport_params(ssl, buf, len) != WOLFSSL_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ngtcp2_crypto_level ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(
|
||||
WOLFSSL_ENCRYPTION_LEVEL wolfssl_level) {
|
||||
switch (wolfssl_level) {
|
||||
case wolfssl_encryption_initial:
|
||||
return NGTCP2_CRYPTO_LEVEL_INITIAL;
|
||||
case wolfssl_encryption_early_data:
|
||||
return NGTCP2_CRYPTO_LEVEL_EARLY;
|
||||
case wolfssl_encryption_handshake:
|
||||
return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
|
||||
case wolfssl_encryption_application:
|
||||
return NGTCP2_CRYPTO_LEVEL_APPLICATION;
|
||||
default:
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
}
|
||||
|
||||
WOLFSSL_ENCRYPTION_LEVEL
|
||||
ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(
|
||||
ngtcp2_crypto_level crypto_level) {
|
||||
switch (crypto_level) {
|
||||
case NGTCP2_CRYPTO_LEVEL_INITIAL:
|
||||
return wolfssl_encryption_initial;
|
||||
case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
|
||||
return wolfssl_encryption_handshake;
|
||||
case NGTCP2_CRYPTO_LEVEL_APPLICATION:
|
||||
return wolfssl_encryption_application;
|
||||
case NGTCP2_CRYPTO_LEVEL_EARLY:
|
||||
return wolfssl_encryption_early_data;
|
||||
default:
|
||||
assert(0);
|
||||
abort(); /* if NDEBUG is set */
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
|
||||
void *user_data) {
|
||||
(void)conn;
|
||||
(void)user_data;
|
||||
|
||||
DEBUG_MSG("WOLFSSL: get path challenge data\n");
|
||||
if (wolfSSL_RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
|
||||
DEBUG_MSG("WOLFSSL: get random\n");
|
||||
if (wolfSSL_RAND_bytes(data, (int)datalen) != 1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_encryption_secrets(WOLFSSL *ssl,
|
||||
WOLFSSL_ENCRYPTION_LEVEL wolfssl_level,
|
||||
const uint8_t *rx_secret,
|
||||
const uint8_t *tx_secret, size_t secretlen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level);
|
||||
|
||||
DEBUG_MSG("WOLFSSL: set encryption secrets, level=%d, rxlen=%lu, txlen=%lu\n",
|
||||
wolfssl_level, rx_secret ? secretlen : 0,
|
||||
tx_secret ? secretlen : 0);
|
||||
if (rx_secret &&
|
||||
ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
|
||||
rx_secret, secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tx_secret &&
|
||||
ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
|
||||
tx_secret, secretlen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_handshake_data(WOLFSSL *ssl,
|
||||
WOLFSSL_ENCRYPTION_LEVEL wolfssl_level,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
ngtcp2_crypto_level level =
|
||||
ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level);
|
||||
int rv;
|
||||
|
||||
DEBUG_MSG("WOLFSSL: add handshake data, level=%d len=%lu\n", wolfssl_level,
|
||||
datalen);
|
||||
rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
|
||||
if (rv != 0) {
|
||||
ngtcp2_conn_set_tls_error(conn, rv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int flush_flight(WOLFSSL *ssl) {
|
||||
(void)ssl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int send_alert(WOLFSSL *ssl, enum wolfssl_encryption_level_t level,
|
||||
uint8_t alert) {
|
||||
ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
|
||||
ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
|
||||
(void)level;
|
||||
|
||||
DEBUG_MSG("WOLFSSL: send alert, level=%d alert=%d\n", level, alert);
|
||||
ngtcp2_conn_set_tls_alert(conn, alert);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static WOLFSSL_QUIC_METHOD quic_method = {
|
||||
set_encryption_secrets,
|
||||
add_handshake_data,
|
||||
flush_flight,
|
||||
send_alert,
|
||||
};
|
||||
|
||||
static void crypto_wolfssl_configure_context(WOLFSSL_CTX *ssl_ctx) {
|
||||
wolfSSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
wolfSSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
wolfSSL_CTX_set_quic_method(ssl_ctx, &quic_method);
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx) {
|
||||
crypto_wolfssl_configure_context(ssl_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx) {
|
||||
crypto_wolfssl_configure_context(ssl_ctx);
|
||||
wolfSSL_CTX_UseSessionTicket(ssl_ctx);
|
||||
return 0;
|
||||
}
|
||||
+1895
-905
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -36,7 +36,7 @@
|
||||
*
|
||||
* Version number of the ngtcp2 library release.
|
||||
*/
|
||||
#define NGTCP2_VERSION "0.1.0-DEV"
|
||||
#define NGTCP2_VERSION "0.8.1"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -46,6 +46,6 @@
|
||||
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
|
||||
* becomes 0x010203.
|
||||
*/
|
||||
#define NGTCP2_VERSION_NUM 0x000100
|
||||
#define NGTCP2_VERSION_NUM 0x000801
|
||||
|
||||
#endif /* VERSION_H */
|
||||
+48
-31
@@ -26,22 +26,31 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
|
||||
ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) {
|
||||
*ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry));
|
||||
#include "ngtcp2_macro.h"
|
||||
|
||||
static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num,
|
||||
ngtcp2_tstamp tstamp) {
|
||||
ent->pkt_num = pkt_num;
|
||||
ent->len = 1;
|
||||
ent->tstamp = tstamp;
|
||||
}
|
||||
|
||||
int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
|
||||
ngtcp2_tstamp tstamp,
|
||||
ngtcp2_objalloc *objalloc) {
|
||||
*ent = ngtcp2_objalloc_acktr_entry_get(objalloc);
|
||||
if (*ent == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
(*ent)->pkt_num = pkt_num;
|
||||
(*ent)->len = 1;
|
||||
(*ent)->tstamp = tstamp;
|
||||
acktr_entry_init(*ent, pkt_num, tstamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) {
|
||||
ngtcp2_mem_free(mem, ent);
|
||||
void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
|
||||
ngtcp2_objalloc *objalloc) {
|
||||
ngtcp2_objalloc_acktr_entry_release(objalloc, ent);
|
||||
}
|
||||
|
||||
static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
|
||||
@@ -52,17 +61,15 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
|
||||
const ngtcp2_mem *mem) {
|
||||
int rv;
|
||||
|
||||
rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry),
|
||||
ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem);
|
||||
|
||||
rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry),
|
||||
mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
|
||||
if (rv != 0) {
|
||||
ngtcp2_ringbuf_free(&acktr->acks);
|
||||
return rv;
|
||||
}
|
||||
ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
|
||||
|
||||
acktr->log = log;
|
||||
acktr->mem = mem;
|
||||
@@ -74,24 +81,31 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
|
||||
}
|
||||
|
||||
void ngtcp2_acktr_free(ngtcp2_acktr *acktr) {
|
||||
#ifdef NOMEMPOOL
|
||||
ngtcp2_ksl_it it;
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
if (acktr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NOMEMPOOL
|
||||
for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it);
|
||||
ngtcp2_ksl_it_next(&it)) {
|
||||
ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem);
|
||||
ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc);
|
||||
}
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
ngtcp2_ksl_free(&acktr->ents);
|
||||
|
||||
ngtcp2_ringbuf_free(&acktr->acks);
|
||||
|
||||
ngtcp2_objalloc_free(&acktr->objalloc);
|
||||
}
|
||||
|
||||
int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_ksl_it it;
|
||||
ngtcp2_ksl_it it, prev_it;
|
||||
ngtcp2_acktr_entry *ent, *prev_ent, *delent;
|
||||
int rv;
|
||||
int added = 0;
|
||||
@@ -122,16 +136,17 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
|
||||
added = 1;
|
||||
}
|
||||
} else {
|
||||
ngtcp2_ksl_it_prev(&it);
|
||||
prev_ent = ngtcp2_ksl_it_get(&it);
|
||||
prev_it = it;
|
||||
ngtcp2_ksl_it_prev(&prev_it);
|
||||
prev_ent = ngtcp2_ksl_it_get(&prev_it);
|
||||
|
||||
assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len);
|
||||
|
||||
if (ent->pkt_num + 1 == pkt_num) {
|
||||
if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
|
||||
prev_ent->len += ent->len + 1;
|
||||
ngtcp2_ksl_remove(&acktr->ents, NULL, &ent->pkt_num);
|
||||
ngtcp2_acktr_entry_del(ent, acktr->mem);
|
||||
ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num);
|
||||
ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
|
||||
added = 1;
|
||||
} else {
|
||||
ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
|
||||
@@ -149,13 +164,13 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem);
|
||||
rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent);
|
||||
if (rv != 0) {
|
||||
ngtcp2_acktr_entry_del(ent, acktr->mem);
|
||||
ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@@ -171,8 +186,8 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
|
||||
it = ngtcp2_ksl_end(&acktr->ents);
|
||||
ngtcp2_ksl_it_prev(&it);
|
||||
delent = ngtcp2_ksl_it_get(&it);
|
||||
ngtcp2_ksl_remove(&acktr->ents, NULL, &delent->pkt_num);
|
||||
ngtcp2_acktr_entry_del(delent, acktr->mem);
|
||||
ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num);
|
||||
ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -186,8 +201,8 @@ void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
|
||||
|
||||
for (; !ngtcp2_ksl_it_end(&it);) {
|
||||
ent = ngtcp2_ksl_it_get(&it);
|
||||
ngtcp2_ksl_remove(&acktr->ents, &it, &ent->pkt_num);
|
||||
ngtcp2_acktr_entry_del(ent, acktr->mem);
|
||||
ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num);
|
||||
ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,13 +227,14 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
|
||||
}
|
||||
|
||||
/*
|
||||
* acktr_remove removes |ent| from |acktr|. The iterator which points
|
||||
* to the entry next to |ent| is assigned to |it|.
|
||||
* acktr_remove removes |ent| from |acktr|. |it| must point to the
|
||||
* node whose key identifies |ent|. The iterator which points to the
|
||||
* entry next to |ent| is assigned to |it|.
|
||||
*/
|
||||
static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it,
|
||||
ngtcp2_acktr_entry *ent) {
|
||||
ngtcp2_ksl_remove(&acktr->ents, it, &ent->pkt_num);
|
||||
ngtcp2_acktr_entry_del(ent, acktr->mem);
|
||||
ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num);
|
||||
ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
|
||||
}
|
||||
|
||||
static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
|
||||
@@ -310,7 +326,8 @@ void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
|
||||
int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr,
|
||||
ngtcp2_duration max_ack_delay,
|
||||
ngtcp2_tstamp ts) {
|
||||
return acktr->first_unacked_ts <= ts - max_ack_delay;
|
||||
return acktr->first_unacked_ts != UINT64_MAX &&
|
||||
acktr->first_unacked_ts + max_ack_delay <= ts;
|
||||
}
|
||||
|
||||
void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
|
||||
|
||||
+34
-26
@@ -35,6 +35,7 @@
|
||||
#include "ngtcp2_ringbuf.h"
|
||||
#include "ngtcp2_ksl.h"
|
||||
#include "ngtcp2_pkt.h"
|
||||
#include "ngtcp2_objalloc.h"
|
||||
|
||||
/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
|
||||
which ngtcp2_acktr stores. */
|
||||
@@ -46,22 +47,30 @@ typedef struct ngtcp2_log ngtcp2_log;
|
||||
* ngtcp2_acktr_entry is a range of packets which need to be acked.
|
||||
*/
|
||||
typedef struct ngtcp2_acktr_entry {
|
||||
/* pkt_num is the largest packet number to acknowledge in this
|
||||
range. */
|
||||
int64_t pkt_num;
|
||||
/* len is the consecutive packets started from pkt_num which
|
||||
includes pkt_num itself counting in decreasing order. So pkt_num
|
||||
= 987 and len = 2, this entry includes packet 987 and 986. */
|
||||
size_t len;
|
||||
/* tstamp is the timestamp when a packet denoted by pkt_num is
|
||||
received. */
|
||||
ngtcp2_tstamp tstamp;
|
||||
union {
|
||||
struct {
|
||||
/* pkt_num is the largest packet number to acknowledge in this
|
||||
range. */
|
||||
int64_t pkt_num;
|
||||
/* len is the consecutive packets started from pkt_num which
|
||||
includes pkt_num itself counting in decreasing order. So pkt_num
|
||||
= 987 and len = 2, this entry includes packet 987 and 986. */
|
||||
size_t len;
|
||||
/* tstamp is the timestamp when a packet denoted by pkt_num is
|
||||
received. */
|
||||
ngtcp2_tstamp tstamp;
|
||||
};
|
||||
|
||||
ngtcp2_opl_entry oplent;
|
||||
};
|
||||
} ngtcp2_acktr_entry;
|
||||
|
||||
ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent);
|
||||
|
||||
/*
|
||||
* ngtcp2_acktr_entry_new allocates memory for ent, and initializes it
|
||||
* with the given parameters. The pointer to the allocated object is
|
||||
* stored to |*ent|.
|
||||
* ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and
|
||||
* initializes it with the given parameters. The pointer to the
|
||||
* allocated object is stored to |*ent|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -69,14 +78,16 @@ typedef struct ngtcp2_acktr_entry {
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
|
||||
ngtcp2_tstamp tstamp, const ngtcp2_mem *mem);
|
||||
int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
|
||||
ngtcp2_tstamp tstamp,
|
||||
ngtcp2_objalloc *objalloc);
|
||||
|
||||
/*
|
||||
* ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It
|
||||
* deallocates memory pointed by |ent|.
|
||||
* ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for
|
||||
* |ent|.
|
||||
*/
|
||||
void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem);
|
||||
void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
|
||||
ngtcp2_objalloc *objalloc);
|
||||
|
||||
typedef struct ngtcp2_acktr_ack_entry {
|
||||
/* largest_ack is the largest packet number in outgoing ACK frame */
|
||||
@@ -86,25 +97,22 @@ typedef struct ngtcp2_acktr_ack_entry {
|
||||
} ngtcp2_acktr_ack_entry;
|
||||
|
||||
/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */
|
||||
#define NGTCP2_ACKTR_FLAG_NONE 0x00
|
||||
#define NGTCP2_ACKTR_FLAG_NONE 0x00u
|
||||
/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
|
||||
acknowledgement is required. */
|
||||
#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01
|
||||
#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u
|
||||
/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending
|
||||
protected packet to be acknowledged. */
|
||||
#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02
|
||||
/* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received
|
||||
acknowledgement for ACK which acknowledges the last handshake
|
||||
packet from client (which contains TLSv1.3 Finished message). */
|
||||
#define NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK 0x80
|
||||
#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u
|
||||
/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
|
||||
expired and canceled. */
|
||||
#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100
|
||||
#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u
|
||||
|
||||
/*
|
||||
* ngtcp2_acktr tracks received packets which we have to send ack.
|
||||
*/
|
||||
typedef struct ngtcp2_acktr {
|
||||
ngtcp2_objalloc objalloc;
|
||||
ngtcp2_ringbuf acks;
|
||||
/* ents includes ngtcp2_acktr_entry sorted by decreasing order of
|
||||
packet number. */
|
||||
|
||||
Vendored
+22
-36
@@ -27,55 +27,41 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/ip.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr,
|
||||
size_t addrlen, void *user_data) {
|
||||
ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
|
||||
ngtcp2_socklen addrlen) {
|
||||
dest->addrlen = addrlen;
|
||||
dest->addr = (struct sockaddr *)addr;
|
||||
dest->user_data = user_data;
|
||||
dest->addr = (ngtcp2_sockaddr *)addr;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) {
|
||||
dest->addrlen = src->addrlen;
|
||||
if (src->addrlen) {
|
||||
memcpy(dest->addr, src->addr, src->addrlen);
|
||||
memcpy(dest->addr, src->addr, (size_t)src->addrlen);
|
||||
}
|
||||
dest->user_data = src->user_data;
|
||||
}
|
||||
|
||||
void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr,
|
||||
size_t addrlen) {
|
||||
void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
|
||||
ngtcp2_socklen addrlen) {
|
||||
dest->addrlen = addrlen;
|
||||
if (addrlen) {
|
||||
memcpy(dest->addr, addr, addrlen);
|
||||
memcpy(dest->addr, addr, (size_t)addrlen);
|
||||
}
|
||||
}
|
||||
|
||||
static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) {
|
||||
static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
|
||||
assert(a->sa_family == b->sa_family);
|
||||
|
||||
switch (a->sa_family) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a,
|
||||
*bi = (const struct sockaddr_in *)(void *)b;
|
||||
case NGTCP2_AF_INET: {
|
||||
const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
|
||||
*bi = (const ngtcp2_sockaddr_in *)(void *)b;
|
||||
return ai->sin_port == bi->sin_port &&
|
||||
memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
|
||||
*bi = (const struct sockaddr_in6 *)(void *)b;
|
||||
case NGTCP2_AF_INET6: {
|
||||
const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
|
||||
*bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
|
||||
return ai->sin6_port == bi->sin6_port &&
|
||||
memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
|
||||
}
|
||||
@@ -92,17 +78,17 @@ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) {
|
||||
|
||||
uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
|
||||
uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE;
|
||||
const struct sockaddr *a = aa->addr;
|
||||
const struct sockaddr *b = bb->addr;
|
||||
const ngtcp2_sockaddr *a = aa->addr;
|
||||
const ngtcp2_sockaddr *b = bb->addr;
|
||||
|
||||
if (a->sa_family != b->sa_family) {
|
||||
return NGTCP2_ADDR_COMPARE_FLAG_FAMILY;
|
||||
}
|
||||
|
||||
switch (a->sa_family) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a,
|
||||
*bi = (const struct sockaddr_in *)(void *)b;
|
||||
case NGTCP2_AF_INET: {
|
||||
const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
|
||||
*bi = (const ngtcp2_sockaddr_in *)(void *)b;
|
||||
if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
|
||||
flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
|
||||
}
|
||||
@@ -111,9 +97,9 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
|
||||
*bi = (const struct sockaddr_in6 *)(void *)b;
|
||||
case NGTCP2_AF_INET6: {
|
||||
const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
|
||||
*bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
|
||||
if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
|
||||
flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
|
||||
}
|
||||
|
||||
Vendored
+4
-13
@@ -38,30 +38,21 @@
|
||||
*/
|
||||
void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src);
|
||||
|
||||
/*
|
||||
* ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the
|
||||
* buffer pointed by dest->addr. dest->len is updated to have
|
||||
* |addrlen|. This function assumes that dest->addr points to a
|
||||
* buffer which have sufficient size to store the copy.
|
||||
*/
|
||||
void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr,
|
||||
size_t addrlen);
|
||||
|
||||
/*
|
||||
* ngtcp2_addr_eq returns nonzero if |a| equals |b|.
|
||||
*/
|
||||
int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b);
|
||||
|
||||
/* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u
|
||||
/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not
|
||||
match. */
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u
|
||||
/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u
|
||||
/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not
|
||||
match. */
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4
|
||||
#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u
|
||||
|
||||
/*
|
||||
* ngtcp2_addr_compare compares address and port between |a| and |b|,
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "ngtcp2_balloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "ngtcp2_mem.h"
|
||||
|
||||
void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
|
||||
const ngtcp2_mem *mem) {
|
||||
assert((blklen & 0xfu) == 0);
|
||||
|
||||
balloc->mem = mem;
|
||||
balloc->blklen = blklen;
|
||||
balloc->head = NULL;
|
||||
ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
|
||||
}
|
||||
|
||||
void ngtcp2_balloc_free(ngtcp2_balloc *balloc) {
|
||||
if (balloc == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngtcp2_balloc_clear(balloc);
|
||||
}
|
||||
|
||||
void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) {
|
||||
ngtcp2_memblock_hd *p, *next;
|
||||
|
||||
for (p = balloc->head; p; p = next) {
|
||||
next = p->next;
|
||||
ngtcp2_mem_free(balloc->mem, p);
|
||||
}
|
||||
|
||||
balloc->head = NULL;
|
||||
ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
|
||||
}
|
||||
|
||||
int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
|
||||
uint8_t *p;
|
||||
ngtcp2_memblock_hd *hd;
|
||||
|
||||
assert(n <= balloc->blklen);
|
||||
|
||||
if (ngtcp2_buf_left(&balloc->buf) < n) {
|
||||
p = ngtcp2_mem_malloc(balloc->mem,
|
||||
sizeof(ngtcp2_memblock_hd) + 0x10u + balloc->blklen);
|
||||
if (p == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
hd = (ngtcp2_memblock_hd *)(void *)p;
|
||||
hd->next = balloc->head;
|
||||
balloc->head = hd;
|
||||
ngtcp2_buf_init(
|
||||
&balloc->buf,
|
||||
(uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) &
|
||||
~(uintptr_t)0xfu),
|
||||
balloc->blklen);
|
||||
}
|
||||
|
||||
assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
|
||||
|
||||
*pbuf = balloc->buf.last;
|
||||
balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_BALLOC_H
|
||||
#define NGTCP2_BALLOC_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "ngtcp2_buf.h"
|
||||
|
||||
typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd;
|
||||
|
||||
/*
|
||||
* ngtcp2_memblock_hd is the header of memory block.
|
||||
*/
|
||||
struct ngtcp2_memblock_hd {
|
||||
ngtcp2_memblock_hd *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* ngtcp2_balloc is a custom memory allocator. It allocates |blklen|
|
||||
* bytes of memory at once on demand, and returns its slice when the
|
||||
* allocation is requested.
|
||||
*/
|
||||
typedef struct ngtcp2_balloc {
|
||||
/* mem is the underlying memory allocator. */
|
||||
const ngtcp2_mem *mem;
|
||||
/* blklen is the size of memory block. */
|
||||
size_t blklen;
|
||||
/* head points to the list of memory block allocated so far. */
|
||||
ngtcp2_memblock_hd *head;
|
||||
/* buf wraps the current memory block for allocation requests. */
|
||||
ngtcp2_buf buf;
|
||||
} ngtcp2_balloc;
|
||||
|
||||
/*
|
||||
* ngtcp2_balloc_init initializes |balloc| with |blklen| which is the
|
||||
* size of memory block.
|
||||
*/
|
||||
void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_balloc_free releases all allocated memory blocks.
|
||||
*/
|
||||
void ngtcp2_balloc_free(ngtcp2_balloc *balloc);
|
||||
|
||||
/*
|
||||
* ngtcp2_balloc_get allocates |n| bytes of memory and assigns its
|
||||
* pointer to |*pbuf|.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n);
|
||||
|
||||
/*
|
||||
* ngtcp2_balloc_clear releases all allocated memory blocks and
|
||||
* initializes its state.
|
||||
*/
|
||||
void ngtcp2_balloc_clear(ngtcp2_balloc *balloc);
|
||||
|
||||
#endif /* NGTCP2_BALLOC_H */
|
||||
Vendored
+692
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2021 ngtcp2 contributors
|
||||
*
|
||||
* 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 "ngtcp2_bbr.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "ngtcp2_log.h"
|
||||
#include "ngtcp2_macro.h"
|
||||
#include "ngtcp2_mem.h"
|
||||
#include "ngtcp2_rcvry.h"
|
||||
#include "ngtcp2_rst.h"
|
||||
|
||||
static const double pacing_gain_cycle[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
|
||||
|
||||
#define NGTCP2_BBR_GAIN_CYCLELEN \
|
||||
(sizeof(pacing_gain_cycle) / sizeof(pacing_gain_cycle[0]))
|
||||
|
||||
#define NGTCP2_BBR_HIGH_GAIN 2.89
|
||||
#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS)
|
||||
#define NGTCP2_RTPROP_FILTERLEN (10 * NGTCP2_SECONDS)
|
||||
#define NGTCP2_BBR_BTL_BW_FILTERLEN 10
|
||||
|
||||
static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
|
||||
static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack,
|
||||
ngtcp2_tstamp ts);
|
||||
static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack);
|
||||
static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_init_round_counting(ngtcp2_bbr_cc *cc);
|
||||
static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack);
|
||||
static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack);
|
||||
static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
double pacing_gain);
|
||||
static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack);
|
||||
static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
|
||||
static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat);
|
||||
static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack);
|
||||
static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp initial_ts);
|
||||
static void bbr_enter_startup(ngtcp2_bbr_cc *cc);
|
||||
static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc);
|
||||
static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc);
|
||||
static void bbr_enter_drain(ngtcp2_bbr_cc *cc);
|
||||
static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
|
||||
static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
|
||||
static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
|
||||
static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
|
||||
static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat);
|
||||
static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc);
|
||||
static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
|
||||
ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
|
||||
ngtcp2_log *log) {
|
||||
cc->ccb.log = log;
|
||||
cc->rst = rst;
|
||||
cc->rand = rand;
|
||||
cc->rand_ctx = *rand_ctx;
|
||||
cc->initial_cwnd = cstat->cwnd;
|
||||
bbr_init(cc, cstat, initial_ts);
|
||||
}
|
||||
|
||||
void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc) { (void)cc; }
|
||||
|
||||
int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
|
||||
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
|
||||
ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
|
||||
const ngtcp2_rand_ctx *rand_ctx,
|
||||
const ngtcp2_mem *mem) {
|
||||
ngtcp2_bbr_cc *bbr_cc;
|
||||
|
||||
bbr_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr_cc));
|
||||
if (bbr_cc == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
ngtcp2_bbr_cc_init(bbr_cc, cstat, rst, initial_ts, rand, rand_ctx, log);
|
||||
|
||||
cc->ccb = &bbr_cc->ccb;
|
||||
cc->on_pkt_acked = ngtcp2_cc_bbr_cc_on_pkt_acked;
|
||||
cc->congestion_event = ngtcp2_cc_bbr_cc_congestion_event;
|
||||
cc->on_spurious_congestion = ngtcp2_cc_bbr_cc_on_spurious_congestion;
|
||||
cc->on_persistent_congestion = ngtcp2_cc_bbr_cc_on_persistent_congestion;
|
||||
cc->on_ack_recv = ngtcp2_cc_bbr_cc_on_ack_recv;
|
||||
cc->on_pkt_sent = ngtcp2_cc_bbr_cc_on_pkt_sent;
|
||||
cc->new_rtt_sample = ngtcp2_cc_bbr_cc_new_rtt_sample;
|
||||
cc->reset = ngtcp2_cc_bbr_cc_reset;
|
||||
cc->event = ngtcp2_cc_bbr_cc_event;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
|
||||
ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr_cc, ccb);
|
||||
|
||||
ngtcp2_bbr_cc_free(bbr_cc);
|
||||
ngtcp2_mem_free(mem, bbr_cc);
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
|
||||
(void)ccx;
|
||||
(void)cstat;
|
||||
(void)pkt;
|
||||
(void)ts;
|
||||
}
|
||||
|
||||
static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp sent_time) {
|
||||
return cstat->congestion_recovery_start_ts != UINT64_MAX &&
|
||||
sent_time <= cstat->congestion_recovery_start_ts;
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp sent_ts,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
|
||||
|
||||
if (cc->in_loss_recovery || cc->congestion_recovery_start_ts != UINT64_MAX ||
|
||||
in_congestion_recovery(cstat, sent_ts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cc->congestion_recovery_start_ts = ts;
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
|
||||
(void)ts;
|
||||
|
||||
cc->congestion_recovery_start_ts = UINT64_MAX;
|
||||
cstat->congestion_recovery_start_ts = UINT64_MAX;
|
||||
|
||||
if (cc->in_loss_recovery) {
|
||||
cc->in_loss_recovery = 0;
|
||||
cc->packet_conservation = 0;
|
||||
bbr_restore_cwnd(cc, cstat);
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
|
||||
(void)ts;
|
||||
|
||||
cstat->congestion_recovery_start_ts = UINT64_MAX;
|
||||
cc->congestion_recovery_start_ts = UINT64_MAX;
|
||||
cc->in_loss_recovery = 0;
|
||||
cc->packet_conservation = 0;
|
||||
|
||||
bbr_save_cwnd(cc, cstat);
|
||||
cstat->cwnd = 2 * cstat->max_udp_payload_size;
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
|
||||
ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
|
||||
|
||||
bbr_update_on_ack(bbr_cc, cstat, ack, ts);
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt) {
|
||||
ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
|
||||
(void)pkt;
|
||||
|
||||
bbr_on_transmit(bbr_cc, cstat);
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
(void)ccx;
|
||||
(void)cstat;
|
||||
(void)ts;
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
|
||||
bbr_init(bbr_cc, cstat, ts);
|
||||
}
|
||||
|
||||
void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
|
||||
(void)ccx;
|
||||
(void)cstat;
|
||||
(void)event;
|
||||
(void)ts;
|
||||
}
|
||||
|
||||
static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
|
||||
bbr_update_model_and_state(cc, cstat, ack, ts);
|
||||
bbr_update_control_parameters(cc, cstat, ack);
|
||||
}
|
||||
|
||||
static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack,
|
||||
ngtcp2_tstamp ts) {
|
||||
bbr_update_btl_bw(cc, cstat, ack);
|
||||
bbr_check_cycle_phase(cc, cstat, ack, ts);
|
||||
bbr_check_full_pipe(cc);
|
||||
bbr_check_drain(cc, cstat, ts);
|
||||
bbr_update_rtprop(cc, cstat, ts);
|
||||
bbr_check_probe_rtt(cc, cstat, ts);
|
||||
}
|
||||
|
||||
static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack) {
|
||||
bbr_set_pacing_rate(cc, cstat);
|
||||
bbr_set_send_quantum(cc, cstat);
|
||||
bbr_set_cwnd(cc, cstat, ack);
|
||||
}
|
||||
|
||||
static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
bbr_handle_restart_from_idle(cc, cstat);
|
||||
}
|
||||
|
||||
static void bbr_init_round_counting(ngtcp2_bbr_cc *cc) {
|
||||
cc->next_round_delivered = 0;
|
||||
cc->round_start = 0;
|
||||
cc->round_count = 0;
|
||||
}
|
||||
|
||||
static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) {
|
||||
if (ack->pkt_delivered >= cc->next_round_delivered) {
|
||||
cc->next_round_delivered = cc->rst->delivered;
|
||||
++cc->round_count;
|
||||
cc->round_start = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cc->round_start = 0;
|
||||
}
|
||||
|
||||
static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack) {
|
||||
if (cc->in_loss_recovery) {
|
||||
if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) {
|
||||
cc->packet_conservation = 0;
|
||||
}
|
||||
|
||||
if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) {
|
||||
cc->in_loss_recovery = 0;
|
||||
cc->packet_conservation = 0;
|
||||
bbr_restore_cwnd(cc, cstat);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (cc->congestion_recovery_start_ts != UINT64_MAX) {
|
||||
cc->in_loss_recovery = 1;
|
||||
bbr_save_cwnd(cc, cstat);
|
||||
cstat->cwnd = cstat->bytes_in_flight +
|
||||
ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size);
|
||||
|
||||
cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts;
|
||||
cc->congestion_recovery_start_ts = UINT64_MAX;
|
||||
cc->packet_conservation = 1;
|
||||
cc->congestion_recovery_next_round_delivered = cc->rst->delivered;
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack) {
|
||||
bbr_update_round(cc, ack);
|
||||
bbr_handle_recovery(cc, cstat, ack);
|
||||
|
||||
if (cstat->delivery_rate_sec < cc->btl_bw && cc->rst->rs.is_app_limited) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngtcp2_window_filter_update(&cc->btl_bw_filter, cstat->delivery_rate_sec,
|
||||
cc->round_count);
|
||||
|
||||
cc->btl_bw = ngtcp2_window_filter_get_best(&cc->btl_bw_filter);
|
||||
}
|
||||
|
||||
static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
cc->rtprop_expired = ts > cc->rtprop_stamp + NGTCP2_RTPROP_FILTERLEN;
|
||||
|
||||
/* Need valid RTT sample */
|
||||
if (cstat->latest_rtt &&
|
||||
(cstat->latest_rtt <= cc->rt_prop || cc->rtprop_expired)) {
|
||||
cc->rt_prop = cstat->latest_rtt;
|
||||
cc->rtprop_stamp = ts;
|
||||
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"bbr update RTprop=%" PRIu64, cc->rt_prop);
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
double nominal_bandwidth =
|
||||
(double)cc->initial_cwnd / (double)NGTCP2_MILLISECONDS;
|
||||
|
||||
cstat->pacing_rate = cc->pacing_gain * nominal_bandwidth;
|
||||
}
|
||||
|
||||
static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
double pacing_gain) {
|
||||
double rate = pacing_gain * (double)cc->btl_bw / NGTCP2_SECONDS;
|
||||
|
||||
if (cc->filled_pipe || rate > cstat->pacing_rate) {
|
||||
cstat->pacing_rate = rate;
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
bbr_set_pacing_rate_with_gain(cc, cstat, cc->pacing_gain);
|
||||
}
|
||||
|
||||
static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
uint64_t send_quantum;
|
||||
(void)cc;
|
||||
|
||||
if (cstat->pacing_rate < 1.2 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
|
||||
cstat->send_quantum = cstat->max_udp_payload_size;
|
||||
} else if (cstat->pacing_rate < 24.0 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
|
||||
cstat->send_quantum = cstat->max_udp_payload_size * 2;
|
||||
} else {
|
||||
send_quantum =
|
||||
(uint64_t)(cstat->pacing_rate * (double)(cstat->min_rtt == UINT64_MAX
|
||||
? NGTCP2_MILLISECONDS
|
||||
: cstat->min_rtt));
|
||||
cstat->send_quantum = (size_t)ngtcp2_min(send_quantum, 64 * 1024);
|
||||
}
|
||||
|
||||
cstat->send_quantum =
|
||||
ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10);
|
||||
}
|
||||
|
||||
static uint64_t bbr_inflight(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
double gain) {
|
||||
uint64_t quanta = 3 * cstat->send_quantum;
|
||||
double estimated_bdp;
|
||||
|
||||
if (cc->rt_prop == UINT64_MAX) {
|
||||
/* no valid RTT samples yet */
|
||||
return cc->initial_cwnd;
|
||||
}
|
||||
|
||||
estimated_bdp = (double)cc->btl_bw * (double)cc->rt_prop / NGTCP2_SECONDS;
|
||||
|
||||
return (uint64_t)(gain * estimated_bdp) + quanta;
|
||||
}
|
||||
|
||||
static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
cc->target_cwnd = bbr_inflight(cc, cstat, cc->cwnd_gain);
|
||||
}
|
||||
|
||||
static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack) {
|
||||
if (ack->bytes_lost > 0) {
|
||||
if (cstat->cwnd > ack->bytes_lost) {
|
||||
cstat->cwnd -= ack->bytes_lost;
|
||||
cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size);
|
||||
} else {
|
||||
cstat->cwnd = cstat->max_udp_payload_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (cc->packet_conservation) {
|
||||
cstat->cwnd =
|
||||
ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered);
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
if (!cc->in_loss_recovery && cc->state != NGTCP2_BBR_STATE_PROBE_RTT) {
|
||||
cc->prior_cwnd = cstat->cwnd;
|
||||
return;
|
||||
}
|
||||
|
||||
cc->prior_cwnd = ngtcp2_max(cc->prior_cwnd, cstat->cwnd);
|
||||
}
|
||||
|
||||
static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
|
||||
cstat->cwnd = ngtcp2_max(cstat->cwnd, cc->prior_cwnd);
|
||||
}
|
||||
|
||||
static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
|
||||
return max_udp_payload_size * 4;
|
||||
}
|
||||
|
||||
static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat) {
|
||||
if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
|
||||
cstat->cwnd =
|
||||
ngtcp2_min(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack) {
|
||||
bbr_update_target_cwnd(cc, cstat);
|
||||
bbr_modulate_cwnd_for_recovery(cc, cstat, ack);
|
||||
|
||||
if (!cc->packet_conservation) {
|
||||
if (cc->filled_pipe) {
|
||||
cstat->cwnd =
|
||||
ngtcp2_min(cstat->cwnd + ack->bytes_delivered, cc->target_cwnd);
|
||||
} else if (cstat->cwnd < cc->target_cwnd ||
|
||||
cc->rst->delivered < cc->initial_cwnd) {
|
||||
cstat->cwnd += ack->bytes_delivered;
|
||||
}
|
||||
|
||||
cstat->cwnd =
|
||||
ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
|
||||
}
|
||||
|
||||
bbr_modulate_cwnd_for_probe_rtt(cc, cstat);
|
||||
}
|
||||
|
||||
static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp initial_ts) {
|
||||
cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
|
||||
cc->prior_cwnd = 0;
|
||||
cc->target_cwnd = 0;
|
||||
cc->btl_bw = 0;
|
||||
cc->rt_prop = UINT64_MAX;
|
||||
cc->rtprop_stamp = initial_ts;
|
||||
cc->cycle_stamp = UINT64_MAX;
|
||||
cc->probe_rtt_done_stamp = UINT64_MAX;
|
||||
cc->cycle_index = 0;
|
||||
cc->rtprop_expired = 0;
|
||||
cc->idle_restart = 0;
|
||||
cc->packet_conservation = 0;
|
||||
cc->probe_rtt_round_done = 0;
|
||||
|
||||
cc->congestion_recovery_start_ts = UINT64_MAX;
|
||||
cc->congestion_recovery_next_round_delivered = 0;
|
||||
cc->in_loss_recovery = 0;
|
||||
|
||||
cstat->send_quantum = cstat->max_udp_payload_size * 10;
|
||||
|
||||
ngtcp2_window_filter_init(&cc->btl_bw_filter, NGTCP2_BBR_BTL_BW_FILTERLEN);
|
||||
|
||||
bbr_init_round_counting(cc);
|
||||
bbr_init_full_pipe(cc);
|
||||
bbr_init_pacing_rate(cc, cstat);
|
||||
bbr_enter_startup(cc);
|
||||
}
|
||||
|
||||
static void bbr_enter_startup(ngtcp2_bbr_cc *cc) {
|
||||
cc->state = NGTCP2_BBR_STATE_STARTUP;
|
||||
cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
|
||||
cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
|
||||
}
|
||||
|
||||
static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc) {
|
||||
cc->filled_pipe = 0;
|
||||
cc->full_bw = 0;
|
||||
cc->full_bw_count = 0;
|
||||
}
|
||||
|
||||
static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc) {
|
||||
if (cc->filled_pipe || !cc->round_start || cc->rst->rs.is_app_limited) {
|
||||
/* no need to check for a full pipe now. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* cc->btl_bw still growing? */
|
||||
if (cc->btl_bw * 100 >= cc->full_bw * 125) {
|
||||
/* record new baseline level */
|
||||
cc->full_bw = cc->btl_bw;
|
||||
cc->full_bw_count = 0;
|
||||
return;
|
||||
}
|
||||
/* another round w/o much growth */
|
||||
++cc->full_bw_count;
|
||||
if (cc->full_bw_count >= 3) {
|
||||
cc->filled_pipe = 1;
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"bbr filled pipe, btl_bw=%" PRIu64, cc->btl_bw);
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_enter_drain(ngtcp2_bbr_cc *cc) {
|
||||
cc->state = NGTCP2_BBR_STATE_DRAIN;
|
||||
/* pace slowly */
|
||||
cc->pacing_gain = 1.0 / NGTCP2_BBR_HIGH_GAIN;
|
||||
/* maintain cwnd */
|
||||
cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
|
||||
}
|
||||
|
||||
static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
if (cc->state == NGTCP2_BBR_STATE_STARTUP && cc->filled_pipe) {
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"bbr exit Startup and enter Drain");
|
||||
|
||||
bbr_enter_drain(cc);
|
||||
}
|
||||
|
||||
if (cc->state == NGTCP2_BBR_STATE_DRAIN &&
|
||||
cstat->bytes_in_flight <= bbr_inflight(cc, cstat, 1.0)) {
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"bbr exit Drain and enter ProbeBW");
|
||||
|
||||
/* we estimate queue is drained */
|
||||
bbr_enter_probe_bw(cc, ts);
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
|
||||
uint8_t rand;
|
||||
|
||||
cc->state = NGTCP2_BBR_STATE_PROBE_BW;
|
||||
cc->pacing_gain = 1;
|
||||
cc->cwnd_gain = 2;
|
||||
|
||||
assert(cc->rand);
|
||||
|
||||
cc->rand(&rand, 1, &cc->rand_ctx);
|
||||
|
||||
cc->cycle_index = NGTCP2_BBR_GAIN_CYCLELEN - 1 - (size_t)(rand * 7 / 256);
|
||||
bbr_advance_cycle_phase(cc, ts);
|
||||
}
|
||||
|
||||
static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
|
||||
if (cc->state == NGTCP2_BBR_STATE_PROBE_BW &&
|
||||
bbr_is_next_cycle_phase(cc, cstat, ack, ts)) {
|
||||
bbr_advance_cycle_phase(cc, ts);
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
|
||||
cc->cycle_stamp = ts;
|
||||
cc->cycle_index = (cc->cycle_index + 1) & (NGTCP2_BBR_GAIN_CYCLELEN - 1);
|
||||
cc->pacing_gain = pacing_gain_cycle[cc->cycle_index];
|
||||
}
|
||||
|
||||
static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
|
||||
int is_full_length = (ts - cc->cycle_stamp) > cc->rt_prop;
|
||||
|
||||
if (cc->pacing_gain > 1) {
|
||||
return is_full_length && (ack->bytes_lost > 0 ||
|
||||
ack->prior_bytes_in_flight >=
|
||||
bbr_inflight(cc, cstat, cc->pacing_gain));
|
||||
}
|
||||
|
||||
if (cc->pacing_gain < 1) {
|
||||
return is_full_length ||
|
||||
ack->prior_bytes_in_flight <= bbr_inflight(cc, cstat, 1);
|
||||
}
|
||||
|
||||
return is_full_length;
|
||||
}
|
||||
|
||||
static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
|
||||
ngtcp2_conn_stat *cstat) {
|
||||
if (cstat->bytes_in_flight == 0 && cc->rst->app_limited) {
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr restart from idle");
|
||||
|
||||
cc->idle_restart = 1;
|
||||
|
||||
if (cc->state == NGTCP2_BBR_STATE_PROBE_BW) {
|
||||
bbr_set_pacing_rate_with_gain(cc, cstat, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
if (cc->state != NGTCP2_BBR_STATE_PROBE_RTT && cc->rtprop_expired &&
|
||||
!cc->idle_restart) {
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr enter ProbeRTT");
|
||||
|
||||
bbr_enter_probe_rtt(cc);
|
||||
bbr_save_cwnd(cc, cstat);
|
||||
cc->probe_rtt_done_stamp = UINT64_MAX;
|
||||
}
|
||||
|
||||
if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
|
||||
bbr_handle_probe_rtt(cc, cstat, ts);
|
||||
}
|
||||
|
||||
cc->idle_restart = 0;
|
||||
}
|
||||
|
||||
static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc) {
|
||||
cc->state = NGTCP2_BBR_STATE_PROBE_RTT;
|
||||
cc->pacing_gain = 1;
|
||||
cc->cwnd_gain = 1;
|
||||
}
|
||||
|
||||
static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
uint64_t app_limited = cc->rst->delivered + cstat->bytes_in_flight;
|
||||
|
||||
/* Ignore low rate samples during NGTCP2_BBR_STATE_PROBE_RTT. */
|
||||
cc->rst->app_limited = app_limited ? app_limited : 1;
|
||||
|
||||
if (cc->probe_rtt_done_stamp == UINT64_MAX &&
|
||||
cstat->bytes_in_flight <= min_pipe_cwnd(cstat->max_udp_payload_size)) {
|
||||
cc->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION;
|
||||
cc->probe_rtt_round_done = 0;
|
||||
cc->next_round_delivered = cc->rst->delivered;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (cc->probe_rtt_done_stamp != UINT64_MAX) {
|
||||
if (cc->round_start) {
|
||||
cc->probe_rtt_round_done = 1;
|
||||
}
|
||||
|
||||
if (cc->probe_rtt_round_done && ts > cc->probe_rtt_done_stamp) {
|
||||
cc->rtprop_stamp = ts;
|
||||
bbr_restore_cwnd(cc, cstat);
|
||||
bbr_exit_probe_rtt(cc, ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
|
||||
if (cc->filled_pipe) {
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"bbr exit ProbeRTT and enter ProbeBW");
|
||||
|
||||
bbr_enter_probe_bw(cc, ts);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"bbr exit ProbeRTT and enter Startup");
|
||||
|
||||
bbr_enter_startup(cc);
|
||||
}
|
||||
Vendored
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2021 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_BBR_H
|
||||
#define NGTCP2_BBR_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "ngtcp2_cc.h"
|
||||
#include "ngtcp2_window_filter.h"
|
||||
|
||||
typedef struct ngtcp2_rst ngtcp2_rst;
|
||||
|
||||
typedef enum ngtcp2_bbr_state {
|
||||
NGTCP2_BBR_STATE_STARTUP,
|
||||
NGTCP2_BBR_STATE_DRAIN,
|
||||
NGTCP2_BBR_STATE_PROBE_BW,
|
||||
NGTCP2_BBR_STATE_PROBE_RTT,
|
||||
} ngtcp2_bbr_state;
|
||||
|
||||
/*
|
||||
* ngtcp2_bbr_cc is BBR congestion controller, described in
|
||||
* https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00
|
||||
*/
|
||||
typedef struct ngtcp2_bbr_cc {
|
||||
ngtcp2_cc_base ccb;
|
||||
|
||||
/* The max filter used to estimate BBR.BtlBw. */
|
||||
ngtcp2_window_filter btl_bw_filter;
|
||||
uint64_t initial_cwnd;
|
||||
ngtcp2_rst *rst;
|
||||
ngtcp2_rand rand;
|
||||
ngtcp2_rand_ctx rand_ctx;
|
||||
|
||||
/* BBR variables */
|
||||
|
||||
/* The dynamic gain factor used to scale BBR.BtlBw to
|
||||
produce BBR.pacing_rate. */
|
||||
double pacing_gain;
|
||||
/* The dynamic gain factor used to scale the estimated BDP to produce a
|
||||
congestion window (cwnd). */
|
||||
double cwnd_gain;
|
||||
uint64_t full_bw;
|
||||
/* packet.delivered value denoting the end of a packet-timed round trip. */
|
||||
uint64_t next_round_delivered;
|
||||
/* Count of packet-timed round trips. */
|
||||
uint64_t round_count;
|
||||
uint64_t prior_cwnd;
|
||||
/* target_cwnd is the upper bound on the volume of data BBR
|
||||
allows in flight. */
|
||||
uint64_t target_cwnd;
|
||||
/* BBR's estimated bottleneck bandwidth available to the
|
||||
transport flow, estimated from the maximum delivery rate sample in a
|
||||
sliding window. */
|
||||
uint64_t btl_bw;
|
||||
/* BBR's estimated two-way round-trip propagation delay of
|
||||
the path, estimated from the windowed minimum recent round-trip delay
|
||||
sample. */
|
||||
ngtcp2_duration rt_prop;
|
||||
/* The wall clock time at which the current BBR.RTProp
|
||||
sample was obtained. */
|
||||
ngtcp2_tstamp rtprop_stamp;
|
||||
ngtcp2_tstamp cycle_stamp;
|
||||
ngtcp2_tstamp probe_rtt_done_stamp;
|
||||
/* congestion_recovery_start_ts is the time when congestion recovery
|
||||
period started.*/
|
||||
ngtcp2_tstamp congestion_recovery_start_ts;
|
||||
uint64_t congestion_recovery_next_round_delivered;
|
||||
size_t full_bw_count;
|
||||
size_t cycle_index;
|
||||
ngtcp2_bbr_state state;
|
||||
/* A boolean that records whether BBR estimates that it has ever fully
|
||||
utilized its available bandwidth ("filled the pipe"). */
|
||||
int filled_pipe;
|
||||
/* A boolean that BBR sets to true once per packet-timed round trip,
|
||||
on ACKs that advance BBR.round_count. */
|
||||
int round_start;
|
||||
int rtprop_expired;
|
||||
int idle_restart;
|
||||
int packet_conservation;
|
||||
int probe_rtt_round_done;
|
||||
/* in_loss_recovery becomes nonzero when BBR enters loss recovery
|
||||
period. */
|
||||
int in_loss_recovery;
|
||||
} ngtcp2_bbr_cc;
|
||||
|
||||
int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
|
||||
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
|
||||
ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
|
||||
const ngtcp2_rand_ctx *rand_ctx,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
|
||||
|
||||
void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *bbr_cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
|
||||
ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
|
||||
ngtcp2_log *log);
|
||||
|
||||
void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
|
||||
|
||||
#endif /* NGTCP2_BBR_H */
|
||||
Vendored
+1486
File diff suppressed because it is too large.
Load diff
Vendored
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2021 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_BBR2_H
|
||||
#define NGTCP2_BBR2_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "ngtcp2_cc.h"
|
||||
#include "ngtcp2_window_filter.h"
|
||||
|
||||
typedef struct ngtcp2_rst ngtcp2_rst;
|
||||
|
||||
typedef enum ngtcp2_bbr2_state {
|
||||
NGTCP2_BBR2_STATE_STARTUP,
|
||||
NGTCP2_BBR2_STATE_DRAIN,
|
||||
NGTCP2_BBR2_STATE_PROBE_BW_DOWN,
|
||||
NGTCP2_BBR2_STATE_PROBE_BW_CRUISE,
|
||||
NGTCP2_BBR2_STATE_PROBE_BW_REFILL,
|
||||
NGTCP2_BBR2_STATE_PROBE_BW_UP,
|
||||
NGTCP2_BBR2_STATE_PROBE_RTT,
|
||||
} ngtcp2_bbr2_state;
|
||||
|
||||
typedef enum ngtcp2_bbr2_ack_phase {
|
||||
NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING,
|
||||
NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING,
|
||||
NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK,
|
||||
NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING,
|
||||
} ngtcp2_bbr2_ack_phase;
|
||||
|
||||
/*
|
||||
* ngtcp2_bbr2_cc is BBR v2 congestion controller, described in
|
||||
* https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control-01
|
||||
*/
|
||||
typedef struct ngtcp2_bbr2_cc {
|
||||
ngtcp2_cc_base ccb;
|
||||
|
||||
uint64_t initial_cwnd;
|
||||
ngtcp2_rst *rst;
|
||||
ngtcp2_rand rand;
|
||||
ngtcp2_rand_ctx rand_ctx;
|
||||
|
||||
/* max_bw_filter for tracking the maximum recent delivery rate
|
||||
samples for estimating max_bw. */
|
||||
ngtcp2_window_filter max_bw_filter;
|
||||
|
||||
ngtcp2_window_filter extra_acked_filter;
|
||||
|
||||
ngtcp2_duration min_rtt;
|
||||
ngtcp2_tstamp min_rtt_stamp;
|
||||
ngtcp2_tstamp probe_rtt_done_stamp;
|
||||
int probe_rtt_round_done;
|
||||
uint64_t prior_cwnd;
|
||||
int idle_restart;
|
||||
ngtcp2_tstamp extra_acked_interval_start;
|
||||
uint64_t extra_acked_delivered;
|
||||
|
||||
/* Congestion signals */
|
||||
int loss_in_round;
|
||||
uint64_t bw_latest;
|
||||
uint64_t inflight_latest;
|
||||
|
||||
/* Lower bounds */
|
||||
uint64_t bw_lo;
|
||||
uint64_t inflight_lo;
|
||||
|
||||
/* Round counting */
|
||||
uint64_t next_round_delivered;
|
||||
int round_start;
|
||||
uint64_t round_count;
|
||||
|
||||
/* Full pipe */
|
||||
int filled_pipe;
|
||||
uint64_t full_bw;
|
||||
size_t full_bw_count;
|
||||
|
||||
/* Pacing rate */
|
||||
double pacing_gain;
|
||||
|
||||
ngtcp2_bbr2_state state;
|
||||
double cwnd_gain;
|
||||
|
||||
int loss_round_start;
|
||||
uint64_t loss_round_delivered;
|
||||
uint64_t rounds_since_bw_probe;
|
||||
uint64_t max_bw;
|
||||
uint64_t bw;
|
||||
uint64_t cycle_count;
|
||||
uint64_t extra_acked;
|
||||
uint64_t bytes_lost_in_round;
|
||||
size_t loss_events_in_round;
|
||||
uint64_t offload_budget;
|
||||
uint64_t probe_up_cnt;
|
||||
ngtcp2_tstamp cycle_stamp;
|
||||
ngtcp2_bbr2_ack_phase ack_phase;
|
||||
ngtcp2_duration bw_probe_wait;
|
||||
int bw_probe_samples;
|
||||
size_t bw_probe_up_rounds;
|
||||
uint64_t bw_probe_up_acks;
|
||||
uint64_t inflight_hi;
|
||||
uint64_t bw_hi;
|
||||
int probe_rtt_expired;
|
||||
ngtcp2_duration probe_rtt_min_delay;
|
||||
ngtcp2_tstamp probe_rtt_min_stamp;
|
||||
int in_loss_recovery;
|
||||
int packet_conservation;
|
||||
uint64_t max_inflight;
|
||||
ngtcp2_tstamp congestion_recovery_start_ts;
|
||||
uint64_t congestion_recovery_next_round_delivered;
|
||||
|
||||
uint64_t prior_inflight_lo;
|
||||
uint64_t prior_inflight_hi;
|
||||
uint64_t prior_bw_lo;
|
||||
} ngtcp2_bbr2_cc;
|
||||
|
||||
int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
|
||||
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
|
||||
ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
|
||||
const ngtcp2_rand_ctx *rand_ctx,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
|
||||
|
||||
#endif /* NGTCP2_BBR2_H */
|
||||
Vendored
+20
-8
@@ -23,6 +23,7 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ngtcp2_buf.h"
|
||||
#include "ngtcp2_mem.h"
|
||||
|
||||
void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
|
||||
buf->begin = buf->pos = buf->last = begin;
|
||||
@@ -31,14 +32,25 @@ void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
|
||||
|
||||
void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; }
|
||||
|
||||
size_t ngtcp2_buf_left(const ngtcp2_buf *buf) {
|
||||
return (size_t)(buf->end - buf->last);
|
||||
}
|
||||
|
||||
size_t ngtcp2_buf_len(const ngtcp2_buf *buf) {
|
||||
return (size_t)(buf->last - buf->pos);
|
||||
}
|
||||
|
||||
size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
|
||||
return (size_t)(buf->end - buf->begin);
|
||||
}
|
||||
|
||||
int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
|
||||
const ngtcp2_mem *mem) {
|
||||
*pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len);
|
||||
if (*pbufchain == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
(*pbufchain)->next = NULL;
|
||||
|
||||
ngtcp2_buf_init(&(*pbufchain)->buf,
|
||||
(uint8_t *)(*pbufchain) + sizeof(ngtcp2_buf_chain), len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) {
|
||||
ngtcp2_mem_free(mem, bufchain);
|
||||
}
|
||||
Vendored
+31
-2
@@ -62,13 +62,13 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf);
|
||||
* written to the underlying buffer. In other words, it returns
|
||||
* buf->end - buf->last.
|
||||
*/
|
||||
size_t ngtcp2_buf_left(const ngtcp2_buf *buf);
|
||||
#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last)
|
||||
|
||||
/*
|
||||
* ngtcp2_buf_len returns the number of bytes left to read. In other
|
||||
* words, it returns buf->last - buf->pos.
|
||||
*/
|
||||
size_t ngtcp2_buf_len(const ngtcp2_buf *buf);
|
||||
#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos)
|
||||
|
||||
/*
|
||||
* ngtcp2_buf_cap returns the capacity of the buffer. In other words,
|
||||
@@ -76,4 +76,33 @@ size_t ngtcp2_buf_len(const ngtcp2_buf *buf);
|
||||
*/
|
||||
size_t ngtcp2_buf_cap(const ngtcp2_buf *buf);
|
||||
|
||||
/*
|
||||
* ngtcp2_buf_chain is a linked list of ngtcp2_buf.
|
||||
*/
|
||||
typedef struct ngtcp2_buf_chain ngtcp2_buf_chain;
|
||||
|
||||
struct ngtcp2_buf_chain {
|
||||
ngtcp2_buf_chain *next;
|
||||
ngtcp2_buf buf;
|
||||
};
|
||||
|
||||
/*
|
||||
* ngtcp2_buf_chain_new creates new ngtcp2_buf_chain and initializes
|
||||
* the internal buffer with |len| bytes space.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_buf_chain_del deletes the resource allocated by |bufchain|.
|
||||
* It also deletes the memory pointed by |bufchain|.
|
||||
*/
|
||||
void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem);
|
||||
|
||||
#endif /* NGTCP2_BUF_H */
|
||||
Vendored
+79
-11
@@ -43,11 +43,15 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
|
||||
|
||||
ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
|
||||
size_t pktlen, ngtcp2_pktns_id pktns_id,
|
||||
ngtcp2_tstamp ts_sent) {
|
||||
ngtcp2_tstamp sent_ts, uint64_t lost,
|
||||
uint64_t tx_in_flight, int is_app_limited) {
|
||||
pkt->pkt_num = pkt_num;
|
||||
pkt->pktlen = pktlen;
|
||||
pkt->pktns_id = pktns_id;
|
||||
pkt->ts_sent = ts_sent;
|
||||
pkt->sent_ts = sent_ts;
|
||||
pkt->lost = lost;
|
||||
pkt->tx_in_flight = tx_in_flight;
|
||||
pkt->is_app_limited = is_app_limited;
|
||||
|
||||
return pkt;
|
||||
}
|
||||
@@ -106,7 +110,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
uint64_t m;
|
||||
(void)ts;
|
||||
|
||||
if (in_congestion_recovery(cstat, pkt->ts_sent)) {
|
||||
if (in_congestion_recovery(cstat, pkt->sent_ts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,12 +133,12 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
}
|
||||
|
||||
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts_sent,
|
||||
ngtcp2_tstamp sent_ts,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
|
||||
uint64_t min_cwnd;
|
||||
|
||||
if (in_congestion_recovery(cstat, ts_sent)) {
|
||||
if (in_congestion_recovery(cstat, sent_ts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -162,9 +166,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx,
|
||||
}
|
||||
|
||||
void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
|
||||
ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
|
||||
uint64_t target_cwnd, initcwnd;
|
||||
(void)ack;
|
||||
(void)ts;
|
||||
|
||||
/* TODO Use sliding window for min rtt measurement */
|
||||
@@ -184,8 +189,12 @@ void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) {
|
||||
void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
|
||||
(void)cstat;
|
||||
(void)ts;
|
||||
|
||||
reno_cc_reset(cc);
|
||||
}
|
||||
|
||||
@@ -198,6 +207,14 @@ static void cubic_cc_reset(ngtcp2_cubic_cc *cc) {
|
||||
cc->epoch_start = UINT64_MAX;
|
||||
cc->k = 0;
|
||||
|
||||
cc->prior.cwnd = 0;
|
||||
cc->prior.ssthresh = 0;
|
||||
cc->prior.w_last_max = 0;
|
||||
cc->prior.w_tcp = 0;
|
||||
cc->prior.origin_point = 0;
|
||||
cc->prior.epoch_start = UINT64_MAX;
|
||||
cc->prior.k = 0;
|
||||
|
||||
cc->rtt_sample_count = 0;
|
||||
cc->current_round_min_rtt = UINT64_MAX;
|
||||
cc->last_round_min_rtt = UINT64_MAX;
|
||||
@@ -225,6 +242,7 @@ int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
|
||||
cc->ccb = &cubic_cc->ccb;
|
||||
cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked;
|
||||
cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event;
|
||||
cc->on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion;
|
||||
cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion;
|
||||
cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv;
|
||||
cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent;
|
||||
@@ -300,7 +318,7 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
cc->window_end = -1;
|
||||
}
|
||||
|
||||
if (in_congestion_recovery(cstat, pkt->ts_sent)) {
|
||||
if (in_congestion_recovery(cstat, pkt->sent_ts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -421,15 +439,25 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
|
||||
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts_sent,
|
||||
ngtcp2_tstamp sent_ts,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
|
||||
uint64_t min_cwnd;
|
||||
|
||||
if (in_congestion_recovery(cstat, ts_sent)) {
|
||||
if (in_congestion_recovery(cstat, sent_ts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cc->prior.cwnd < cstat->cwnd) {
|
||||
cc->prior.cwnd = cstat->cwnd;
|
||||
cc->prior.ssthresh = cstat->ssthresh;
|
||||
cc->prior.w_last_max = cc->w_last_max;
|
||||
cc->prior.w_tcp = cc->w_tcp;
|
||||
cc->prior.origin_point = cc->origin_point;
|
||||
cc->prior.epoch_start = cc->epoch_start;
|
||||
cc->prior.k = cc->k;
|
||||
}
|
||||
|
||||
cstat->congestion_recovery_start_ts = ts;
|
||||
|
||||
cc->epoch_start = UINT64_MAX;
|
||||
@@ -449,6 +477,40 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx,
|
||||
cstat->cwnd);
|
||||
}
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
|
||||
(void)ts;
|
||||
|
||||
if (cstat->cwnd >= cc->prior.cwnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
cstat->congestion_recovery_start_ts = UINT64_MAX;
|
||||
|
||||
cstat->cwnd = cc->prior.cwnd;
|
||||
cstat->ssthresh = cc->prior.ssthresh;
|
||||
cc->w_last_max = cc->prior.w_last_max;
|
||||
cc->w_tcp = cc->prior.w_tcp;
|
||||
cc->origin_point = cc->prior.origin_point;
|
||||
cc->epoch_start = cc->prior.epoch_start;
|
||||
cc->k = cc->prior.k;
|
||||
|
||||
cc->prior.cwnd = 0;
|
||||
cc->prior.ssthresh = 0;
|
||||
cc->prior.w_last_max = 0;
|
||||
cc->prior.w_tcp = 0;
|
||||
cc->prior.origin_point = 0;
|
||||
cc->prior.epoch_start = UINT64_MAX;
|
||||
cc->prior.k = 0;
|
||||
|
||||
ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
|
||||
"spurious congestion is detected and congestion state is "
|
||||
"restored cwnd=%" PRIu64,
|
||||
cstat->cwnd);
|
||||
}
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
@@ -460,9 +522,11 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx,
|
||||
}
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
|
||||
uint64_t target_cwnd, initcwnd;
|
||||
(void)ack;
|
||||
(void)ts;
|
||||
|
||||
/* TODO Use sliding window for min rtt measurement */
|
||||
@@ -511,8 +575,12 @@ void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
++cc->rtt_sample_count;
|
||||
}
|
||||
|
||||
void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) {
|
||||
void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
|
||||
(void)cstat;
|
||||
(void)ts;
|
||||
|
||||
cubic_cc_reset(cc);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+293
-7
@@ -36,6 +36,273 @@
|
||||
|
||||
typedef struct ngtcp2_log ngtcp2_log;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* :type:`ngtcp2_cc_base` is the base structure of custom congestion
|
||||
* control algorithm. It must be the first field of custom congestion
|
||||
* controller.
|
||||
*/
|
||||
typedef struct ngtcp2_cc_base {
|
||||
/**
|
||||
* :member:`log` is ngtcp2 library internal logger.
|
||||
*/
|
||||
ngtcp2_log *log;
|
||||
} ngtcp2_cc_base;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* :type:`ngtcp2_cc_pkt` is a convenient structure to include
|
||||
* acked/lost/sent packet.
|
||||
*/
|
||||
typedef struct ngtcp2_cc_pkt {
|
||||
/**
|
||||
* :member:`pkt_num` is the packet number
|
||||
*/
|
||||
int64_t pkt_num;
|
||||
/**
|
||||
* :member:`pktlen` is the length of packet.
|
||||
*/
|
||||
size_t pktlen;
|
||||
/**
|
||||
* :member:`pktns_id` is the ID of packet number space which this
|
||||
* packet belongs to.
|
||||
*/
|
||||
ngtcp2_pktns_id pktns_id;
|
||||
/**
|
||||
* :member:`sent_ts` is the timestamp when packet is sent.
|
||||
*/
|
||||
ngtcp2_tstamp sent_ts;
|
||||
/**
|
||||
* :member:`lost` is the number of bytes lost when this packet was
|
||||
* sent.
|
||||
*/
|
||||
uint64_t lost;
|
||||
/**
|
||||
* :member:`tx_in_flight` is the bytes in flight when this packet
|
||||
* was sent.
|
||||
*/
|
||||
uint64_t tx_in_flight;
|
||||
/**
|
||||
* :member:`is_app_limited` is nonzero if the connection is
|
||||
* app-limited when this packet was sent.
|
||||
*/
|
||||
int is_app_limited;
|
||||
} ngtcp2_cc_pkt;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* :type:`ngtcp2_cc_ack` is a convenient structure which stores
|
||||
* acknowledged and lost bytes.
|
||||
*/
|
||||
typedef struct ngtcp2_cc_ack {
|
||||
/**
|
||||
* :member:`prior_bytes_in_flight` is the in-flight bytes before
|
||||
* processing this ACK.
|
||||
*/
|
||||
uint64_t prior_bytes_in_flight;
|
||||
/**
|
||||
* :member:`bytes_delivered` is the number of bytes acknowledged.
|
||||
*/
|
||||
uint64_t bytes_delivered;
|
||||
/**
|
||||
* :member:`bytes_lost` is the number of bytes declared lost.
|
||||
*/
|
||||
uint64_t bytes_lost;
|
||||
/**
|
||||
* :member:`pkt_delivered` is the cumulative acknowledged bytes when
|
||||
* the last packet acknowledged by this ACK was sent.
|
||||
*/
|
||||
uint64_t pkt_delivered;
|
||||
/**
|
||||
* :member:`largest_acked_sent_ts` is the time when the largest
|
||||
* acknowledged packet was sent.
|
||||
*/
|
||||
ngtcp2_tstamp largest_acked_sent_ts;
|
||||
/**
|
||||
* :member:`rtt` is the RTT sample. It is UINT64_MAX if no RTT
|
||||
* sample is available.
|
||||
*/
|
||||
ngtcp2_duration rtt;
|
||||
} ngtcp2_cc_ack;
|
||||
|
||||
typedef struct ngtcp2_cc ngtcp2_cc;
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is
|
||||
* called with an acknowledged packet.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_on_pkt_lost` is a callback function which is
|
||||
* called with a lost packet.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt,
|
||||
ngtcp2_tstamp ts);
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_congestion_event` is a callback function which is
|
||||
* called when congestion event happens (e.g., when packet is lost).
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp sent_ts,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_on_spurious_congestion` is a callback function
|
||||
* which is called when a spurious congestion is detected.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_on_spurious_congestion)(ngtcp2_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_on_persistent_congestion` is a callback function
|
||||
* which is called when persistent congestion is established.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_on_ack_recv` is a callback function which is
|
||||
* called when an acknowledgement is received.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_ack *ack,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is
|
||||
* called when an ack-eliciting packet is sent.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is
|
||||
* called when new RTT sample is obtained.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_reset` is a callback function which is called when
|
||||
* congestion state must be reset.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
* :type:`ngtcp2_cc_event_type` defines congestion control events.
|
||||
*/
|
||||
typedef enum ngtcp2_cc_event_type {
|
||||
/**
|
||||
* :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet
|
||||
* is sent and no other ack-eliciting packet is present.
|
||||
*/
|
||||
NGTCP2_CC_EVENT_TYPE_TX_START
|
||||
} ngtcp2_cc_event_type;
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* :type:`ngtcp2_cc_event` is a callback function which is called when
|
||||
* a specific event happens.
|
||||
*/
|
||||
typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* :type:`ngtcp2_cc` is congestion control algorithm interface to
|
||||
* allow custom implementation.
|
||||
*/
|
||||
typedef struct ngtcp2_cc {
|
||||
/**
|
||||
* :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which
|
||||
* usually contains a state.
|
||||
*/
|
||||
ngtcp2_cc_base *ccb;
|
||||
/**
|
||||
* :member:`on_pkt_acked` is a callback function which is called
|
||||
* when a packet is acknowledged.
|
||||
*/
|
||||
ngtcp2_cc_on_pkt_acked on_pkt_acked;
|
||||
/**
|
||||
* :member:`on_pkt_lost` is a callback function which is called when
|
||||
* a packet is lost.
|
||||
*/
|
||||
ngtcp2_cc_on_pkt_lost on_pkt_lost;
|
||||
/**
|
||||
* :member:`congestion_event` is a callback function which is called
|
||||
* when congestion event happens (.e.g, packet is lost).
|
||||
*/
|
||||
ngtcp2_cc_congestion_event congestion_event;
|
||||
/**
|
||||
* :member:`on_spurious_congestion` is a callback function which is
|
||||
* called when a spurious congestion is detected.
|
||||
*/
|
||||
ngtcp2_cc_on_spurious_congestion on_spurious_congestion;
|
||||
/**
|
||||
* :member:`on_persistent_congestion` is a callback function which
|
||||
* is called when persistent congestion is established.
|
||||
*/
|
||||
ngtcp2_cc_on_persistent_congestion on_persistent_congestion;
|
||||
/**
|
||||
* :member:`on_ack_recv` is a callback function which is called when
|
||||
* an acknowledgement is received.
|
||||
*/
|
||||
ngtcp2_cc_on_ack_recv on_ack_recv;
|
||||
/**
|
||||
* :member:`on_pkt_sent` is a callback function which is called when
|
||||
* ack-eliciting packet is sent.
|
||||
*/
|
||||
ngtcp2_cc_on_pkt_sent on_pkt_sent;
|
||||
/**
|
||||
* :member:`new_rtt_sample` is a callback function which is called
|
||||
* when new RTT sample is obtained.
|
||||
*/
|
||||
ngtcp2_cc_new_rtt_sample new_rtt_sample;
|
||||
/**
|
||||
* :member:`reset` is a callback function which is called when
|
||||
* congestion control state must be reset.
|
||||
*/
|
||||
ngtcp2_cc_reset reset;
|
||||
/**
|
||||
* :member:`event` is a callback function which is called when a
|
||||
* specific event happens.
|
||||
*/
|
||||
ngtcp2_cc_event event;
|
||||
} ngtcp2_cc;
|
||||
|
||||
/*
|
||||
* ngtcp2_cc_compute_initcwnd computes initial cwnd.
|
||||
*/
|
||||
@@ -43,7 +310,8 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size);
|
||||
|
||||
ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
|
||||
size_t pktlen, ngtcp2_pktns_id pktns_id,
|
||||
ngtcp2_tstamp ts_sent);
|
||||
ngtcp2_tstamp sent_ts, uint64_t lost,
|
||||
uint64_t tx_in_flight, int is_app_limited);
|
||||
|
||||
/* ngtcp2_reno_cc is the RENO congestion controller. */
|
||||
typedef struct ngtcp2_reno_cc {
|
||||
@@ -66,7 +334,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts_sent,
|
||||
ngtcp2_tstamp sent_ts,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
|
||||
@@ -74,9 +342,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc);
|
||||
void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/* ngtcp2_cubic_cc is CUBIC congestion controller. */
|
||||
typedef struct ngtcp2_cubic_cc {
|
||||
@@ -88,6 +357,18 @@ typedef struct ngtcp2_cubic_cc {
|
||||
uint64_t origin_point;
|
||||
ngtcp2_tstamp epoch_start;
|
||||
uint64_t k;
|
||||
/* prior stores the congestion state when a congestion event occurs
|
||||
in order to restore the state when it turns out that the event is
|
||||
spurious. */
|
||||
struct {
|
||||
uint64_t cwnd;
|
||||
uint64_t ssthresh;
|
||||
uint64_t w_last_max;
|
||||
uint64_t w_tcp;
|
||||
uint64_t origin_point;
|
||||
ngtcp2_tstamp epoch_start;
|
||||
uint64_t k;
|
||||
} prior;
|
||||
/* HyStart++ variables */
|
||||
size_t rtt_sample_count;
|
||||
uint64_t current_round_min_rtt;
|
||||
@@ -111,15 +392,19 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts_sent,
|
||||
ngtcp2_tstamp sent_ts,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
|
||||
ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
const ngtcp2_cc_pkt *pkt);
|
||||
@@ -127,7 +412,8 @@ void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc);
|
||||
void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
|
||||
ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
|
||||
|
||||
Vendored
+43
-17
@@ -30,7 +30,7 @@
|
||||
#include "ngtcp2_path.h"
|
||||
#include "ngtcp2_str.h"
|
||||
|
||||
void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; }
|
||||
void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); }
|
||||
|
||||
void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) {
|
||||
assert(datalen <= NGTCP2_MAX_CIDLEN);
|
||||
@@ -56,23 +56,17 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) {
|
||||
|
||||
int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; }
|
||||
|
||||
void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid,
|
||||
const uint8_t *token) {
|
||||
void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) {
|
||||
scid->pe.index = NGTCP2_PQ_BAD_INDEX;
|
||||
scid->seq = seq;
|
||||
scid->cid = *cid;
|
||||
scid->ts_retired = UINT64_MAX;
|
||||
scid->retired_ts = UINT64_MAX;
|
||||
scid->flags = NGTCP2_SCID_FLAG_NONE;
|
||||
if (token) {
|
||||
memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
} else {
|
||||
memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) {
|
||||
ngtcp2_scid_init(dest, src->seq, &src->cid, src->token);
|
||||
dest->ts_retired = src->ts_retired;
|
||||
ngtcp2_scid_init(dest, src->seq, &src->cid);
|
||||
dest->retired_ts = src->retired_ts;
|
||||
dest->flags = src->flags;
|
||||
}
|
||||
|
||||
@@ -82,35 +76,58 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
|
||||
dcid->cid = *cid;
|
||||
if (token) {
|
||||
memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT;
|
||||
} else {
|
||||
memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
dcid->flags = NGTCP2_DCID_FLAG_NONE;
|
||||
}
|
||||
ngtcp2_path_storage_zero(&dcid->ps);
|
||||
dcid->ts_retired = UINT64_MAX;
|
||||
dcid->flags = NGTCP2_DCID_FLAG_NONE;
|
||||
dcid->retired_ts = UINT64_MAX;
|
||||
dcid->bound_ts = UINT64_MAX;
|
||||
dcid->bytes_sent = 0;
|
||||
dcid->bytes_recv = 0;
|
||||
dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
|
||||
}
|
||||
|
||||
void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) {
|
||||
assert(token);
|
||||
|
||||
dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
|
||||
memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
}
|
||||
|
||||
void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) {
|
||||
ngtcp2_path_copy(&dcid->ps.path, path);
|
||||
}
|
||||
|
||||
void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
|
||||
ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token);
|
||||
ngtcp2_dcid_init(dest, src->seq, &src->cid,
|
||||
(src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token
|
||||
: NULL);
|
||||
ngtcp2_path_copy(&dest->ps.path, &src->ps.path);
|
||||
dest->ts_retired = src->ts_retired;
|
||||
dest->retired_ts = src->retired_ts;
|
||||
dest->bound_ts = src->bound_ts;
|
||||
dest->flags = src->flags;
|
||||
dest->bytes_sent = src->bytes_sent;
|
||||
dest->bytes_recv = src->bytes_recv;
|
||||
dest->max_udp_payload_size = src->max_udp_payload_size;
|
||||
}
|
||||
|
||||
void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
|
||||
dest->seq = src->seq;
|
||||
dest->cid = src->cid;
|
||||
memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
|
||||
dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
|
||||
memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
|
||||
} else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
|
||||
dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT;
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
|
||||
const ngtcp2_cid *cid, const uint8_t *token) {
|
||||
if (dcid->seq == seq) {
|
||||
return ngtcp2_cid_eq(&dcid->cid, cid) &&
|
||||
(dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
|
||||
memcmp(dcid->token, token,
|
||||
NGTCP2_STATELESS_RESET_TOKENLEN) == 0
|
||||
? 0
|
||||
@@ -119,3 +136,12 @@ int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
|
||||
|
||||
return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO;
|
||||
}
|
||||
|
||||
int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
|
||||
const uint8_t *token) {
|
||||
return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
|
||||
ngtcp2_cmemeq(dcid->token, token,
|
||||
NGTCP2_STATELESS_RESET_TOKENLEN)
|
||||
? 0
|
||||
: NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
Vendored
+47
-25
@@ -34,14 +34,14 @@
|
||||
#include "ngtcp2_pq.h"
|
||||
#include "ngtcp2_path.h"
|
||||
|
||||
/* NGTCP2_SCID_FLAG_NONE indicats that no flag is set. */
|
||||
#define NGTCP2_SCID_FLAG_NONE 0x00
|
||||
/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGTCP2_SCID_FLAG_NONE 0x00u
|
||||
/* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that
|
||||
a remote endpoint uses a particular Connection ID. */
|
||||
#define NGTCP2_SCID_FLAG_USED 0x01
|
||||
#define NGTCP2_SCID_FLAG_USED 0x01u
|
||||
/* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID
|
||||
is retired. */
|
||||
#define NGTCP2_SCID_FLAG_RETIRED 0x02
|
||||
#define NGTCP2_SCID_FLAG_RETIRED 0x02u
|
||||
|
||||
typedef struct ngtcp2_scid {
|
||||
ngtcp2_pq_entry pe;
|
||||
@@ -49,22 +49,21 @@ typedef struct ngtcp2_scid {
|
||||
uint64_t seq;
|
||||
/* cid is a connection ID */
|
||||
ngtcp2_cid cid;
|
||||
/* ts_retired is the timestamp when peer tells that this CID is
|
||||
/* retired_ts is the timestamp when peer tells that this CID is
|
||||
retired. */
|
||||
ngtcp2_tstamp ts_retired;
|
||||
ngtcp2_tstamp retired_ts;
|
||||
/* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */
|
||||
uint8_t flags;
|
||||
/* token is a stateless reset token associated to this CID.
|
||||
Actually, the stateless reset token is tied to the connection,
|
||||
not to the particular connection ID. */
|
||||
uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
|
||||
} ngtcp2_scid;
|
||||
|
||||
/* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGTCP2_DCID_FLAG_NONE 0x00
|
||||
#define NGTCP2_DCID_FLAG_NONE 0x00u
|
||||
/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path
|
||||
has been validated. */
|
||||
#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01
|
||||
#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u
|
||||
/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset
|
||||
token is set in token field. */
|
||||
#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u
|
||||
|
||||
typedef struct ngtcp2_dcid {
|
||||
/* seq is the sequence number associated to the CID. */
|
||||
@@ -74,14 +73,21 @@ typedef struct ngtcp2_dcid {
|
||||
/* path is a path which cid is bound to. The addresses are zero
|
||||
length if cid has not been bound to a particular path yet. */
|
||||
ngtcp2_path_storage ps;
|
||||
/* ts_retired is the timestamp when peer tells that this CID is
|
||||
/* retired_ts is the timestamp when peer tells that this CID is
|
||||
retired. */
|
||||
ngtcp2_tstamp ts_retired;
|
||||
ngtcp2_tstamp retired_ts;
|
||||
/* bound_ts is the timestamp when this connection ID is bound to a
|
||||
particular path. It is only assigned when a connection ID is
|
||||
used just for sending PATH_RESPONSE and is not zero-length. */
|
||||
ngtcp2_tstamp bound_ts;
|
||||
/* bytes_sent is the number of bytes sent to an associated path. */
|
||||
uint64_t bytes_sent;
|
||||
/* bytes_recv is the number of bytes received from an associated
|
||||
path. */
|
||||
uint64_t bytes_recv;
|
||||
/* max_udp_payload_size is the maximum size of UDP payload that is
|
||||
allowed to send to this path. */
|
||||
size_t max_udp_payload_size;
|
||||
/* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */
|
||||
uint8_t flags;
|
||||
/* token is a stateless reset token associated to this CID.
|
||||
@@ -93,12 +99,6 @@ typedef struct ngtcp2_dcid {
|
||||
/* ngtcp2_cid_zero makes |cid| zero-length. */
|
||||
void ngtcp2_cid_zero(ngtcp2_cid *cid);
|
||||
|
||||
/*
|
||||
* ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same
|
||||
* connection ID.
|
||||
*/
|
||||
int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other);
|
||||
|
||||
/*
|
||||
* ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller
|
||||
* than |rhs|.
|
||||
@@ -112,12 +112,9 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs);
|
||||
int ngtcp2_cid_empty(const ngtcp2_cid *cid);
|
||||
|
||||
/*
|
||||
* ngtcp2_scid_init initializes |scid| with the given parameters. If
|
||||
* |token| is NULL, the function fills scid->token it with 0. |token|
|
||||
* must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
|
||||
* ngtcp2_scid_init initializes |scid| with the given parameters.
|
||||
*/
|
||||
void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid,
|
||||
const uint8_t *token);
|
||||
void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid);
|
||||
|
||||
/*
|
||||
* ngtcp2_scid_copy copies |src| into |dest|.
|
||||
@@ -132,6 +129,19 @@ void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src);
|
||||
void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
|
||||
const uint8_t *token);
|
||||
|
||||
/*
|
||||
* ngtcp2_dcid_set_token sets |token| to |dcid|. |token| must not be
|
||||
* NULL and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
|
||||
*/
|
||||
void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token);
|
||||
|
||||
/*
|
||||
* ngtcp2_dcid_set_path sets |path| to |dcid|. It sets
|
||||
* max_udp_payload_size to the minimum UDP payload size supported
|
||||
* by the IP protocol version.
|
||||
*/
|
||||
void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path);
|
||||
|
||||
/*
|
||||
* ngtcp2_dcid_copy copies |src| into |dest|.
|
||||
*/
|
||||
@@ -150,4 +160,16 @@ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
|
||||
int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
|
||||
const ngtcp2_cid *cid, const uint8_t *token);
|
||||
|
||||
/*
|
||||
* ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset
|
||||
* token |token| against the one included in |dcid|. This function
|
||||
* returns 0 if the verification succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_INVALID_ARGUMENT
|
||||
* Tokens do not match; or |dcid| does not contain a token.
|
||||
*/
|
||||
int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
|
||||
const uint8_t *token);
|
||||
|
||||
#endif /* NGTCP2_CID_H */
|
||||
Vendored
+3637
-1371
File diff suppressed because it is too large.
Load diff
Vendored
+350
-57
@@ -36,14 +36,16 @@
|
||||
#include "ngtcp2_acktr.h"
|
||||
#include "ngtcp2_rtb.h"
|
||||
#include "ngtcp2_strm.h"
|
||||
#include "ngtcp2_mem.h"
|
||||
#include "ngtcp2_idtr.h"
|
||||
#include "ngtcp2_str.h"
|
||||
#include "ngtcp2_pkt.h"
|
||||
#include "ngtcp2_log.h"
|
||||
#include "ngtcp2_pq.h"
|
||||
#include "ngtcp2_cc.h"
|
||||
#include "ngtcp2_bbr.h"
|
||||
#include "ngtcp2_bbr2.h"
|
||||
#include "ngtcp2_pv.h"
|
||||
#include "ngtcp2_pmtud.h"
|
||||
#include "ngtcp2_cid.h"
|
||||
#include "ngtcp2_buf.h"
|
||||
#include "ngtcp2_ppe.h"
|
||||
@@ -117,6 +119,21 @@ typedef enum {
|
||||
packets sent in NGTCP2_ECN_STATE_TESTING period. */
|
||||
#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10
|
||||
|
||||
/* NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN is the maximum length
|
||||
of reason phrase to remember. If the received reason phrase is
|
||||
longer than this value, it is truncated. */
|
||||
#define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024
|
||||
|
||||
/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u
|
||||
/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other
|
||||
than Initial packet should be padded. Initial packet might be
|
||||
padded based on QUIC requirement regardless of this flag. */
|
||||
#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u
|
||||
/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come
|
||||
and it should be encoded into the current packet. */
|
||||
#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02u
|
||||
|
||||
/*
|
||||
* ngtcp2_max_frame is defined so that it covers the largest ACK
|
||||
* frame.
|
||||
@@ -140,52 +157,69 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
|
||||
const uint8_t *data);
|
||||
|
||||
/* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGTCP2_CONN_FLAG_NONE 0x00
|
||||
/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake
|
||||
completed. */
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01
|
||||
#define NGTCP2_CONN_FLAG_NONE 0x00u
|
||||
/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares
|
||||
that TLS handshake has completed. The condition of this
|
||||
declaration varies between TLS implementations and this flag does
|
||||
not indicate the completion of QUIC handshake. Some
|
||||
implementations declare TLS handshake completion as server when
|
||||
they write off Server Finished and before deriving application rx
|
||||
secret. */
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01u
|
||||
/* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is
|
||||
negotiated. This is only used for client. */
|
||||
#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02
|
||||
#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02u
|
||||
/* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport
|
||||
parameters are received. */
|
||||
#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04
|
||||
/* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected packet
|
||||
is received, and decrypted successfully. This flag is used to stop
|
||||
retransmitting handshake packets. It might be replaced with an
|
||||
another mechanism when we implement key update. */
|
||||
#define NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT 0x08
|
||||
#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u
|
||||
/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a
|
||||
local transport parameters are applied. */
|
||||
#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u
|
||||
/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry
|
||||
packet. */
|
||||
#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10
|
||||
#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u
|
||||
/* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is
|
||||
rejected by a peer. */
|
||||
#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20
|
||||
#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u
|
||||
/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired
|
||||
keep-alive timer has been cancelled. */
|
||||
#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u
|
||||
/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint
|
||||
confirmed completion of handshake. */
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u
|
||||
/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the
|
||||
library transitions its state to "post handshake". */
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100u
|
||||
/* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early
|
||||
handshake retransmission has done when server receives overlapping
|
||||
Initial crypto data. */
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200
|
||||
#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u
|
||||
/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint
|
||||
sends a QUIC packet without Fixed Bit set if a remote endpoint
|
||||
supports Greasing QUIC Bit extension. */
|
||||
#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u
|
||||
/* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is
|
||||
not confirmed by the local endpoint. That is, it has not received
|
||||
ACK frame which acknowledges packet which is encrypted with new
|
||||
key. */
|
||||
#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800
|
||||
#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u
|
||||
/* NGTCP2_CONN_FLAG_PPE_PENDING is set when
|
||||
NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of
|
||||
ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */
|
||||
#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000
|
||||
#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u
|
||||
/* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer
|
||||
should be restarted on next write. */
|
||||
#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000
|
||||
#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u
|
||||
/* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer
|
||||
verified client address. This flag is only used by client. */
|
||||
#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000
|
||||
#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u
|
||||
/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is
|
||||
installed. conn->early.ckm cannot be used for this purpose because
|
||||
it might be discarded when a certain condition is met. */
|
||||
#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000u
|
||||
/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local
|
||||
endpoint has initiated key update. */
|
||||
#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u
|
||||
|
||||
typedef struct ngtcp2_crypto_data {
|
||||
ngtcp2_buf buf;
|
||||
@@ -264,9 +298,9 @@ typedef struct ngtcp2_pktns {
|
||||
struct {
|
||||
/* ect0, ect1, ce are the ECN counts received in the latest
|
||||
ACK frame. */
|
||||
size_t ect0;
|
||||
size_t ect1;
|
||||
size_t ce;
|
||||
uint64_t ect0;
|
||||
uint64_t ect1;
|
||||
uint64_t ce;
|
||||
} ack;
|
||||
} ecn;
|
||||
} rx;
|
||||
@@ -283,6 +317,8 @@ typedef struct ngtcp2_pktns {
|
||||
ngtcp2_crypto_km *ckm;
|
||||
/* hp_ctx is cipher context for packet header protection. */
|
||||
ngtcp2_crypto_cipher_ctx hp_ctx;
|
||||
/* data is the submitted crypto data. */
|
||||
ngtcp2_buf_chain *data;
|
||||
} tx;
|
||||
|
||||
struct {
|
||||
@@ -308,16 +344,28 @@ typedef enum ngtcp2_ecn_state {
|
||||
NGTCP2_ECN_STATE_CAPABLE,
|
||||
} ngtcp2_ecn_state;
|
||||
|
||||
ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE,
|
||||
sizeof(ngtcp2_dcid));
|
||||
ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE,
|
||||
sizeof(ngtcp2_dcid));
|
||||
ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
|
||||
sizeof(ngtcp2_dcid));
|
||||
ngtcp2_static_ringbuf_def(path_challenge, 4,
|
||||
sizeof(ngtcp2_path_challenge_entry));
|
||||
|
||||
ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent);
|
||||
|
||||
struct ngtcp2_conn {
|
||||
ngtcp2_objalloc frc_objalloc;
|
||||
ngtcp2_objalloc rtb_entry_objalloc;
|
||||
ngtcp2_objalloc strm_objalloc;
|
||||
ngtcp2_conn_state state;
|
||||
ngtcp2_callbacks callbacks;
|
||||
/* rcid is a connection ID present in Initial or 0-RTT packet from
|
||||
client as destination connection ID. Server uses this field to
|
||||
check that duplicated Initial or 0-RTT packet are indeed sent to
|
||||
this connection. It is also sent to client as
|
||||
original_destination_connection_id transport parameter. Client
|
||||
uses this field to validate original_destination_connection_id
|
||||
transport parameter if no Retry packet is involved. */
|
||||
this connection. Client uses this field to validate
|
||||
original_destination_connection_id transport parameter. */
|
||||
ngtcp2_cid rcid;
|
||||
/* oscid is the source connection ID initially used by the local
|
||||
endpoint. */
|
||||
@@ -335,21 +383,25 @@ struct ngtcp2_conn {
|
||||
ngtcp2_dcid current;
|
||||
/* bound is a set of destination connection IDs which are bound to
|
||||
particular paths. These paths are not validated yet. */
|
||||
ngtcp2_ringbuf bound;
|
||||
ngtcp2_static_ringbuf_dcid_bound bound;
|
||||
/* unused is a set of unused CID received from peer. */
|
||||
ngtcp2_ringbuf unused;
|
||||
ngtcp2_static_ringbuf_dcid_unused unused;
|
||||
/* retired is a set of CID retired by local endpoint. Keep them
|
||||
in 3*PTO to catch packets in flight along the old path. */
|
||||
ngtcp2_ringbuf retired;
|
||||
ngtcp2_static_ringbuf_dcid_retired retired;
|
||||
/* seqgap tracks received sequence numbers in order to ignore
|
||||
retransmitted duplicated NEW_CONNECTION_ID frame. */
|
||||
ngtcp2_gaptr seqgap;
|
||||
/* retire_prior_to is the largest retire_prior_to received so
|
||||
far. */
|
||||
uint64_t retire_prior_to;
|
||||
/* num_retire_queued is the number of RETIRE_CONNECTION_ID frames
|
||||
queued for transmission. */
|
||||
size_t num_retire_queued;
|
||||
struct {
|
||||
/* seqs contains sequence number of Connection ID whose
|
||||
retirement is not acknowledged by the remote endpoint yet. */
|
||||
uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2];
|
||||
/* len is the number of sequence numbers that seq contains. */
|
||||
size_t len;
|
||||
} retire_unacked;
|
||||
/* zerolen_seq is a pseudo sequence number of zero-length
|
||||
Destination Connection ID in order to distinguish between
|
||||
them. */
|
||||
@@ -374,6 +426,9 @@ struct ngtcp2_conn {
|
||||
struct {
|
||||
/* strmq contains ngtcp2_strm which has frames to send. */
|
||||
ngtcp2_pq strmq;
|
||||
/* strmq_nretrans is the number of entries in strmq which has
|
||||
stream data to resent. */
|
||||
size_t strmq_nretrans;
|
||||
/* ack is ACK frame. The underlying buffer is reused. */
|
||||
ngtcp2_frame *ack;
|
||||
/* max_ack_blks is the number of additional ngtcp2_ack_blk which
|
||||
@@ -399,6 +454,16 @@ struct ngtcp2_conn {
|
||||
validation period. */
|
||||
size_t dgram_sent;
|
||||
} ecn;
|
||||
|
||||
struct {
|
||||
/* pktlen is the number of bytes written before calling
|
||||
ngtcp2_conn_update_pkt_tx_time which resets this field to
|
||||
0. */
|
||||
size_t pktlen;
|
||||
/* next_ts is the time to send next packet. It is UINT64_MAX if
|
||||
packet pacing is disabled or expired.*/
|
||||
ngtcp2_tstamp next_ts;
|
||||
} pacing;
|
||||
} tx;
|
||||
|
||||
struct {
|
||||
@@ -415,9 +480,9 @@ struct ngtcp2_conn {
|
||||
/* window is the connection-level flow control window size. */
|
||||
uint64_t window;
|
||||
/* path_challenge stores received PATH_CHALLENGE data. */
|
||||
ngtcp2_ringbuf path_challenge;
|
||||
/* ccec is the received connection close error code. */
|
||||
ngtcp2_connection_close_error_code ccec;
|
||||
ngtcp2_static_ringbuf_path_challenge path_challenge;
|
||||
/* ccerr is the received connection close error. */
|
||||
ngtcp2_connection_close_error ccerr;
|
||||
} rx;
|
||||
|
||||
struct {
|
||||
@@ -427,6 +492,21 @@ struct ngtcp2_conn {
|
||||
/* discard_started_ts is the timestamp when the timer to discard
|
||||
early key has started. Used by server only. */
|
||||
ngtcp2_tstamp discard_started_ts;
|
||||
/* transport_params is the values remembered by client from the
|
||||
previous session. These are set by
|
||||
ngtcp2_conn_set_early_remote_transport_params(). Server does
|
||||
not use this field. Server must not set values for these
|
||||
parameters that are smaller than the remembered values. */
|
||||
struct {
|
||||
uint64_t initial_max_streams_bidi;
|
||||
uint64_t initial_max_streams_uni;
|
||||
uint64_t initial_max_stream_data_bidi_local;
|
||||
uint64_t initial_max_stream_data_bidi_remote;
|
||||
uint64_t initial_max_stream_data_uni;
|
||||
uint64_t initial_max_data;
|
||||
uint64_t active_connection_id_limit;
|
||||
uint64_t max_datagram_frame_size;
|
||||
} transport_params;
|
||||
} early;
|
||||
|
||||
struct {
|
||||
@@ -456,11 +536,11 @@ struct ngtcp2_conn {
|
||||
struct {
|
||||
/* transport_params is the received transport parameters during
|
||||
handshake. It is used for Short packet only. */
|
||||
ngtcp2_transport_params transport_params;
|
||||
ngtcp2_transport_params *transport_params;
|
||||
/* pending_transport_params is received transport parameters
|
||||
during handshake. It is copied to transport_params when 1RTT
|
||||
key is available. */
|
||||
ngtcp2_transport_params pending_transport_params;
|
||||
ngtcp2_transport_params *pending_transport_params;
|
||||
struct {
|
||||
ngtcp2_idtr idtr;
|
||||
/* unsent_max_streams is the maximum number of streams of peer
|
||||
@@ -519,6 +599,8 @@ struct ngtcp2_conn {
|
||||
ngtcp2_crypto_aead_ctx retry_aead_ctx;
|
||||
/* tls_error is TLS related error. */
|
||||
int tls_error;
|
||||
/* tls_alert is TLS alert generated by the local endpoint. */
|
||||
uint8_t tls_alert;
|
||||
/* decryption_failure_count is the number of received packets that
|
||||
fail authentication. */
|
||||
uint64_t decryption_failure_count;
|
||||
@@ -533,14 +615,63 @@ struct ngtcp2_conn {
|
||||
ngtcp2_frame_chain **pfrc;
|
||||
int pkt_empty;
|
||||
int hd_logged;
|
||||
uint8_t rtb_entry_flags;
|
||||
int was_client_initial;
|
||||
/* flags is bitwise OR of zero or more of
|
||||
NGTCP2_RTB_ENTRY_FLAG_*. */
|
||||
uint16_t rtb_entry_flags;
|
||||
ngtcp2_ssize hs_spktlen;
|
||||
int require_padding;
|
||||
} pkt;
|
||||
|
||||
struct {
|
||||
/* last_ts is a timestamp when a last packet is sent or received
|
||||
on a current path. */
|
||||
ngtcp2_tstamp last_ts;
|
||||
/* timeout is keep-alive timeout. When it expires, a packet
|
||||
should be sent to a current path to keep connection alive. It
|
||||
might be used to keep NAT binding intact. If 0 is set,
|
||||
keep-alive timer is disabled. */
|
||||
ngtcp2_duration timeout;
|
||||
} keep_alive;
|
||||
|
||||
struct {
|
||||
/* Initial keys for negotiated version. If original version ==
|
||||
negotiated version, these fields are not used. */
|
||||
struct {
|
||||
ngtcp2_crypto_km *ckm;
|
||||
ngtcp2_crypto_cipher_ctx hp_ctx;
|
||||
} rx;
|
||||
struct {
|
||||
ngtcp2_crypto_km *ckm;
|
||||
ngtcp2_crypto_cipher_ctx hp_ctx;
|
||||
} tx;
|
||||
/* version is QUIC version that the above Initial keys are created
|
||||
for. */
|
||||
uint32_t version;
|
||||
/* preferred_versions is the array of versions that are preferred
|
||||
by the local endpoint. Server negotiates one of those versions
|
||||
in this array if a client initially selects a less preferred
|
||||
version. Client uses this field and original_version field to
|
||||
prevent version downgrade attack if it reacted upon Version
|
||||
Negotiation packet. */
|
||||
uint32_t *preferred_versions;
|
||||
/* preferred_versionslen is the number of versions stored in the
|
||||
array pointed by preferred_versions. This field is only used
|
||||
by server. */
|
||||
size_t preferred_versionslen;
|
||||
/* other_versions is the versions that the local endpoint sends in
|
||||
version_information transport parameter. This is the wire
|
||||
image of other_versions field of version_information transport
|
||||
parameter. */
|
||||
uint8_t *other_versions;
|
||||
/* other_versionslen is the length of data pointed by
|
||||
other_versions field. */
|
||||
size_t other_versionslen;
|
||||
} vneg;
|
||||
|
||||
ngtcp2_map strms;
|
||||
ngtcp2_conn_stat cstat;
|
||||
ngtcp2_pv *pv;
|
||||
ngtcp2_pmtud *pmtud;
|
||||
ngtcp2_log log;
|
||||
ngtcp2_qlog qlog;
|
||||
ngtcp2_rst rst;
|
||||
@@ -550,9 +681,10 @@ struct ngtcp2_conn {
|
||||
/* idle_ts is the time instant when idle timer started. */
|
||||
ngtcp2_tstamp idle_ts;
|
||||
void *user_data;
|
||||
uint32_t version;
|
||||
uint32_t client_chosen_version;
|
||||
uint32_t negotiated_version;
|
||||
/* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */
|
||||
uint16_t flags;
|
||||
uint32_t flags;
|
||||
int server;
|
||||
};
|
||||
|
||||
@@ -583,6 +715,8 @@ typedef struct ngtcp2_vmsg_datagram {
|
||||
const ngtcp2_vec *data;
|
||||
/* datacnt is the number of ngtcp2_vec pointed by data. */
|
||||
size_t datacnt;
|
||||
/* dgram_id is an opaque identifier chosen by an application. */
|
||||
uint64_t dgram_id;
|
||||
/* flags is bitwise OR of zero or more of
|
||||
NGTCP2_WRITE_DATAGRAM_FLAG_*. */
|
||||
uint32_t flags;
|
||||
@@ -647,8 +781,7 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
|
||||
* NGTCP2_ERR_CALLBACK_FAILURE
|
||||
* User-defined callback function failed.
|
||||
*/
|
||||
int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
|
||||
uint64_t app_error_code);
|
||||
int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_close_stream closes stream |strm| if no further
|
||||
@@ -664,8 +797,7 @@ int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
|
||||
* NGTCP2_ERR_CALLBACK_FAILURE
|
||||
* User-defined callback function failed.
|
||||
*/
|
||||
int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
|
||||
uint64_t app_error_code);
|
||||
int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest
|
||||
@@ -673,12 +805,20 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
|
||||
* ack_delay included in ACK frame. |ack_delay| is actually tainted
|
||||
* (sent by peer), so don't assume that |ack_delay| is always smaller
|
||||
* than, or equals to |rtt|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_INVALID_ARGUMENT
|
||||
* RTT sample is ignored.
|
||||
*/
|
||||
void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
|
||||
ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
|
||||
int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
|
||||
ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
|
||||
|
||||
void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
|
||||
|
||||
int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_detect_lost_pkt detects lost packets.
|
||||
*
|
||||
@@ -721,16 +861,17 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm);
|
||||
ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn);
|
||||
|
||||
ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
|
||||
ngtcp2_pkt_info *pi, uint8_t *dest,
|
||||
size_t destlen, ngtcp2_vmsg *vmsg,
|
||||
ngtcp2_tstamp ts);
|
||||
int pkt_info_version, ngtcp2_pkt_info *pi,
|
||||
uint8_t *dest, size_t destlen,
|
||||
ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr|
|
||||
* frame only in the buffer pointed by |dest| whose length if
|
||||
* ngtcp2_conn_write_single_frame_pkt writes a packet which contains
|
||||
* |fr| frame only in the buffer pointed by |dest| whose length if
|
||||
* |destlen|. |type| is a long packet type to send. If |type| is 0,
|
||||
* Short packet is used. |dcid| is used as a destination connection
|
||||
* ID.
|
||||
* ID. |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*. Only
|
||||
* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized.
|
||||
*
|
||||
* The packet written by this function will not be retransmitted.
|
||||
*
|
||||
@@ -742,8 +883,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
|
||||
*/
|
||||
ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
|
||||
ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
|
||||
uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags,
|
||||
const ngtcp2_path *path, ngtcp2_tstamp ts);
|
||||
uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
|
||||
uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_commit_local_transport_params commits the local
|
||||
@@ -819,4 +960,156 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
|
||||
*/
|
||||
ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_conn_get_idle_expiry` returns the time when a connection
|
||||
* should be closed if it continues to be idle. If idle timeout is
|
||||
* disabled, this function returns ``UINT64_MAX``.
|
||||
*/
|
||||
ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn);
|
||||
|
||||
ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq|
|
||||
* of unacknowledged retiring Destination Connection ID.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_CONNECTION_ID_LIMIT
|
||||
* The number of unacknowledged retirement exceeds the limit.
|
||||
*/
|
||||
int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number
|
||||
* |seq| of unacknowledged retiring Destination Connection ID. It is
|
||||
* fine if such sequence number is not found.
|
||||
*/
|
||||
void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
|
||||
|
||||
/*
|
||||
* ngtcp2_conn_server_negotiate_version negotiates QUIC version. It
|
||||
* is compatible version negotiation. It returns the negotiated QUIC
|
||||
* version. This function must not be called by client.
|
||||
*/
|
||||
uint32_t
|
||||
ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
|
||||
const ngtcp2_version_info *version_info);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_conn_write_connection_close_pkt` writes a packet which
|
||||
* contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed
|
||||
* by |dest| whose capacity is |datalen|.
|
||||
*
|
||||
* If |path| is not ``NULL``, this function stores the network path
|
||||
* with which the packet should be sent. Each addr field must point
|
||||
* to the buffer which should be at least ``sizeof(struct
|
||||
* sockaddr_storage)`` bytes long. The assignment might not be done
|
||||
* if nothing is written to |dest|.
|
||||
*
|
||||
* If |pi| is not ``NULL``, this function stores packet metadata in it
|
||||
* if it succeeds. The metadata includes ECN markings.
|
||||
*
|
||||
* This function must not be called from inside the callback
|
||||
* functions.
|
||||
*
|
||||
* At the moment, successful call to this function makes connection
|
||||
* close. We may change this behaviour in the future to allow
|
||||
* graceful shutdown.
|
||||
*
|
||||
* This function returns the number of bytes written in |dest| if it
|
||||
* succeeds, or one of the following negative error codes:
|
||||
*
|
||||
* :macro:`NGTCP2_ERR_NOMEM`
|
||||
* Out of memory
|
||||
* :macro:`NGTCP2_ERR_NOBUF`
|
||||
* Buffer is too small
|
||||
* :macro:`NGTCP2_ERR_INVALID_STATE`
|
||||
* The current state does not allow sending CONNECTION_CLOSE.
|
||||
* :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
|
||||
* Packet number is exhausted, and cannot send any more packet.
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
|
||||
* User callback failed
|
||||
*/
|
||||
ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
|
||||
ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
|
||||
size_t destlen, uint64_t error_code, const uint8_t *reason,
|
||||
size_t reasonlen, ngtcp2_tstamp ts);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_conn_write_application_close_pkt` writes a packet which
|
||||
* contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
|
||||
* by |dest| whose capacity is |datalen|.
|
||||
*
|
||||
* If |path| is not ``NULL``, this function stores the network path
|
||||
* with which the packet should be sent. Each addr field must point
|
||||
* to the buffer which should be at least ``sizeof(struct
|
||||
* sockaddr_storage)`` bytes long. The assignment might not be done
|
||||
* if nothing is written to |dest|.
|
||||
*
|
||||
* If |pi| is not ``NULL``, this function stores packet metadata in it
|
||||
* if it succeeds. The metadata includes ECN markings.
|
||||
*
|
||||
* If handshake has not been confirmed yet, CONNECTION_CLOSE (type
|
||||
* 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
|
||||
* instead.
|
||||
*
|
||||
* This function must not be called from inside the callback
|
||||
* functions.
|
||||
*
|
||||
* At the moment, successful call to this function makes connection
|
||||
* close. We may change this behaviour in the future to allow
|
||||
* graceful shutdown.
|
||||
*
|
||||
* This function returns the number of bytes written in |dest| if it
|
||||
* succeeds, or one of the following negative error codes:
|
||||
*
|
||||
* :macro:`NGTCP2_ERR_NOMEM`
|
||||
* Out of memory
|
||||
* :macro:`NGTCP2_ERR_NOBUF`
|
||||
* Buffer is too small
|
||||
* :macro:`NGTCP2_ERR_INVALID_STATE`
|
||||
* The current state does not allow sending CONNECTION_CLOSE.
|
||||
* :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
|
||||
* Packet number is exhausted, and cannot send any more packet.
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
|
||||
* User callback failed
|
||||
*/
|
||||
ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
|
||||
ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
|
||||
size_t destlen, uint64_t app_error_code, const uint8_t *reason,
|
||||
size_t reasonlen, ngtcp2_tstamp ts);
|
||||
|
||||
int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn);
|
||||
|
||||
void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_conn_set_remote_transport_params` sets transport parameter
|
||||
* |params| from a remote endpoint to |conn|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
|
||||
* Failed to validate a remote transport parameters.
|
||||
* :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
|
||||
* Version negotiation failure.
|
||||
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
|
||||
* User callback failed
|
||||
* :macro:`NGTCP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_conn_set_remote_transport_params(
|
||||
ngtcp2_conn *conn, const ngtcp2_transport_params *params);
|
||||
|
||||
#endif /* NGTCP2_CONN_H */
|
||||
Vendored
+5
-4
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "ngtcp2_str.h"
|
||||
#include "ngtcp2_pkt.h"
|
||||
#include "ngtcp2_net.h"
|
||||
|
||||
uint64_t ngtcp2_get_uint64(const uint8_t *p) {
|
||||
uint64_t n;
|
||||
@@ -155,13 +156,13 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) {
|
||||
uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n) {
|
||||
uint8_t *rv;
|
||||
|
||||
assert(n < 16384);
|
||||
assert(n < 1073741824);
|
||||
|
||||
rv = ngtcp2_put_uint16be(p, n);
|
||||
*p |= 0x40;
|
||||
rv = ngtcp2_put_uint32be(p, n);
|
||||
*p |= 0x80;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Vendored
+4
-103
@@ -29,107 +29,8 @@
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
#endif /* HAVE_ARPA_INET_H */
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
# include <byteswap.h>
|
||||
#endif /* HAVE_BYTESWAP_H */
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
# include <endian.h>
|
||||
#endif /* HAVE_ENDIAN_H */
|
||||
|
||||
#ifdef HAVE_SYS_ENDIAN_H
|
||||
# include <sys/endian.h>
|
||||
#endif /* HAVE_SYS_ENDIAN_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#if defined(HAVE_BSWAP_64) || \
|
||||
(defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
|
||||
# define ngtcp2_bswap64 bswap_64
|
||||
#else /* !HAVE_BSWAP_64 */
|
||||
# define ngtcp2_bswap64(N) \
|
||||
((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \
|
||||
ngtcp2_ntohl((uint32_t)((N) >> 32)))
|
||||
#endif /* !HAVE_BSWAP_64 */
|
||||
|
||||
#if defined(HAVE_BE64TOH) || \
|
||||
(defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
|
||||
# define ngtcp2_ntohl64(N) be64toh(N)
|
||||
# define ngtcp2_htonl64(N) htobe64(N)
|
||||
#else /* !HAVE_BE64TOH */
|
||||
# if defined(WORDS_BIGENDIAN)
|
||||
# define ngtcp2_ntohl64(N) (N)
|
||||
# define ngtcp2_htonl64(N) (N)
|
||||
# else /* !WORDS_BIGENDIAN */
|
||||
# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
|
||||
# define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
|
||||
# endif /* !WORDS_BIGENDIAN */
|
||||
#endif /* !HAVE_BE64TOH */
|
||||
|
||||
#if defined(WIN32)
|
||||
/* Windows requires ws2_32 library for ntonl family functions. We
|
||||
define inline functions for those function so that we don't have
|
||||
dependeny on that lib. */
|
||||
|
||||
# ifdef _MSC_VER
|
||||
# define STIN static __inline
|
||||
# else
|
||||
# define STIN static inline
|
||||
# endif
|
||||
|
||||
STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
|
||||
uint32_t res;
|
||||
unsigned char *p = (unsigned char *)&res;
|
||||
*p++ = hostlong >> 24;
|
||||
*p++ = (hostlong >> 16) & 0xffu;
|
||||
*p++ = (hostlong >> 8) & 0xffu;
|
||||
*p = hostlong & 0xffu;
|
||||
return res;
|
||||
}
|
||||
|
||||
STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
|
||||
uint16_t res;
|
||||
unsigned char *p = (unsigned char *)&res;
|
||||
*p++ = hostshort >> 8;
|
||||
*p = hostshort & 0xffu;
|
||||
return res;
|
||||
}
|
||||
|
||||
STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
|
||||
uint32_t res;
|
||||
unsigned char *p = (unsigned char *)&netlong;
|
||||
res = *p++ << 24;
|
||||
res += *p++ << 16;
|
||||
res += *p++ << 8;
|
||||
res += *p;
|
||||
return res;
|
||||
}
|
||||
|
||||
STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
|
||||
uint16_t res;
|
||||
unsigned char *p = (unsigned char *)&netshort;
|
||||
res = *p++ << 8;
|
||||
res += *p;
|
||||
return res;
|
||||
}
|
||||
|
||||
#else /* !WIN32 */
|
||||
|
||||
# define ngtcp2_htonl htonl
|
||||
# define ngtcp2_htons htons
|
||||
# define ngtcp2_ntohl ntohl
|
||||
# define ngtcp2_ntohs ntohs
|
||||
|
||||
#endif /* !WIN32 */
|
||||
|
||||
/*
|
||||
* ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned
|
||||
* integer encoded as network byte order, and returns it in host byte
|
||||
@@ -220,12 +121,12 @@ uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n);
|
||||
uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n);
|
||||
|
||||
/*
|
||||
* ngtcp2_put_varint14 writes |n| in |p| using variable-length integer
|
||||
* encoding. |n| must be strictly less than 16384. The function
|
||||
* always encodes |n| in 2 bytes. It returns the one beyond of the
|
||||
* ngtcp2_put_varint30 writes |n| in |p| using variable-length integer
|
||||
* encoding. |n| must be strictly less than 1073741824. The function
|
||||
* always encodes |n| in 4 bytes. It returns the one beyond of the
|
||||
* last written position.
|
||||
*/
|
||||
uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n);
|
||||
uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n);
|
||||
|
||||
/*
|
||||
* ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It
|
||||
|
||||
+197
-21
@@ -30,6 +30,7 @@
|
||||
#include "ngtcp2_str.h"
|
||||
#include "ngtcp2_conv.h"
|
||||
#include "ngtcp2_conn.h"
|
||||
#include "ngtcp2_net.h"
|
||||
|
||||
int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
|
||||
size_t secretlen,
|
||||
@@ -91,6 +92,8 @@ void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
|
||||
size_t i;
|
||||
uint64_t n;
|
||||
|
||||
assert(ivlen >= 8);
|
||||
|
||||
memcpy(dest, iv, ivlen);
|
||||
n = ngtcp2_htonl64((uint64_t)pkt_num);
|
||||
|
||||
@@ -146,14 +149,17 @@ static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id,
|
||||
return p;
|
||||
}
|
||||
|
||||
ngtcp2_ssize
|
||||
ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
|
||||
ngtcp2_transport_params_type exttype,
|
||||
const ngtcp2_transport_params *params) {
|
||||
static const uint8_t empty_address[16];
|
||||
|
||||
ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
|
||||
uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype,
|
||||
int transport_params_version, const ngtcp2_transport_params *params) {
|
||||
uint8_t *p;
|
||||
size_t len = 0;
|
||||
/* For some reason, gcc 7.3.0 requires this initialization. */
|
||||
size_t preferred_addrlen = 0;
|
||||
size_t version_infolen = 0;
|
||||
(void)transport_params_version;
|
||||
|
||||
switch (exttype) {
|
||||
case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO:
|
||||
@@ -218,7 +224,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
|
||||
len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
|
||||
params->initial_max_streams_uni);
|
||||
}
|
||||
if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) {
|
||||
if (params->max_udp_payload_size !=
|
||||
NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) {
|
||||
len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
|
||||
params->max_udp_payload_size);
|
||||
}
|
||||
@@ -249,6 +256,20 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
|
||||
len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
|
||||
params->max_datagram_frame_size);
|
||||
}
|
||||
if (params->grease_quic_bit) {
|
||||
len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
|
||||
ngtcp2_put_varint_len(0);
|
||||
}
|
||||
if (params->version_info_present) {
|
||||
version_infolen = sizeof(uint32_t) + params->version_info.other_versionslen;
|
||||
len += ngtcp2_put_varint_len(
|
||||
NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) +
|
||||
ngtcp2_put_varint_len(version_infolen) + version_infolen;
|
||||
}
|
||||
|
||||
if (dest == NULL && destlen == 0) {
|
||||
return (ngtcp2_ssize)len;
|
||||
}
|
||||
|
||||
if (destlen < len) {
|
||||
return NGTCP2_ERR_NOBUF;
|
||||
@@ -271,13 +292,25 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
|
||||
p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS);
|
||||
p = ngtcp2_put_varint(p, preferred_addrlen);
|
||||
|
||||
p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr,
|
||||
sizeof(params->preferred_address.ipv4_addr));
|
||||
p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port);
|
||||
if (params->preferred_address.ipv4_present) {
|
||||
p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr,
|
||||
sizeof(params->preferred_address.ipv4_addr));
|
||||
p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port);
|
||||
} else {
|
||||
p = ngtcp2_cpymem(p, empty_address,
|
||||
sizeof(params->preferred_address.ipv4_addr));
|
||||
p = ngtcp2_put_uint16be(p, 0);
|
||||
}
|
||||
|
||||
p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr,
|
||||
sizeof(params->preferred_address.ipv6_addr));
|
||||
p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port);
|
||||
if (params->preferred_address.ipv6_present) {
|
||||
p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr,
|
||||
sizeof(params->preferred_address.ipv6_addr));
|
||||
p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port);
|
||||
} else {
|
||||
p = ngtcp2_cpymem(p, empty_address,
|
||||
sizeof(params->preferred_address.ipv6_addr));
|
||||
p = ngtcp2_put_uint16be(p, 0);
|
||||
}
|
||||
|
||||
*p++ = (uint8_t)params->preferred_address.cid.datalen;
|
||||
if (params->preferred_address.cid.datalen) {
|
||||
@@ -330,7 +363,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
|
||||
params->initial_max_streams_uni);
|
||||
}
|
||||
|
||||
if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) {
|
||||
if (params->max_udp_payload_size !=
|
||||
NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) {
|
||||
p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
|
||||
params->max_udp_payload_size);
|
||||
}
|
||||
@@ -367,6 +401,21 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
|
||||
params->max_datagram_frame_size);
|
||||
}
|
||||
|
||||
if (params->grease_quic_bit) {
|
||||
p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT);
|
||||
p = ngtcp2_put_varint(p, 0);
|
||||
}
|
||||
|
||||
if (params->version_info_present) {
|
||||
p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT);
|
||||
p = ngtcp2_put_varint(p, version_infolen);
|
||||
p = ngtcp2_put_uint32be(p, params->version_info.chosen_version);
|
||||
if (params->version_info.other_versionslen) {
|
||||
p = ngtcp2_cpymem(p, params->version_info.other_versions,
|
||||
params->version_info.other_versionslen);
|
||||
}
|
||||
}
|
||||
|
||||
assert((size_t)(p - dest) == len);
|
||||
|
||||
return (ngtcp2_ssize)len;
|
||||
@@ -467,9 +516,9 @@ static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p,
|
||||
return (ngtcp2_ssize)(p - begin);
|
||||
}
|
||||
|
||||
int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
ngtcp2_transport_params_type exttype,
|
||||
const uint8_t *data, size_t datalen) {
|
||||
int ngtcp2_decode_transport_params_versioned(
|
||||
int transport_params_version, ngtcp2_transport_params *params,
|
||||
ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen) {
|
||||
const uint8_t *p, *end;
|
||||
size_t len;
|
||||
uint64_t param_type;
|
||||
@@ -477,9 +526,12 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
ngtcp2_ssize nread;
|
||||
int initial_scid_present = 0;
|
||||
int original_dcid_present = 0;
|
||||
size_t i;
|
||||
(void)transport_params_version;
|
||||
|
||||
p = data;
|
||||
end = data + datalen;
|
||||
if (datalen == 0) {
|
||||
return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
|
||||
}
|
||||
|
||||
/* Set default values */
|
||||
memset(params, 0, sizeof(*params));
|
||||
@@ -488,7 +540,7 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
params->initial_max_stream_data_bidi_local = 0;
|
||||
params->initial_max_stream_data_bidi_remote = 0;
|
||||
params->initial_max_stream_data_uni = 0;
|
||||
params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE;
|
||||
params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
|
||||
params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
|
||||
params->stateless_reset_token_present = 0;
|
||||
params->preferred_address_present = 0;
|
||||
@@ -502,10 +554,10 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
memset(¶ms->retry_scid, 0, sizeof(params->retry_scid));
|
||||
memset(¶ms->initial_scid, 0, sizeof(params->initial_scid));
|
||||
memset(¶ms->original_dcid, 0, sizeof(params->original_dcid));
|
||||
params->version_info_present = 0;
|
||||
|
||||
if (datalen == 0) {
|
||||
return 0;
|
||||
}
|
||||
p = data;
|
||||
end = data + datalen;
|
||||
|
||||
for (; (size_t)(end - p) >= 2;) {
|
||||
nread = decode_varint(¶m_type, p, end);
|
||||
@@ -634,12 +686,24 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
params->preferred_address.ipv4_port = ngtcp2_get_uint16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
if (params->preferred_address.ipv4_port ||
|
||||
memcmp(empty_address, params->preferred_address.ipv4_addr,
|
||||
sizeof(params->preferred_address.ipv4_addr)) != 0) {
|
||||
params->preferred_address.ipv4_present = 1;
|
||||
}
|
||||
|
||||
memcpy(params->preferred_address.ipv6_addr, p,
|
||||
sizeof(params->preferred_address.ipv6_addr));
|
||||
p += sizeof(params->preferred_address.ipv6_addr);
|
||||
params->preferred_address.ipv6_port = ngtcp2_get_uint16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
if (params->preferred_address.ipv6_port ||
|
||||
memcmp(empty_address, params->preferred_address.ipv6_addr,
|
||||
sizeof(params->preferred_address.ipv6_addr)) != 0) {
|
||||
params->preferred_address.ipv6_present = 1;
|
||||
}
|
||||
|
||||
/* cid */
|
||||
params->preferred_address.cid.datalen = *p++;
|
||||
len += params->preferred_address.cid.datalen;
|
||||
@@ -720,6 +784,45 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
}
|
||||
p += nread;
|
||||
break;
|
||||
case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT:
|
||||
nread = decode_varint(&valuelen, p, end);
|
||||
if (nread < 0 || valuelen != 0) {
|
||||
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
|
||||
}
|
||||
p += nread;
|
||||
params->grease_quic_bit = 1;
|
||||
break;
|
||||
case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT:
|
||||
nread = decode_varint(&valuelen, p, end);
|
||||
if (nread < 0) {
|
||||
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
|
||||
}
|
||||
p += nread;
|
||||
if ((size_t)(end - p) < valuelen) {
|
||||
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
|
||||
}
|
||||
if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) {
|
||||
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
|
||||
}
|
||||
params->version_info.chosen_version = ngtcp2_get_uint32(p);
|
||||
if (params->version_info.chosen_version == 0) {
|
||||
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
|
||||
}
|
||||
p += sizeof(uint32_t);
|
||||
if (valuelen > sizeof(uint32_t)) {
|
||||
params->version_info.other_versions = (uint8_t *)p;
|
||||
params->version_info.other_versionslen =
|
||||
(size_t)valuelen - sizeof(uint32_t);
|
||||
|
||||
for (i = sizeof(uint32_t); i < valuelen;
|
||||
i += sizeof(uint32_t), p += sizeof(uint32_t)) {
|
||||
if (ngtcp2_get_uint32(p) == 0) {
|
||||
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
params->version_info_present = 1;
|
||||
break;
|
||||
default:
|
||||
/* Ignore unknown parameter */
|
||||
nread = decode_varint(&valuelen, p, end);
|
||||
@@ -747,3 +850,76 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transport_params_copy_new(ngtcp2_transport_params **pdest,
|
||||
const ngtcp2_transport_params *src,
|
||||
const ngtcp2_mem *mem) {
|
||||
size_t len = sizeof(**pdest);
|
||||
ngtcp2_transport_params *dest;
|
||||
uint8_t *p;
|
||||
|
||||
if (src->version_info_present) {
|
||||
len += src->version_info.other_versionslen;
|
||||
}
|
||||
|
||||
dest = ngtcp2_mem_malloc(mem, len);
|
||||
if (dest == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
*dest = *src;
|
||||
|
||||
if (src->version_info_present && src->version_info.other_versionslen) {
|
||||
p = (uint8_t *)dest + sizeof(*dest);
|
||||
memcpy(p, src->version_info.other_versions,
|
||||
src->version_info.other_versionslen);
|
||||
dest->version_info.other_versions = p;
|
||||
}
|
||||
|
||||
*pdest = dest;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_decode_transport_params_new(ngtcp2_transport_params **pparams,
|
||||
ngtcp2_transport_params_type exttype,
|
||||
const uint8_t *data, size_t datalen,
|
||||
const ngtcp2_mem *mem) {
|
||||
int rv;
|
||||
ngtcp2_transport_params params;
|
||||
|
||||
rv = ngtcp2_decode_transport_params(¶ms, exttype, data, datalen);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mem == NULL) {
|
||||
mem = ngtcp2_mem_default();
|
||||
}
|
||||
|
||||
return transport_params_copy_new(pparams, ¶ms, mem);
|
||||
}
|
||||
|
||||
void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
|
||||
const ngtcp2_mem *mem) {
|
||||
if (params == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem == NULL) {
|
||||
mem = ngtcp2_mem_default();
|
||||
}
|
||||
|
||||
ngtcp2_mem_free(mem, params);
|
||||
}
|
||||
|
||||
int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest,
|
||||
const ngtcp2_transport_params *src,
|
||||
const ngtcp2_mem *mem) {
|
||||
if (src == NULL) {
|
||||
*pdest = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return transport_params_copy_new(pdest, src, mem);
|
||||
}
|
||||
+46
-2
@@ -41,11 +41,38 @@
|
||||
/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */
|
||||
#define NGTCP2_MAX_AEAD_OVERHEAD 16
|
||||
|
||||
/* ngtcp2_transport_param_id is the registry of QUIC transport
|
||||
parameter ID. */
|
||||
typedef enum ngtcp2_transport_param_id {
|
||||
NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000,
|
||||
NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001,
|
||||
NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002,
|
||||
NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009,
|
||||
NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a,
|
||||
NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b,
|
||||
NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c,
|
||||
NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d,
|
||||
NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e,
|
||||
NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f,
|
||||
NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010,
|
||||
/* https://datatracker.ietf.org/doc/html/rfc9221 */
|
||||
NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020,
|
||||
NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2,
|
||||
/* https://quicwg.org/quic-v2/draft-ietf-quic-v2.html */
|
||||
NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT = 0xff73db,
|
||||
} ngtcp2_transport_param_id;
|
||||
|
||||
/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */
|
||||
#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00
|
||||
#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u
|
||||
/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is
|
||||
set. */
|
||||
#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01
|
||||
#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u
|
||||
|
||||
typedef struct ngtcp2_crypto_km {
|
||||
ngtcp2_vec secret;
|
||||
@@ -100,4 +127,21 @@ typedef struct ngtcp2_crypto_cc {
|
||||
void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
|
||||
int64_t pkt_num);
|
||||
|
||||
/*
|
||||
* ngtcp2_transport_params_copy_new makes a copy of |src|, and assigns
|
||||
* it to |*pdest|. If |src| is NULL, NULL is assigned to |*pdest|.
|
||||
*
|
||||
* Caller is responsible to call ngtcp2_transport_params_del to free
|
||||
* the memory assigned to |*pdest|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest,
|
||||
const ngtcp2_transport_params *src,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
#endif /* NGTCP2_CRYPTO_H */
|
||||
Vendored
+14
-4
@@ -64,8 +64,8 @@ const char *ngtcp2_strerror(int liberr) {
|
||||
return "ERR_MALFORMED_TRANSPORT_PARAM";
|
||||
case NGTCP2_ERR_FRAME_ENCODING:
|
||||
return "ERR_FRAME_ENCODING";
|
||||
case NGTCP2_ERR_TLS_DECRYPT:
|
||||
return "ERR_TLS_DECRYPT";
|
||||
case NGTCP2_ERR_DECRYPT:
|
||||
return "ERR_DECRYPT";
|
||||
case NGTCP2_ERR_STREAM_SHUT_WR:
|
||||
return "ERR_STREAM_SHUT_WR";
|
||||
case NGTCP2_ERR_STREAM_NOT_FOUND:
|
||||
@@ -82,8 +82,6 @@ const char *ngtcp2_strerror(int liberr) {
|
||||
return "ERR_TRANSPORT_PARAM";
|
||||
case NGTCP2_ERR_DISCARD_PKT:
|
||||
return "ERR_DISCARD_PKT";
|
||||
case NGTCP2_ERR_PATH_VALIDATION_FAILED:
|
||||
return "ERR_PATH_VALIDATION_FAILED";
|
||||
case NGTCP2_ERR_CONN_ID_BLOCKED:
|
||||
return "ERR_CONN_ID_BLOCKED";
|
||||
case NGTCP2_ERR_CALLBACK_FAILURE:
|
||||
@@ -102,6 +100,14 @@ const char *ngtcp2_strerror(int liberr) {
|
||||
return "ERR_AEAD_LIMIT_REACHED";
|
||||
case NGTCP2_ERR_NO_VIABLE_PATH:
|
||||
return "ERR_NO_VIABLE_PATH";
|
||||
case NGTCP2_ERR_VERSION_NEGOTIATION:
|
||||
return "ERR_VERSION_NEGOTIATION";
|
||||
case NGTCP2_ERR_HANDSHAKE_TIMEOUT:
|
||||
return "ERR_HANDSHAKE_TIMEOUT";
|
||||
case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
|
||||
return "ERR_VERSION_NEGOTIATION_FAILURE";
|
||||
case NGTCP2_ERR_IDLE_CLOSE:
|
||||
return "ERR_IDLE_CLOSE";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
@@ -129,6 +135,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
|
||||
case NGTCP2_ERR_TRANSPORT_PARAM:
|
||||
return NGTCP2_TRANSPORT_PARAMETER_ERROR;
|
||||
case NGTCP2_ERR_INVALID_ARGUMENT:
|
||||
case NGTCP2_ERR_NOMEM:
|
||||
case NGTCP2_ERR_CALLBACK_FAILURE:
|
||||
return NGTCP2_INTERNAL_ERROR;
|
||||
case NGTCP2_ERR_STREAM_STATE:
|
||||
return NGTCP2_STREAM_STATE_ERROR;
|
||||
@@ -138,6 +146,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
|
||||
return NGTCP2_AEAD_LIMIT_REACHED;
|
||||
case NGTCP2_ERR_NO_VIABLE_PATH:
|
||||
return NGTCP2_NO_VIABLE_PATH;
|
||||
case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
|
||||
return NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT;
|
||||
default:
|
||||
return NGTCP2_PROTOCOL_VIOLATION;
|
||||
}
|
||||
|
||||
+65
-27
@@ -23,29 +23,26 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ngtcp2_gaptr.h"
|
||||
#include "ngtcp2_range.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
|
||||
int rv;
|
||||
ngtcp2_range range = {0, UINT64_MAX};
|
||||
void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
|
||||
ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
|
||||
mem);
|
||||
|
||||
rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar,
|
||||
sizeof(ngtcp2_range), mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
gaptr->mem = mem;
|
||||
}
|
||||
|
||||
static int gaptr_gap_init(ngtcp2_gaptr *gaptr) {
|
||||
ngtcp2_range range = {0, UINT64_MAX};
|
||||
int rv;
|
||||
|
||||
rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL);
|
||||
if (rv != 0) {
|
||||
ngtcp2_ksl_free(&gaptr->gap);
|
||||
return rv;
|
||||
}
|
||||
|
||||
gaptr->mem = mem;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -57,11 +54,18 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
|
||||
ngtcp2_ksl_free(&gaptr->gap);
|
||||
}
|
||||
|
||||
int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
|
||||
int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) {
|
||||
int rv;
|
||||
ngtcp2_range k, m, l, r, q = {offset, offset + datalen};
|
||||
ngtcp2_ksl_it it;
|
||||
|
||||
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
|
||||
rv = gaptr_gap_init(gaptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
ngtcp2_ksl_range_exclusive_compar);
|
||||
|
||||
@@ -73,7 +77,7 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
|
||||
}
|
||||
|
||||
if (ngtcp2_range_eq(&k, &m)) {
|
||||
ngtcp2_ksl_remove(&gaptr->gap, &it, &k);
|
||||
ngtcp2_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
|
||||
continue;
|
||||
}
|
||||
ngtcp2_range_cut(&l, &r, &k, &m);
|
||||
@@ -95,35 +99,69 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
|
||||
}
|
||||
|
||||
uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) {
|
||||
ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
|
||||
ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
|
||||
ngtcp2_ksl_it it;
|
||||
ngtcp2_range r;
|
||||
|
||||
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
it = ngtcp2_ksl_begin(&gaptr->gap);
|
||||
r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
|
||||
|
||||
return r.begin;
|
||||
}
|
||||
|
||||
ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
|
||||
uint64_t offset) {
|
||||
ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
|
||||
uint64_t offset) {
|
||||
ngtcp2_range q = {offset, offset + 1};
|
||||
return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
ngtcp2_ksl_range_exclusive_compar);
|
||||
ngtcp2_ksl_it it;
|
||||
|
||||
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
|
||||
ngtcp2_range r = {0, UINT64_MAX};
|
||||
return r;
|
||||
}
|
||||
|
||||
it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
ngtcp2_ksl_range_exclusive_compar);
|
||||
|
||||
assert(!ngtcp2_ksl_it_end(&it));
|
||||
|
||||
return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
|
||||
}
|
||||
|
||||
int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
|
||||
size_t datalen) {
|
||||
uint64_t datalen) {
|
||||
ngtcp2_range q = {offset, offset + datalen};
|
||||
ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar(
|
||||
&gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar);
|
||||
ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
|
||||
ngtcp2_range m = ngtcp2_range_intersect(&q, &k);
|
||||
ngtcp2_ksl_it it;
|
||||
ngtcp2_range k;
|
||||
ngtcp2_range m;
|
||||
|
||||
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
|
||||
ngtcp2_ksl_range_exclusive_compar);
|
||||
k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
|
||||
m = ngtcp2_range_intersect(&q, &k);
|
||||
|
||||
return ngtcp2_range_len(&m) == 0;
|
||||
}
|
||||
|
||||
void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) {
|
||||
ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
|
||||
ngtcp2_ksl_it it;
|
||||
ngtcp2_range r;
|
||||
|
||||
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
it = ngtcp2_ksl_begin(&gaptr->gap);
|
||||
|
||||
assert(!ngtcp2_ksl_it_end(&it));
|
||||
|
||||
r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
|
||||
|
||||
ngtcp2_ksl_remove(&gaptr->gap, NULL, &r);
|
||||
ngtcp2_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
|
||||
}
|
||||
+8
-13
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "ngtcp2_mem.h"
|
||||
#include "ngtcp2_ksl.h"
|
||||
#include "ngtcp2_range.h"
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX).
|
||||
@@ -47,14 +48,8 @@ typedef struct ngtcp2_gaptr {
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr_init initializes |gaptr|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
|
||||
void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr_free frees resources allocated for |gaptr|.
|
||||
@@ -71,7 +66,7 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr);
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen);
|
||||
int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen);
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr_first_gap_offset returns the offset to the first gap.
|
||||
@@ -80,18 +75,18 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen);
|
||||
uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr);
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to
|
||||
* the first gap which overlaps or comes after |offset|.
|
||||
* ngtcp2_gaptr_get_first_gap_after returns the first gap which
|
||||
* overlaps or comes after |offset|.
|
||||
*/
|
||||
ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
|
||||
uint64_t offset);
|
||||
ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
|
||||
uint64_t offset);
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset +
|
||||
* datalen) is completely pushed into this object.
|
||||
*/
|
||||
int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
|
||||
size_t datalen);
|
||||
uint64_t datalen);
|
||||
|
||||
/*
|
||||
* ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if
|
||||
|
||||
Vendored
+2
-9
@@ -26,17 +26,10 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
|
||||
int rv;
|
||||
|
||||
rv = ngtcp2_gaptr_init(&idtr->gap, mem);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
|
||||
ngtcp2_gaptr_init(&idtr->gap, mem);
|
||||
|
||||
idtr->server = server;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_idtr_free(ngtcp2_idtr *idtr) {
|
||||
|
||||
Vendored
+1
-7
@@ -51,14 +51,8 @@ typedef struct ngtcp2_idtr {
|
||||
*
|
||||
* If this object records server initiated ID (even number), set
|
||||
* |server| to nonzero.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
|
||||
void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_idtr_free frees resources allocated for |idtr|.
|
||||
|
||||
Vendored
+131
-53
@@ -33,9 +33,11 @@
|
||||
#include "ngtcp2_mem.h"
|
||||
#include "ngtcp2_range.h"
|
||||
|
||||
static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
|
||||
|
||||
static size_t ksl_nodelen(size_t keylen) {
|
||||
return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
|
||||
(size_t)~0xf;
|
||||
return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
|
||||
~(uintptr_t)0xfu;
|
||||
}
|
||||
|
||||
static size_t ksl_blklen(size_t nodelen) {
|
||||
@@ -51,31 +53,48 @@ static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
|
||||
memcpy(node->key, key, ksl->keylen);
|
||||
}
|
||||
|
||||
int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
|
||||
const ngtcp2_mem *mem) {
|
||||
void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
|
||||
const ngtcp2_mem *mem) {
|
||||
size_t nodelen = ksl_nodelen(keylen);
|
||||
size_t blklen = ksl_blklen(nodelen);
|
||||
ngtcp2_ksl_blk *head;
|
||||
|
||||
ksl->head = ngtcp2_mem_malloc(mem, blklen);
|
||||
if (!ksl->head) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
ksl->front = ksl->back = ksl->head;
|
||||
ngtcp2_objalloc_init(&ksl->blkalloc,
|
||||
((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
|
||||
mem);
|
||||
|
||||
ksl->head = NULL;
|
||||
ksl->front = ksl->back = NULL;
|
||||
ksl->compar = compar;
|
||||
ksl->keylen = keylen;
|
||||
ksl->nodelen = nodelen;
|
||||
ksl->n = 0;
|
||||
ksl->mem = mem;
|
||||
}
|
||||
|
||||
static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) {
|
||||
return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc,
|
||||
ksl_blklen(ksl->nodelen));
|
||||
}
|
||||
|
||||
static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
|
||||
ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
|
||||
}
|
||||
|
||||
static int ksl_head_init(ngtcp2_ksl *ksl) {
|
||||
ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl);
|
||||
if (!head) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
head = ksl->head;
|
||||
head->next = head->prev = NULL;
|
||||
head->n = 0;
|
||||
head->leaf = 1;
|
||||
|
||||
ksl->head = head;
|
||||
ksl->front = ksl->back = head;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef NOMEMPOOL
|
||||
/*
|
||||
* ksl_free_blk frees |blk| recursively.
|
||||
*/
|
||||
@@ -88,15 +107,20 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
|
||||
}
|
||||
}
|
||||
|
||||
ngtcp2_mem_free(ksl->mem, blk);
|
||||
ksl_blk_objalloc_del(ksl, blk);
|
||||
}
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
|
||||
if (!ksl) {
|
||||
if (!ksl || !ksl->head) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NOMEMPOOL
|
||||
ksl_free_blk(ksl, ksl->head);
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
ngtcp2_objalloc_free(&ksl->blkalloc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -110,7 +134,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
|
||||
static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
|
||||
ngtcp2_ksl_blk *rblk;
|
||||
|
||||
rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
|
||||
rblk = ksl_blk_objalloc_new(ksl);
|
||||
if (rblk == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -194,9 +218,9 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
|
||||
|
||||
lblk = ksl->head;
|
||||
|
||||
nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
|
||||
nhead = ksl_blk_objalloc_new(ksl);
|
||||
if (nhead == NULL) {
|
||||
ngtcp2_mem_free(ksl->mem, rblk);
|
||||
ksl_blk_objalloc_del(ksl, rblk);
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
nhead->next = nhead->prev = NULL;
|
||||
@@ -240,29 +264,33 @@ static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i,
|
||||
|
||||
static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
|
||||
const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) {
|
||||
ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid;
|
||||
size_t i;
|
||||
ngtcp2_ksl_node *node;
|
||||
|
||||
while (right - left > 1) {
|
||||
mid = (left + right) >> 1;
|
||||
node = ngtcp2_ksl_nth_node(ksl, blk, (size_t)mid);
|
||||
if (compar((ngtcp2_ksl_key *)node->key, key)) {
|
||||
left = mid;
|
||||
} else {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes;
|
||||
i < blk->n && compar((ngtcp2_ksl_key *)node->key, key);
|
||||
++i, node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
|
||||
;
|
||||
|
||||
return (size_t)right;
|
||||
return i;
|
||||
}
|
||||
|
||||
int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
|
||||
const ngtcp2_ksl_key *key, void *data) {
|
||||
ngtcp2_ksl_blk *blk = ksl->head;
|
||||
ngtcp2_ksl_blk *blk;
|
||||
ngtcp2_ksl_node *node;
|
||||
size_t i;
|
||||
int rv;
|
||||
|
||||
if (!ksl->head) {
|
||||
rv = ksl_head_init(ksl);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
blk = ksl->head;
|
||||
|
||||
if (blk->n == NGTCP2_KSL_MAX_NBLK) {
|
||||
rv = ksl_split_head(ksl);
|
||||
if (rv != 0) {
|
||||
@@ -374,10 +402,10 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
|
||||
ksl->back = lblk;
|
||||
}
|
||||
|
||||
ngtcp2_mem_free(ksl->mem, rblk);
|
||||
ksl_blk_objalloc_del(ksl, rblk);
|
||||
|
||||
if (ksl->head == blk && blk->n == 2) {
|
||||
ngtcp2_mem_free(ksl->mem, ksl->head);
|
||||
ksl_blk_objalloc_del(ksl, ksl->head);
|
||||
ksl->head = lblk;
|
||||
} else {
|
||||
ksl_remove_node(ksl, blk, i + 1);
|
||||
@@ -469,12 +497,42 @@ static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs,
|
||||
return !compar(lhs, rhs) && !compar(rhs, lhs);
|
||||
}
|
||||
|
||||
int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
|
||||
const ngtcp2_ksl_it *hint,
|
||||
const ngtcp2_ksl_key *key) {
|
||||
ngtcp2_ksl_blk *blk = hint->blk;
|
||||
|
||||
assert(ksl->head);
|
||||
|
||||
if (blk->n <= NGTCP2_KSL_MIN_NBLK) {
|
||||
return ngtcp2_ksl_remove(ksl, it, key);
|
||||
}
|
||||
|
||||
ksl_remove_node(ksl, blk, hint->i);
|
||||
|
||||
--ksl->n;
|
||||
|
||||
if (it) {
|
||||
if (hint->i == blk->n && blk->next) {
|
||||
ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
|
||||
} else {
|
||||
ngtcp2_ksl_it_init(it, ksl, blk, hint->i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
|
||||
const ngtcp2_ksl_key *key) {
|
||||
ngtcp2_ksl_blk *blk = ksl->head;
|
||||
ngtcp2_ksl_node *node;
|
||||
size_t i;
|
||||
|
||||
if (!ksl->head) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!blk->leaf && blk->n == 2 &&
|
||||
ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK &&
|
||||
ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) {
|
||||
@@ -550,6 +608,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
|
||||
ngtcp2_ksl_it it;
|
||||
size_t i;
|
||||
|
||||
if (!blk) {
|
||||
ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
return it;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, ksl->compar);
|
||||
|
||||
@@ -587,6 +650,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
|
||||
ngtcp2_ksl_it it;
|
||||
size_t i;
|
||||
|
||||
if (!blk) {
|
||||
ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
return it;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, key, compar);
|
||||
|
||||
@@ -623,6 +691,8 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
|
||||
ngtcp2_ksl_node *node;
|
||||
size_t i;
|
||||
|
||||
assert(ksl->head);
|
||||
|
||||
for (;;) {
|
||||
i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
|
||||
|
||||
@@ -667,36 +737,49 @@ static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) {
|
||||
size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; }
|
||||
|
||||
void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
|
||||
size_t i;
|
||||
ngtcp2_ksl_blk *head;
|
||||
|
||||
if (!ksl->head->leaf) {
|
||||
for (i = 0; i < ksl->head->n; ++i) {
|
||||
ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk);
|
||||
}
|
||||
if (!ksl->head) {
|
||||
return;
|
||||
}
|
||||
|
||||
ksl->front = ksl->back = ksl->head;
|
||||
#ifdef NOMEMPOOL
|
||||
ksl_free_blk(ksl, ksl->head);
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
ksl->front = ksl->back = ksl->head = NULL;
|
||||
ksl->n = 0;
|
||||
|
||||
head = ksl->head;
|
||||
|
||||
head->next = head->prev = NULL;
|
||||
head->n = 0;
|
||||
head->leaf = 1;
|
||||
ngtcp2_objalloc_clear(&ksl->blkalloc);
|
||||
}
|
||||
|
||||
void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
|
||||
void ngtcp2_ksl_print(ngtcp2_ksl *ksl) {
|
||||
if (!ksl->head) {
|
||||
return;
|
||||
}
|
||||
|
||||
ksl_print(ksl, ksl->head, 0);
|
||||
}
|
||||
|
||||
ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
|
||||
ngtcp2_ksl_it it;
|
||||
ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
|
||||
|
||||
if (ksl->head) {
|
||||
ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
|
||||
} else {
|
||||
ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) {
|
||||
ngtcp2_ksl_it it;
|
||||
ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
|
||||
|
||||
if (ksl->head) {
|
||||
ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
|
||||
} else {
|
||||
ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
@@ -707,11 +790,6 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
|
||||
it->i = i;
|
||||
}
|
||||
|
||||
void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) {
|
||||
assert(it->i < it->blk->n);
|
||||
return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data;
|
||||
}
|
||||
|
||||
void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) {
|
||||
assert(!ngtcp2_ksl_it_begin(it));
|
||||
|
||||
|
||||
Vendored
+41
-25
@@ -33,6 +33,8 @@
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "ngtcp2_objalloc.h"
|
||||
|
||||
/*
|
||||
* Skip List using single key instead of range.
|
||||
*/
|
||||
@@ -79,25 +81,33 @@ struct ngtcp2_ksl_node {
|
||||
* ngtcp2_ksl_blk contains ngtcp2_ksl_node objects.
|
||||
*/
|
||||
struct ngtcp2_ksl_blk {
|
||||
/* next points to the next block if leaf field is nonzero. */
|
||||
ngtcp2_ksl_blk *next;
|
||||
/* prev points to the previous block if leaf field is nonzero. */
|
||||
ngtcp2_ksl_blk *prev;
|
||||
/* n is the number of nodes this object contains in nodes. */
|
||||
uint32_t n;
|
||||
/* leaf is nonzero if this block contains leaf nodes. */
|
||||
uint32_t leaf;
|
||||
union {
|
||||
uint64_t align;
|
||||
/* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
|
||||
ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is
|
||||
allocated along with the additional variable length key
|
||||
storage, the size of buffer is unknown until ngtcp2_ksl_init is
|
||||
called. */
|
||||
uint8_t nodes[1];
|
||||
struct {
|
||||
/* next points to the next block if leaf field is nonzero. */
|
||||
ngtcp2_ksl_blk *next;
|
||||
/* prev points to the previous block if leaf field is nonzero. */
|
||||
ngtcp2_ksl_blk *prev;
|
||||
/* n is the number of nodes this object contains in nodes. */
|
||||
uint32_t n;
|
||||
/* leaf is nonzero if this block contains leaf nodes. */
|
||||
uint32_t leaf;
|
||||
union {
|
||||
uint64_t align;
|
||||
/* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
|
||||
ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is
|
||||
allocated along with the additional variable length key
|
||||
storage, the size of buffer is unknown until ngtcp2_ksl_init is
|
||||
called. */
|
||||
uint8_t nodes[1];
|
||||
};
|
||||
};
|
||||
|
||||
ngtcp2_opl_entry oplent;
|
||||
};
|
||||
};
|
||||
|
||||
ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent);
|
||||
|
||||
/*
|
||||
* ngtcp2_ksl_compar is a function type which returns nonzero if key
|
||||
* |lhs| should be placed before |rhs|. It returns 0 otherwise.
|
||||
@@ -122,6 +132,7 @@ struct ngtcp2_ksl_it {
|
||||
* ngtcp2_ksl is a deterministic paged skip list.
|
||||
*/
|
||||
struct ngtcp2_ksl {
|
||||
ngtcp2_objalloc blkalloc;
|
||||
/* head points to the root block. */
|
||||
ngtcp2_ksl_blk *head;
|
||||
/* front points to the first leaf block. */
|
||||
@@ -135,21 +146,14 @@ struct ngtcp2_ksl {
|
||||
/* nodelen is the actual size of ngtcp2_ksl_node including key
|
||||
storage. */
|
||||
size_t nodelen;
|
||||
const ngtcp2_mem *mem;
|
||||
};
|
||||
|
||||
/*
|
||||
* ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare
|
||||
* function. |keylen| is the length of key.
|
||||
*
|
||||
* It returns 0 if it succeeds, or one of the following negative error
|
||||
* codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
|
||||
const ngtcp2_mem *mem);
|
||||
void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is
|
||||
@@ -191,6 +195,17 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
|
||||
int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
|
||||
const ngtcp2_ksl_key *key);
|
||||
|
||||
/*
|
||||
* ngtcp2_ksl_remove_hint removes the |key| from |ksl|. |hint| must
|
||||
* point to the same node denoted by |key|. |hint| is used to remove
|
||||
* a node efficiently in some cases. Other than that, it behaves
|
||||
* exactly like ngtcp2_ksl_remove. |it| and |hint| can point to the
|
||||
* same object.
|
||||
*/
|
||||
int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
|
||||
const ngtcp2_ksl_it *hint,
|
||||
const ngtcp2_ksl_key *key);
|
||||
|
||||
/*
|
||||
* ngtcp2_ksl_lower_bound returns the iterator which points to the
|
||||
* first node which has the key which is equal to |key| or the last
|
||||
@@ -266,7 +281,8 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
|
||||
* |it| points to. It is undefined to call this function when
|
||||
* ngtcp2_ksl_it_end(it) returns nonzero.
|
||||
*/
|
||||
void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it);
|
||||
#define ngtcp2_ksl_it_get(IT) \
|
||||
ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
|
||||
|
||||
/*
|
||||
* ngtcp2_ksl_it_next advances the iterator by one. It is undefined
|
||||
|
||||
Vendored
+82
-43
@@ -34,6 +34,7 @@
|
||||
#include "ngtcp2_str.h"
|
||||
#include "ngtcp2_vec.h"
|
||||
#include "ngtcp2_macro.h"
|
||||
#include "ngtcp2_conv.h"
|
||||
|
||||
void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
|
||||
ngtcp2_printf log_printf, ngtcp2_tstamp ts,
|
||||
@@ -69,7 +70,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
|
||||
*
|
||||
* # Frame event
|
||||
*
|
||||
* <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>)
|
||||
* <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>)
|
||||
*
|
||||
* <DIR>:
|
||||
* Flow direction. tx=transmission, rx=reception
|
||||
@@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
|
||||
* Packet number.
|
||||
*
|
||||
* <PKTNAME>:
|
||||
* Packet name. (e.g., Initial, Handshake, S01)
|
||||
*
|
||||
* <PKTTYPE>:
|
||||
* Packet type in hex string.
|
||||
* Packet name. (e.g., Initial, Handshake, 1RTT)
|
||||
*
|
||||
* <FRAMENAME>:
|
||||
* Frame name. (e.g., STREAM, ACK, PING)
|
||||
@@ -94,16 +92,16 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
|
||||
|
||||
/* TODO Split second and remaining fraction with comma */
|
||||
#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s"
|
||||
#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)"
|
||||
#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s"
|
||||
#define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters"
|
||||
|
||||
#define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \
|
||||
timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \
|
||||
(DIR), hd->pkt_num, strpkttype(hd), hd->type
|
||||
(DIR), hd->pkt_num, strpkttype(hd)
|
||||
|
||||
#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \
|
||||
timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \
|
||||
(DIR), hd->pkt_num, strpkttype(hd), hd->type
|
||||
(DIR), hd->pkt_num, strpkttype(hd)
|
||||
|
||||
#define NGTCP2_LOG_TP_HD_FIELDS \
|
||||
timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry"
|
||||
@@ -140,6 +138,8 @@ static const char *strerrorcode(uint64_t error_code) {
|
||||
return "CRYPTO_BUFFER_EXCEEDED";
|
||||
case NGTCP2_KEY_UPDATE_ERROR:
|
||||
return "KEY_UPDATE_ERROR";
|
||||
case NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT:
|
||||
return "VERSION_NEGOTIATION_ERROR";
|
||||
default:
|
||||
if (0x100u <= error_code && error_code <= 0x1ffu) {
|
||||
return "CRYPTO_ERROR";
|
||||
@@ -155,8 +155,6 @@ static const char *strapperrorcode(uint64_t app_error_code) {
|
||||
|
||||
static const char *strpkttype_long(uint8_t type) {
|
||||
switch (type) {
|
||||
case NGTCP2_PKT_VERSION_NEGOTIATION:
|
||||
return "VN";
|
||||
case NGTCP2_PKT_INITIAL:
|
||||
return "Initial";
|
||||
case NGTCP2_PKT_RETRY:
|
||||
@@ -174,7 +172,26 @@ static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
|
||||
if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
|
||||
return strpkttype_long(hd->type);
|
||||
}
|
||||
return "Short";
|
||||
|
||||
switch (hd->type) {
|
||||
case NGTCP2_PKT_VERSION_NEGOTIATION:
|
||||
return "VN";
|
||||
case NGTCP2_PKT_STATELESS_RESET:
|
||||
return "SR";
|
||||
case NGTCP2_PKT_1RTT:
|
||||
return "1RTT";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) {
|
||||
ngtcp2_pkt_hd hd = {0};
|
||||
|
||||
hd.type = type;
|
||||
hd.flags = flags;
|
||||
|
||||
return strpkttype(&hd);
|
||||
}
|
||||
|
||||
static const char *strevent(ngtcp2_log_event ev) {
|
||||
@@ -201,13 +218,13 @@ static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; }
|
||||
|
||||
static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
const ngtcp2_stream *fr, const char *dir) {
|
||||
log->log_printf(log->user_data,
|
||||
(NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64
|
||||
" fin=%d offset=%" PRIu64 " len=%zu uni=%d"),
|
||||
NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags,
|
||||
fr->stream_id, fr->fin, fr->offset,
|
||||
ngtcp2_vec_len(fr->data, fr->datacnt),
|
||||
(fr->stream_id & 0x2) != 0);
|
||||
log->log_printf(
|
||||
log->user_data,
|
||||
(NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 " fin=%d offset=%" PRIu64
|
||||
" len=%" PRIu64 " uni=%d"),
|
||||
NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id,
|
||||
fr->fin, fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt),
|
||||
(fr->stream_id & 0x2) != 0);
|
||||
}
|
||||
|
||||
static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
@@ -396,17 +413,11 @@ static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
|
||||
static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
const ngtcp2_crypto *fr, const char *dir) {
|
||||
size_t datalen = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < fr->datacnt; ++i) {
|
||||
datalen += fr->data[i].len;
|
||||
}
|
||||
|
||||
log->log_printf(
|
||||
log->user_data,
|
||||
(NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64),
|
||||
NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen);
|
||||
NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset,
|
||||
ngtcp2_vec_len(fr->data, fr->datacnt));
|
||||
}
|
||||
|
||||
static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
@@ -446,7 +457,8 @@ static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
|
||||
static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
const ngtcp2_datagram *fr, const char *dir) {
|
||||
log->log_printf(log->user_data, (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%zu"),
|
||||
log->log_printf(log->user_data,
|
||||
(NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%" PRIu64),
|
||||
NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
|
||||
ngtcp2_vec_len(fr->data, fr->datacnt));
|
||||
}
|
||||
@@ -570,6 +582,8 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
|
||||
|
||||
memset(&shd, 0, sizeof(shd));
|
||||
|
||||
shd.type = NGTCP2_PKT_STATELESS_RESET;
|
||||
|
||||
log->log_printf(
|
||||
log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"),
|
||||
NGTCP2_LOG_PKT_HD_FIELDS("rx"),
|
||||
@@ -583,6 +597,7 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
|
||||
uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1];
|
||||
uint8_t addr[16 * 2 + 7 + 1];
|
||||
uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1];
|
||||
size_t i;
|
||||
|
||||
if (!log->log_printf) {
|
||||
return;
|
||||
@@ -694,6 +709,26 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
|
||||
log->log_printf(log->user_data,
|
||||
(NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64),
|
||||
NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size);
|
||||
log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"),
|
||||
NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit);
|
||||
|
||||
if (params->version_info_present) {
|
||||
log->log_printf(
|
||||
log->user_data,
|
||||
(NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"),
|
||||
NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version);
|
||||
|
||||
assert(!(params->version_info.other_versionslen & 0x3));
|
||||
|
||||
for (i = 0; i < params->version_info.other_versionslen;
|
||||
i += sizeof(uint32_t)) {
|
||||
log->log_printf(
|
||||
log->user_data,
|
||||
(NGTCP2_LOG_TP " version_information.other_versions[%zu]=0x%08x"),
|
||||
NGTCP2_LOG_TP_HD_FIELDS, i >> 2,
|
||||
ngtcp2_get_uint32(¶ms->version_info.other_versions[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
|
||||
@@ -702,11 +737,9 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
|
||||
return;
|
||||
}
|
||||
|
||||
ngtcp2_log_info(
|
||||
log, NGTCP2_LOG_EVENT_RCV,
|
||||
"pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num,
|
||||
(flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short",
|
||||
type, sent_ts);
|
||||
ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV,
|
||||
"pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num,
|
||||
strpkttype_type_flags(type, flags), sent_ts);
|
||||
}
|
||||
|
||||
static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
@@ -718,15 +751,21 @@ static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
|
||||
return;
|
||||
}
|
||||
|
||||
ngtcp2_log_info(
|
||||
log, NGTCP2_LOG_EVENT_PKT,
|
||||
"%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d",
|
||||
dir, hd->pkt_num,
|
||||
(const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
|
||||
(const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
|
||||
(hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type)
|
||||
: "Short",
|
||||
hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
|
||||
if (hd->type == NGTCP2_PKT_1RTT) {
|
||||
ngtcp2_log_info(
|
||||
log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d",
|
||||
dir, hd->pkt_num,
|
||||
(const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
|
||||
strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
|
||||
} else {
|
||||
ngtcp2_log_info(
|
||||
log, NGTCP2_LOG_EVENT_PKT,
|
||||
"%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu",
|
||||
dir, hd->pkt_num,
|
||||
(const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
|
||||
(const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
|
||||
hd->version, strpkttype(hd), hd->len);
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
|
||||
@@ -762,6 +801,6 @@ void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
|
||||
|
||||
void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
|
||||
ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT,
|
||||
"cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num,
|
||||
strpkttype(hd), hd->type);
|
||||
"cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num,
|
||||
strpkttype(hd));
|
||||
}
|
||||
Vendored
+47
-4
@@ -33,9 +33,7 @@
|
||||
|
||||
#include "ngtcp2_pkt.h"
|
||||
|
||||
typedef struct ngtcp2_log ngtcp2_log;
|
||||
|
||||
struct ngtcp2_log {
|
||||
typedef struct ngtcp2_log {
|
||||
/* log_printf is a sink to write log. NULL means no logging
|
||||
output. */
|
||||
ngtcp2_printf log_printf;
|
||||
@@ -49,7 +47,44 @@ struct ngtcp2_log {
|
||||
void *user_data;
|
||||
/* scid is SCID encoded as NULL-terminated hex string. */
|
||||
uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1];
|
||||
};
|
||||
} ngtcp2_log;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
* :type:`ngtcp2_log_event` defines an event of ngtcp2 library
|
||||
* internal logger.
|
||||
*/
|
||||
typedef enum ngtcp2_log_event {
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_NONE` represents no event.
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_NONE,
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_CON,
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event.
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_PKT,
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event.
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_FRM,
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event.
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_RCV,
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event.
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_CRY,
|
||||
/**
|
||||
* :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event.
|
||||
*/
|
||||
NGTCP2_LOG_EVENT_PTV,
|
||||
} ngtcp2_log_event;
|
||||
|
||||
void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
|
||||
ngtcp2_printf log_printf, ngtcp2_tstamp ts,
|
||||
@@ -77,4 +112,12 @@ void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
|
||||
|
||||
void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_log_info` writes info level log.
|
||||
*/
|
||||
void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
|
||||
...);
|
||||
|
||||
#endif /* NGTCP2_LOG_H */
|
||||
Vendored
+199
-195
@@ -27,170 +27,166 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ngtcp2_conv.h"
|
||||
|
||||
#define INITIAL_TABLE_LENGTH 256
|
||||
#define NGTCP2_INITIAL_TABLE_LENBITS 4
|
||||
|
||||
int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
|
||||
void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
|
||||
map->mem = mem;
|
||||
map->tablelen = INITIAL_TABLE_LENGTH;
|
||||
map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket));
|
||||
if (map->table == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
map->tablelen = 0;
|
||||
map->tablelenbits = 0;
|
||||
map->table = NULL;
|
||||
map->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_map_free(ngtcp2_map *map) {
|
||||
size_t i;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->ksl) {
|
||||
ngtcp2_ksl_free(bkt->ksl);
|
||||
ngtcp2_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
}
|
||||
|
||||
ngtcp2_mem_free(map->mem, map->table);
|
||||
}
|
||||
|
||||
void ngtcp2_map_each_free(ngtcp2_map *map,
|
||||
int (*func)(ngtcp2_map_entry *entry, void *ptr),
|
||||
void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
uint32_t i;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
ngtcp2_ksl_it it;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
func(bkt->ptr, ptr);
|
||||
bkt->ptr = NULL;
|
||||
assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it);
|
||||
ngtcp2_ksl_it_next(&it)) {
|
||||
func(ngtcp2_ksl_it_get(&it), ptr);
|
||||
}
|
||||
|
||||
ngtcp2_ksl_free(bkt->ksl);
|
||||
ngtcp2_mem_free(map->mem, bkt->ksl);
|
||||
bkt->ksl = NULL;
|
||||
}
|
||||
func(bkt->data, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_map_each(ngtcp2_map *map,
|
||||
int (*func)(ngtcp2_map_entry *entry, void *ptr),
|
||||
int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr) {
|
||||
int rv;
|
||||
uint32_t i;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
ngtcp2_ksl_it it;
|
||||
|
||||
if (map->size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = func(bkt->ptr, ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it);
|
||||
ngtcp2_ksl_it_next(&it)) {
|
||||
rv = func(ngtcp2_ksl_it_get(&it), ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) {
|
||||
entry->key = key;
|
||||
}
|
||||
|
||||
/* FNV1a hash */
|
||||
static uint32_t hash(key_type key, uint32_t mod) {
|
||||
uint8_t *p, *end;
|
||||
uint32_t h = 0x811C9DC5u;
|
||||
|
||||
key = ngtcp2_htonl64(key);
|
||||
p = (uint8_t *)&key;
|
||||
end = p + sizeof(key_type);
|
||||
|
||||
for (; p != end;) {
|
||||
h ^= *p++;
|
||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
||||
}
|
||||
|
||||
return h & (mod - 1);
|
||||
}
|
||||
|
||||
static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
|
||||
return *(key_type *)lhs < *(key_type *)rhs;
|
||||
}
|
||||
|
||||
static int map_insert(ngtcp2_map *map, ngtcp2_map_bucket *table,
|
||||
uint32_t tablelen, ngtcp2_map_entry *entry) {
|
||||
uint32_t h = hash(entry->key, tablelen);
|
||||
ngtcp2_map_bucket *bkt = &table[h];
|
||||
const ngtcp2_mem *mem = map->mem;
|
||||
int rv;
|
||||
|
||||
if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) {
|
||||
bkt->ptr = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!bkt->ksl) {
|
||||
bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl));
|
||||
if (bkt->ksl == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
|
||||
}
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
|
||||
rv = func(bkt->data, ptr);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bkt->ptr = NULL;
|
||||
}
|
||||
|
||||
return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 */
|
||||
static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) {
|
||||
static uint32_t hash(ngtcp2_map_key_type key) {
|
||||
return (uint32_t)((key * 11400714819323198485llu) >> 32);
|
||||
}
|
||||
|
||||
static size_t h2idx(uint32_t hash, uint32_t bits) {
|
||||
return hash >> (32 - bits);
|
||||
}
|
||||
|
||||
static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
|
||||
ngtcp2_map_bucket *bkt, size_t idx) {
|
||||
return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
|
||||
}
|
||||
|
||||
static void map_bucket_swap(ngtcp2_map_bucket *bkt, uint32_t *phash,
|
||||
ngtcp2_map_key_type *pkey, void **pdata) {
|
||||
uint32_t h = bkt->hash;
|
||||
ngtcp2_map_key_type key = bkt->key;
|
||||
void *data = bkt->data;
|
||||
|
||||
bkt->hash = *phash;
|
||||
bkt->key = *pkey;
|
||||
bkt->data = *pdata;
|
||||
|
||||
*phash = h;
|
||||
*pkey = key;
|
||||
*pdata = data;
|
||||
}
|
||||
|
||||
static void map_bucket_set_data(ngtcp2_map_bucket *bkt, uint32_t hash,
|
||||
ngtcp2_map_key_type key, void *data) {
|
||||
bkt->hash = hash;
|
||||
bkt->key = key;
|
||||
bkt->data = data;
|
||||
}
|
||||
|
||||
void ngtcp2_map_print_distance(ngtcp2_map *map) {
|
||||
uint32_t i;
|
||||
size_t idx;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
fprintf(stderr, "@%u <EMPTY>\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = h2idx(bkt->hash, map->tablelenbits);
|
||||
fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
|
||||
bkt->hash, bkt->key, idx,
|
||||
distance(map->tablelen, map->tablelenbits, bkt, idx));
|
||||
}
|
||||
}
|
||||
|
||||
static int insert(ngtcp2_map_bucket *table, uint32_t tablelen,
|
||||
uint32_t tablelenbits, uint32_t hash, ngtcp2_map_key_type key,
|
||||
void *data) {
|
||||
size_t idx = h2idx(hash, tablelenbits);
|
||||
size_t d = 0, dd;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
|
||||
for (;;) {
|
||||
bkt = &table[idx];
|
||||
|
||||
if (bkt->data == NULL) {
|
||||
map_bucket_set_data(bkt, hash, key, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dd = distance(tablelen, tablelenbits, bkt, idx);
|
||||
if (d > dd) {
|
||||
map_bucket_swap(bkt, &hash, &key, &data);
|
||||
d = dd;
|
||||
} else if (bkt->key == key) {
|
||||
/* TODO This check is just a waste after first swap or if this
|
||||
function is called from map_resize. That said, there is no
|
||||
difference with or without this conditional in performance
|
||||
wise. */
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 and new_tablelen == (1 <<
|
||||
new_tablelenbits) must hold. */
|
||||
static int map_resize(ngtcp2_map *map, uint32_t new_tablelen,
|
||||
uint32_t new_tablelenbits) {
|
||||
uint32_t i;
|
||||
ngtcp2_map_bucket *new_table;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
ngtcp2_ksl_it it;
|
||||
int rv;
|
||||
(void)rv;
|
||||
|
||||
new_table =
|
||||
ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket));
|
||||
@@ -200,64 +196,46 @@ static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) {
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
|
||||
if (bkt->ptr) {
|
||||
rv = map_insert(map, new_table, new_tablelen, bkt->ptr);
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
|
||||
if (bkt->data == NULL) {
|
||||
continue;
|
||||
}
|
||||
rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
|
||||
bkt->data);
|
||||
|
||||
if (bkt->ksl) {
|
||||
for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it);
|
||||
ngtcp2_ksl_it_next(&it)) {
|
||||
rv = map_insert(map, new_table, new_tablelen, ngtcp2_ksl_it_get(&it));
|
||||
if (rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
if (bkt->ksl) {
|
||||
ngtcp2_ksl_free(bkt->ksl);
|
||||
ngtcp2_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
assert(0 == rv);
|
||||
}
|
||||
|
||||
ngtcp2_mem_free(map->mem, map->table);
|
||||
map->tablelen = new_tablelen;
|
||||
map->tablelenbits = new_tablelenbits;
|
||||
map->table = new_table;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (i = 0; i < new_tablelen; ++i) {
|
||||
bkt = &new_table[i];
|
||||
if (bkt->ksl) {
|
||||
ngtcp2_ksl_free(bkt->ksl);
|
||||
ngtcp2_mem_free(map->mem, bkt->ksl);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) {
|
||||
int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
|
||||
int rv;
|
||||
|
||||
assert(data);
|
||||
|
||||
/* Load factor is 0.75 */
|
||||
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
||||
rv = map_resize(map, map->tablelen * 2);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
if (map->tablelen) {
|
||||
rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS,
|
||||
NGTCP2_INITIAL_TABLE_LENBITS);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = map_insert(map, map->table, map->tablelen, new_entry);
|
||||
|
||||
rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
|
||||
data);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
@@ -265,67 +243,93 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) {
|
||||
ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
||||
ngtcp2_ksl_it it;
|
||||
void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) {
|
||||
uint32_t h;
|
||||
size_t idx;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
size_t d = 0;
|
||||
|
||||
if (bkt->ptr) {
|
||||
if (bkt->ptr->key == key) {
|
||||
return bkt->ptr;
|
||||
}
|
||||
if (map->size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
it = ngtcp2_ksl_lower_bound(bkt->ksl, &key);
|
||||
if (ngtcp2_ksl_it_end(&it) || *(key_type *)ngtcp2_ksl_it_key(&it) != key) {
|
||||
h = hash(key);
|
||||
idx = h2idx(h, map->tablelenbits);
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL ||
|
||||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||
return NULL;
|
||||
}
|
||||
return ngtcp2_ksl_it_get(&it);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
if (bkt->key == key) {
|
||||
return bkt->data;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_map_remove(ngtcp2_map *map, key_type key) {
|
||||
ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
||||
int rv;
|
||||
int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) {
|
||||
uint32_t h;
|
||||
size_t idx, didx;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
size_t d = 0;
|
||||
|
||||
if (bkt->ptr) {
|
||||
if (bkt->ptr->key == key) {
|
||||
bkt->ptr = NULL;
|
||||
--map->size;
|
||||
return 0;
|
||||
}
|
||||
if (map->size == 0) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->ksl) {
|
||||
rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
--map->size;
|
||||
return 0;
|
||||
}
|
||||
h = hash(key);
|
||||
idx = h2idx(h, map->tablelenbits);
|
||||
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
|
||||
if (bkt->data == NULL ||
|
||||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (bkt->key == key) {
|
||||
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||
|
||||
didx = idx;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
|
||||
for (;;) {
|
||||
bkt = &map->table[idx];
|
||||
if (bkt->data == NULL ||
|
||||
distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
map->table[didx] = *bkt;
|
||||
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||
didx = idx;
|
||||
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
|
||||
--map->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
++d;
|
||||
idx = (idx + 1) & (map->tablelen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_map_clear(ngtcp2_map *map) {
|
||||
uint32_t i;
|
||||
ngtcp2_map_bucket *bkt;
|
||||
|
||||
for (i = 0; i < map->tablelen; ++i) {
|
||||
bkt = &map->table[i];
|
||||
bkt->ptr = NULL;
|
||||
if (bkt->ksl) {
|
||||
ngtcp2_ksl_free(bkt->ksl);
|
||||
ngtcp2_mem_free(map->mem, bkt->ksl);
|
||||
bkt->ksl = NULL;
|
||||
}
|
||||
if (map->tablelen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
|
||||
map->size = 0;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+24
-40
@@ -33,21 +33,15 @@
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "ngtcp2_mem.h"
|
||||
#include "ngtcp2_ksl.h"
|
||||
|
||||
/* Implementation of unordered map */
|
||||
|
||||
typedef uint64_t key_type;
|
||||
|
||||
typedef struct ngtcp2_map_entry ngtcp2_map_entry;
|
||||
|
||||
struct ngtcp2_map_entry {
|
||||
key_type key;
|
||||
};
|
||||
typedef uint64_t ngtcp2_map_key_type;
|
||||
|
||||
typedef struct ngtcp2_map_bucket {
|
||||
ngtcp2_map_entry *ptr;
|
||||
ngtcp2_ksl *ksl;
|
||||
uint32_t hash;
|
||||
ngtcp2_map_key_type key;
|
||||
void *data;
|
||||
} ngtcp2_map_bucket;
|
||||
|
||||
typedef struct ngtcp2_map {
|
||||
@@ -55,18 +49,13 @@ typedef struct ngtcp2_map {
|
||||
const ngtcp2_mem *mem;
|
||||
size_t size;
|
||||
uint32_t tablelen;
|
||||
uint32_t tablelenbits;
|
||||
} ngtcp2_map;
|
||||
|
||||
/*
|
||||
* Initializes the map |map|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
|
||||
void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |map|. The stored entries
|
||||
@@ -78,21 +67,14 @@ void ngtcp2_map_free(ngtcp2_map *map);
|
||||
/*
|
||||
* Deallocates each entries using |func| function and any resources
|
||||
* allocated for |map|. The |func| function is responsible for freeing
|
||||
* given the |entry| object. The |ptr| will be passed to the |func| as
|
||||
* given the |data| object. The |ptr| will be passed to the |func| as
|
||||
* send argument. The return value of the |func| will be ignored.
|
||||
*/
|
||||
void ngtcp2_map_each_free(ngtcp2_map *map,
|
||||
int (*func)(ngtcp2_map_entry *entry, void *ptr),
|
||||
void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr);
|
||||
|
||||
/*
|
||||
* Initializes the |entry| with the |key|. All entries to be inserted
|
||||
* to the map must be initialized with this function.
|
||||
*/
|
||||
void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key);
|
||||
|
||||
/*
|
||||
* Inserts the new |entry| with the key |entry->key| to the map |map|.
|
||||
* Inserts the new |data| with the |key| to the map |map|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -102,25 +84,25 @@ void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key);
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry);
|
||||
int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data);
|
||||
|
||||
/*
|
||||
* Returns the entry associated by the key |key|. If there is no such
|
||||
* entry, this function returns NULL.
|
||||
* Returns the data associated by the key |key|. If there is no such
|
||||
* data, this function returns NULL.
|
||||
*/
|
||||
ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key);
|
||||
void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key);
|
||||
|
||||
/*
|
||||
* Removes the entry associated by the key |key| from the |map|. The
|
||||
* removed entry is not freed by this function.
|
||||
* Removes the data associated by the key |key| from the |map|. The
|
||||
* removed data is not freed by this function.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_INVALID_ARGUMENT
|
||||
* The entry associated by |key| does not exist.
|
||||
* The data associated by |key| does not exist.
|
||||
*/
|
||||
int ngtcp2_map_remove(ngtcp2_map *map, key_type key);
|
||||
int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key);
|
||||
|
||||
/*
|
||||
* Removes all entries from |map|.
|
||||
@@ -133,20 +115,22 @@ void ngtcp2_map_clear(ngtcp2_map *map);
|
||||
size_t ngtcp2_map_size(ngtcp2_map *map);
|
||||
|
||||
/*
|
||||
* Applies the function |func| to each entry in the |map| with the
|
||||
* Applies the function |func| to each data in the |map| with the
|
||||
* optional user supplied pointer |ptr|.
|
||||
*
|
||||
* If the |func| returns 0, this function calls the |func| with the
|
||||
* next entry. If the |func| returns nonzero, it will not call the
|
||||
* next data. If the |func| returns nonzero, it will not call the
|
||||
* |func| for further entries and return the return value of the
|
||||
* |func| immediately. Thus, this function returns 0 if all the
|
||||
* invocations of the |func| return 0, or nonzero value which the last
|
||||
* invocation of |func| returns.
|
||||
*
|
||||
* Don't use this function to free each entry. Use
|
||||
* Don't use this function to free each data. Use
|
||||
* ngtcp2_map_each_free() instead.
|
||||
*/
|
||||
int ngtcp2_map_each(ngtcp2_map *map,
|
||||
int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr);
|
||||
int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
|
||||
void *ptr);
|
||||
|
||||
void ngtcp2_map_print_distance(ngtcp2_map *map);
|
||||
|
||||
#endif /* NGTCP2_MAP_H */
|
||||
Vendored
+54
-16
@@ -25,26 +25,28 @@
|
||||
*/
|
||||
#include "ngtcp2_mem.h"
|
||||
|
||||
static void *default_malloc(size_t size, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
#include <stdio.h>
|
||||
|
||||
static void *default_malloc(size_t size, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void default_free(void *ptr, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
static void default_free(void *ptr, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
|
||||
(void)mem_user_data;
|
||||
static void *default_realloc(void *ptr, size_t size, void *user_data) {
|
||||
(void)user_data;
|
||||
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
@@ -54,22 +56,58 @@ static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free,
|
||||
|
||||
const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; }
|
||||
|
||||
#ifndef MEMDEBUG
|
||||
void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) {
|
||||
return mem->malloc(size, mem->mem_user_data);
|
||||
return mem->malloc(size, mem->user_data);
|
||||
}
|
||||
|
||||
void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) {
|
||||
mem->free(ptr, mem->mem_user_data);
|
||||
}
|
||||
|
||||
void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) {
|
||||
free_func(ptr, mem_user_data);
|
||||
mem->free(ptr, mem->user_data);
|
||||
}
|
||||
|
||||
void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) {
|
||||
return mem->calloc(nmemb, size, mem->mem_user_data);
|
||||
return mem->calloc(nmemb, size, mem->user_data);
|
||||
}
|
||||
|
||||
void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) {
|
||||
return mem->realloc(ptr, size, mem->mem_user_data);
|
||||
return mem->realloc(ptr, size, mem->user_data);
|
||||
}
|
||||
#else /* MEMDEBUG */
|
||||
void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
|
||||
const char *func, const char *file, size_t line) {
|
||||
void *nptr = mem->malloc(size, mem->user_data);
|
||||
|
||||
fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
|
||||
file, line);
|
||||
|
||||
return nptr;
|
||||
}
|
||||
|
||||
void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
|
||||
const char *file, size_t line) {
|
||||
fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
|
||||
|
||||
mem->free(ptr, mem->user_data);
|
||||
}
|
||||
|
||||
void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
|
||||
const char *func, const char *file, size_t line) {
|
||||
void *nptr = mem->calloc(nmemb, size, mem->user_data);
|
||||
|
||||
fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
|
||||
size, func, file, line);
|
||||
|
||||
return nptr;
|
||||
}
|
||||
|
||||
void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
|
||||
const char *func, const char *file,
|
||||
size_t line) {
|
||||
void *nptr = mem->realloc(ptr, size, mem->user_data);
|
||||
|
||||
fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
|
||||
size, func, file, line);
|
||||
|
||||
return nptr;
|
||||
}
|
||||
#endif /* MEMDEBUG */
|
||||
Vendored
+30
-1
@@ -34,10 +34,39 @@
|
||||
|
||||
/* Convenient wrapper functions to call allocator function in
|
||||
|mem|. */
|
||||
#ifndef MEMDEBUG
|
||||
void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size);
|
||||
|
||||
void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr);
|
||||
void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data);
|
||||
|
||||
void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size);
|
||||
|
||||
void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size);
|
||||
#else /* MEMDEBUG */
|
||||
void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
|
||||
const char *func, const char *file, size_t line);
|
||||
|
||||
# define ngtcp2_mem_malloc(MEM, SIZE) \
|
||||
ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
|
||||
|
||||
void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
|
||||
const char *file, size_t line);
|
||||
|
||||
# define ngtcp2_mem_free(MEM, PTR) \
|
||||
ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
|
||||
|
||||
void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
|
||||
const char *func, const char *file, size_t line);
|
||||
|
||||
# define ngtcp2_mem_calloc(MEM, NMEMB, SIZE) \
|
||||
ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
|
||||
__LINE__)
|
||||
|
||||
void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
|
||||
const char *func, const char *file, size_t line);
|
||||
|
||||
# define ngtcp2_mem_realloc(MEM, PTR, SIZE) \
|
||||
ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__)
|
||||
#endif /* MEMDEBUG */
|
||||
|
||||
#endif /* NGTCP2_MEM_H */
|
||||
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_NET_H
|
||||
#define NGTCP2_NET_H
|
||||
|
||||
/* This header file is explicitly allowed to be shared with
|
||||
ngtcp2_crypto library. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
#endif /* HAVE_ARPA_INET_H */
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
# include <byteswap.h>
|
||||
#endif /* HAVE_BYTESWAP_H */
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
# include <endian.h>
|
||||
#endif /* HAVE_ENDIAN_H */
|
||||
|
||||
#ifdef HAVE_SYS_ENDIAN_H
|
||||
# include <sys/endian.h>
|
||||
#endif /* HAVE_SYS_ENDIAN_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#if defined(HAVE_BSWAP_64) || \
|
||||
(defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
|
||||
# define ngtcp2_bswap64 bswap_64
|
||||
#else /* !HAVE_BSWAP_64 */
|
||||
# define ngtcp2_bswap64(N) \
|
||||
((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \
|
||||
ngtcp2_ntohl((uint32_t)((N) >> 32)))
|
||||
#endif /* !HAVE_BSWAP_64 */
|
||||
|
||||
#if defined(HAVE_BE64TOH) || \
|
||||
(defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
|
||||
# define ngtcp2_ntohl64(N) be64toh(N)
|
||||
# define ngtcp2_htonl64(N) htobe64(N)
|
||||
#else /* !HAVE_BE64TOH */
|
||||
# if defined(WORDS_BIGENDIAN)
|
||||
# define ngtcp2_ntohl64(N) (N)
|
||||
# define ngtcp2_htonl64(N) (N)
|
||||
# else /* !WORDS_BIGENDIAN */
|
||||
# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
|
||||
# define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
|
||||
# endif /* !WORDS_BIGENDIAN */
|
||||
#endif /* !HAVE_BE64TOH */
|
||||
|
||||
#if defined(WIN32)
|
||||
/* Windows requires ws2_32 library for ntonl family functions. We
|
||||
define inline functions for those function so that we don't have
|
||||
dependeny on that lib. */
|
||||
|
||||
# ifdef _MSC_VER
|
||||
# define STIN static __inline
|
||||
# else
|
||||
# define STIN static inline
|
||||
# endif
|
||||
|
||||
STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
|
||||
uint32_t res;
|
||||
unsigned char *p = (unsigned char *)&res;
|
||||
*p++ = (unsigned char)(hostlong >> 24);
|
||||
*p++ = (hostlong >> 16) & 0xffu;
|
||||
*p++ = (hostlong >> 8) & 0xffu;
|
||||
*p = hostlong & 0xffu;
|
||||
return res;
|
||||
}
|
||||
|
||||
STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
|
||||
uint16_t res;
|
||||
unsigned char *p = (unsigned char *)&res;
|
||||
*p++ = (unsigned char)(hostshort >> 8);
|
||||
*p = hostshort & 0xffu;
|
||||
return res;
|
||||
}
|
||||
|
||||
STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
|
||||
uint32_t res;
|
||||
unsigned char *p = (unsigned char *)&netlong;
|
||||
res = *p++ << 24;
|
||||
res += *p++ << 16;
|
||||
res += *p++ << 8;
|
||||
res += *p;
|
||||
return res;
|
||||
}
|
||||
|
||||
STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
|
||||
uint16_t res;
|
||||
unsigned char *p = (unsigned char *)&netshort;
|
||||
res = *p++ << 8;
|
||||
res += *p;
|
||||
return res;
|
||||
}
|
||||
|
||||
#else /* !WIN32 */
|
||||
|
||||
# define ngtcp2_htonl htonl
|
||||
# define ngtcp2_htons htons
|
||||
# define ngtcp2_ntohl ntohl
|
||||
# define ngtcp2_ntohs ntohs
|
||||
|
||||
#endif /* !WIN32 */
|
||||
|
||||
#endif /* NGTCP2_NET_H */
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "ngtcp2_objalloc.h"
|
||||
|
||||
void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
|
||||
const ngtcp2_mem *mem) {
|
||||
ngtcp2_balloc_init(&objalloc->balloc, blklen, mem);
|
||||
ngtcp2_opl_init(&objalloc->opl);
|
||||
}
|
||||
|
||||
void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) {
|
||||
ngtcp2_balloc_free(&objalloc->balloc);
|
||||
}
|
||||
|
||||
void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) {
|
||||
ngtcp2_opl_clear(&objalloc->opl);
|
||||
ngtcp2_balloc_clear(&objalloc->balloc);
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_OBJALLOC_H
|
||||
#define NGTCP2_OBJALLOC_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include "ngtcp2_balloc.h"
|
||||
#include "ngtcp2_opl.h"
|
||||
#include "ngtcp2_macro.h"
|
||||
#include "ngtcp2_mem.h"
|
||||
|
||||
/*
|
||||
* ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides
|
||||
* an object pool with the custom allocator to reduce the allocation
|
||||
* and deallocation overheads for small objects.
|
||||
*/
|
||||
typedef struct ngtcp2_objalloc {
|
||||
ngtcp2_balloc balloc;
|
||||
ngtcp2_opl opl;
|
||||
} ngtcp2_objalloc;
|
||||
|
||||
/*
|
||||
* ngtcp2_objalloc_init initializes |objalloc|. |blklen| is directly
|
||||
* passed to ngtcp2_balloc_init.
|
||||
*/
|
||||
void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_objalloc_free releases all allocated resources.
|
||||
*/
|
||||
void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc);
|
||||
|
||||
/*
|
||||
* ngtcp2_objalloc_clear releases all allocated resources and
|
||||
* initializes its state.
|
||||
*/
|
||||
void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc);
|
||||
|
||||
#ifndef NOMEMPOOL
|
||||
# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \
|
||||
inline static void ngtcp2_objalloc_##NAME##_init( \
|
||||
ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
|
||||
ngtcp2_objalloc_init( \
|
||||
objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
|
||||
ngtcp2_objalloc *objalloc) { \
|
||||
ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \
|
||||
TYPE *obj; \
|
||||
int rv; \
|
||||
\
|
||||
if (!oplent) { \
|
||||
rv = \
|
||||
ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \
|
||||
if (rv != 0) { \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
return obj; \
|
||||
} \
|
||||
\
|
||||
return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \
|
||||
ngtcp2_objalloc *objalloc, size_t len) { \
|
||||
ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \
|
||||
TYPE *obj; \
|
||||
int rv; \
|
||||
\
|
||||
if (!oplent) { \
|
||||
rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len); \
|
||||
if (rv != 0) { \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
return obj; \
|
||||
} \
|
||||
\
|
||||
return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \
|
||||
} \
|
||||
\
|
||||
inline static void ngtcp2_objalloc_##NAME##_release( \
|
||||
ngtcp2_objalloc *objalloc, TYPE *obj) { \
|
||||
ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
|
||||
}
|
||||
#else /* NOMEMPOOL */
|
||||
# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \
|
||||
inline static void ngtcp2_objalloc_##NAME##_init( \
|
||||
ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
|
||||
ngtcp2_objalloc_init( \
|
||||
objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
|
||||
ngtcp2_objalloc *objalloc) { \
|
||||
return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
|
||||
} \
|
||||
\
|
||||
inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \
|
||||
ngtcp2_objalloc *objalloc, size_t len) { \
|
||||
return ngtcp2_mem_malloc(objalloc->balloc.mem, len); \
|
||||
} \
|
||||
\
|
||||
inline static void ngtcp2_objalloc_##NAME##_release( \
|
||||
ngtcp2_objalloc *objalloc, TYPE *obj) { \
|
||||
ngtcp2_mem_free(objalloc->balloc.mem, obj); \
|
||||
}
|
||||
#endif /* NOMEMPOOL */
|
||||
|
||||
#endif /* NGTCP2_OBJALLOC_H */
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "ngtcp2_opl.h"
|
||||
|
||||
void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; }
|
||||
|
||||
void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) {
|
||||
ent->next = opl->head;
|
||||
opl->head = ent;
|
||||
}
|
||||
|
||||
ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) {
|
||||
ngtcp2_opl_entry *ent = opl->head;
|
||||
|
||||
if (!ent) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
opl->head = ent->next;
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; }
|
||||
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_OPL_H
|
||||
#define NGTCP2_OPL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
typedef struct ngtcp2_opl_entry ngtcp2_opl_entry;
|
||||
|
||||
struct ngtcp2_opl_entry {
|
||||
ngtcp2_opl_entry *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* ngtcp2_opl is an object memory pool.
|
||||
*/
|
||||
typedef struct ngtcp2_opl {
|
||||
ngtcp2_opl_entry *head;
|
||||
} ngtcp2_opl;
|
||||
|
||||
/*
|
||||
* ngtcp2_opl_init initializes |opl|.
|
||||
*/
|
||||
void ngtcp2_opl_init(ngtcp2_opl *opl);
|
||||
|
||||
/*
|
||||
* ngtcp2_opl_push inserts |ent| to |opl| head.
|
||||
*/
|
||||
void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent);
|
||||
|
||||
/*
|
||||
* ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and
|
||||
* returns it. If |opl| does not have any entry, it returns NULL.
|
||||
*/
|
||||
ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl);
|
||||
|
||||
void ngtcp2_opl_clear(ngtcp2_opl *opl);
|
||||
|
||||
#endif /* NGTCP2_OPL_H */
|
||||
Vendored
+16
-13
@@ -37,6 +37,7 @@ void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
|
||||
void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) {
|
||||
ngtcp2_addr_copy(&dest->local, &src->local);
|
||||
ngtcp2_addr_copy(&dest->remote, &src->remote);
|
||||
dest->user_data = src->user_data;
|
||||
}
|
||||
|
||||
int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
|
||||
@@ -45,30 +46,32 @@ int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
|
||||
}
|
||||
|
||||
void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
|
||||
const struct sockaddr *local_addr,
|
||||
size_t local_addrlen, void *local_user_data,
|
||||
const struct sockaddr *remote_addr,
|
||||
size_t remote_addrlen, void *remote_user_data) {
|
||||
ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf,
|
||||
0, local_user_data);
|
||||
const ngtcp2_sockaddr *local_addr,
|
||||
ngtcp2_socklen local_addrlen,
|
||||
const ngtcp2_sockaddr *remote_addr,
|
||||
ngtcp2_socklen remote_addrlen, void *user_data) {
|
||||
ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
|
||||
0);
|
||||
ngtcp2_addr_init(&ps->path.remote,
|
||||
(const struct sockaddr *)&ps->remote_addrbuf, 0,
|
||||
remote_user_data);
|
||||
(const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
|
||||
|
||||
ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen);
|
||||
ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen);
|
||||
|
||||
ps->path.user_data = user_data;
|
||||
}
|
||||
|
||||
void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps,
|
||||
const ngtcp2_path *path) {
|
||||
ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen,
|
||||
path->local.user_data, path->remote.addr,
|
||||
path->remote.addrlen, path->remote.user_data);
|
||||
path->remote.addr, path->remote.addrlen,
|
||||
path->user_data);
|
||||
}
|
||||
|
||||
void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) {
|
||||
ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf,
|
||||
0, NULL);
|
||||
ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
|
||||
0);
|
||||
ngtcp2_addr_init(&ps->path.remote,
|
||||
(const struct sockaddr *)&ps->remote_addrbuf, 0, NULL);
|
||||
(const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
|
||||
ps->path.user_data = NULL;
|
||||
}
|
||||
Vendored
+194
-53
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ngtcp2_conv.h"
|
||||
#include "ngtcp2_str.h"
|
||||
@@ -61,13 +60,12 @@ void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
|
||||
ngtcp2_mem_free(mem, pc);
|
||||
}
|
||||
|
||||
int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
|
||||
size_t *pdcidlen, const uint8_t **pscid,
|
||||
size_t *pscidlen, const uint8_t *data,
|
||||
int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data,
|
||||
size_t datalen, size_t short_dcidlen) {
|
||||
size_t len;
|
||||
uint32_t version;
|
||||
size_t dcidlen, scidlen;
|
||||
int supported_version;
|
||||
|
||||
assert(datalen);
|
||||
|
||||
@@ -95,23 +93,31 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
|
||||
|
||||
version = ngtcp2_get_uint32(&data[1]);
|
||||
|
||||
if ((version == 0 || version == NGTCP2_PROTO_VER_V1 ||
|
||||
(NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
|
||||
version <= NGTCP2_PROTO_VER_DRAFT_MAX)) &&
|
||||
supported_version = ngtcp2_is_supported_version(version);
|
||||
|
||||
if (supported_version &&
|
||||
(dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
*pversion = version;
|
||||
*pdcid = &data[6];
|
||||
*pdcidlen = dcidlen;
|
||||
*pscid = &data[6 + dcidlen + 1];
|
||||
*pscidlen = scidlen;
|
||||
if (version && !supported_version &&
|
||||
datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (version && version != NGTCP2_PROTO_VER_V1 &&
|
||||
(version < NGTCP2_PROTO_VER_DRAFT_MIN ||
|
||||
NGTCP2_PROTO_VER_DRAFT_MAX < version)) {
|
||||
return 1;
|
||||
dest->version = version;
|
||||
dest->dcid = &data[6];
|
||||
dest->dcidlen = dcidlen;
|
||||
dest->scid = &data[6 + dcidlen + 1];
|
||||
dest->scidlen = scidlen;
|
||||
|
||||
if (!version) {
|
||||
/* VN */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!supported_version) {
|
||||
return NGTCP2_ERR_VERSION_NEGOTIATION;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -123,11 +129,11 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
*pversion = 0;
|
||||
*pdcid = &data[1];
|
||||
*pdcidlen = short_dcidlen;
|
||||
*pscid = NULL;
|
||||
*pscidlen = 0;
|
||||
dest->version = 0;
|
||||
dest->dcid = &data[1];
|
||||
dest->dcidlen = short_dcidlen;
|
||||
dest->scid = NULL;
|
||||
dest->scidlen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -170,6 +176,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
const uint8_t *token = NULL;
|
||||
size_t tokenlen = 0;
|
||||
uint64_t vi;
|
||||
uint8_t flags = NGTCP2_PKT_FLAG_LONG_FORM;
|
||||
|
||||
if (pktlen < 5) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
@@ -183,16 +190,20 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
|
||||
if (version == 0) {
|
||||
type = NGTCP2_PKT_VERSION_NEGOTIATION;
|
||||
/* Version Negotiation is not a long header packet. */
|
||||
flags = NGTCP2_PKT_FLAG_NONE;
|
||||
/* This must be Version Negotiation packet which lacks packet
|
||||
number and payload length fields. */
|
||||
len = 5 + 2;
|
||||
} else {
|
||||
if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
|
||||
}
|
||||
|
||||
type = ngtcp2_pkt_get_type_long(pkt[0]);
|
||||
type = ngtcp2_pkt_get_type_long(version, pkt[0]);
|
||||
switch (type) {
|
||||
case 0:
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
case NGTCP2_PKT_INITIAL:
|
||||
len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN -
|
||||
1; /* Cut packet number field */
|
||||
@@ -267,10 +278,15 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NGTCP2_PKT_VERSION_NEGOTIATION:
|
||||
case NGTCP2_PKT_RETRY:
|
||||
break;
|
||||
default:
|
||||
if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
|
||||
assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
|
||||
/* Version Negotiation is not a long header packet. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Length */
|
||||
n = ngtcp2_get_varint_len(p);
|
||||
len += n - 1;
|
||||
@@ -280,7 +296,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
}
|
||||
}
|
||||
|
||||
dest->flags = NGTCP2_PKT_FLAG_LONG_FORM;
|
||||
dest->flags = flags;
|
||||
dest->type = type;
|
||||
dest->version = version;
|
||||
dest->pkt_num = 0;
|
||||
@@ -297,11 +313,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
p += ntokenlen + tokenlen;
|
||||
|
||||
switch (type) {
|
||||
case NGTCP2_PKT_VERSION_NEGOTIATION:
|
||||
case NGTCP2_PKT_RETRY:
|
||||
dest->len = 0;
|
||||
break;
|
||||
default:
|
||||
if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
|
||||
assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
|
||||
/* Version Negotiation is not a long header packet. */
|
||||
dest->len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
vi = ngtcp2_get_varint(&n, p);
|
||||
if (vi > SIZE_MAX) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
@@ -319,6 +341,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
size_t pktlen, size_t dcidlen) {
|
||||
size_t len = 1 + dcidlen;
|
||||
const uint8_t *p = pkt;
|
||||
uint8_t flags = NGTCP2_PKT_FLAG_NONE;
|
||||
|
||||
assert(dcidlen <= NGTCP2_MAX_CIDLEN);
|
||||
|
||||
@@ -326,14 +349,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) ||
|
||||
(pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) {
|
||||
if (pkt[0] & NGTCP2_HEADER_FORM_BIT) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) {
|
||||
flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
|
||||
}
|
||||
|
||||
p = &pkt[1];
|
||||
|
||||
dest->type = NGTCP2_PKT_SHORT;
|
||||
dest->type = NGTCP2_PKT_1RTT;
|
||||
|
||||
ngtcp2_cid_init(&dest->dcid, p, dcidlen);
|
||||
p += dcidlen;
|
||||
@@ -342,11 +368,13 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
|
||||
garbage. */
|
||||
ngtcp2_cid_zero(&dest->scid);
|
||||
|
||||
dest->flags = NGTCP2_PKT_FLAG_NONE;
|
||||
dest->flags = flags;
|
||||
dest->version = 0;
|
||||
dest->len = 0;
|
||||
dest->pkt_num = 0;
|
||||
dest->pkt_numlen = 0;
|
||||
dest->token.base = NULL;
|
||||
dest->token.len = 0;
|
||||
|
||||
assert((size_t)(p - pkt) == len);
|
||||
|
||||
@@ -361,7 +389,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
|
||||
len and 1 byte for packet number. */
|
||||
|
||||
if (hd->type != NGTCP2_PKT_RETRY) {
|
||||
len += 2 /* Length */ + hd->pkt_numlen;
|
||||
len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen;
|
||||
}
|
||||
|
||||
if (hd->type == NGTCP2_PKT_INITIAL) {
|
||||
@@ -374,8 +402,15 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
|
||||
|
||||
p = out;
|
||||
|
||||
*p++ = (uint8_t)(NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK |
|
||||
(hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1));
|
||||
*p = (uint8_t)(NGTCP2_HEADER_FORM_BIT |
|
||||
(ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) |
|
||||
(uint8_t)(hd->pkt_numlen - 1));
|
||||
if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
|
||||
*p |= NGTCP2_FIXED_BIT_MASK;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
||||
p = ngtcp2_put_uint32be(p, hd->version);
|
||||
*p++ = (uint8_t)hd->dcid.datalen;
|
||||
if (hd->dcid.datalen) {
|
||||
@@ -394,7 +429,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
|
||||
}
|
||||
|
||||
if (hd->type != NGTCP2_PKT_RETRY) {
|
||||
p = ngtcp2_put_varint14(p, (uint16_t)hd->len);
|
||||
p = ngtcp2_put_varint30(p, (uint32_t)hd->len);
|
||||
p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
|
||||
}
|
||||
|
||||
@@ -414,7 +449,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
|
||||
|
||||
p = out;
|
||||
|
||||
*p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1);
|
||||
*p = (uint8_t)(hd->pkt_numlen - 1);
|
||||
if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
|
||||
*p |= NGTCP2_FIXED_BIT_MASK;
|
||||
}
|
||||
if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) {
|
||||
*p |= NGTCP2_SHORT_KEY_PHASE_BIT;
|
||||
}
|
||||
@@ -1428,7 +1466,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
|
||||
|
||||
vi = ngtcp2_get_varint(&n, p);
|
||||
if (payloadlen - len < vi) {
|
||||
return NGTCP2_FRAME_ENCODING_ERROR;
|
||||
return NGTCP2_ERR_FRAME_ENCODING;
|
||||
}
|
||||
|
||||
datalen = (size_t)vi;
|
||||
@@ -1436,6 +1474,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
|
||||
dest->type = type;
|
||||
@@ -2020,8 +2059,8 @@ ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen,
|
||||
|
||||
ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
|
||||
const ngtcp2_datagram *fr) {
|
||||
size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
|
||||
size_t len =
|
||||
uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
|
||||
uint64_t len =
|
||||
1 +
|
||||
(fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_varint_len(datalen)) +
|
||||
datalen;
|
||||
@@ -2039,7 +2078,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
|
||||
|
||||
*p++ = fr->type;
|
||||
if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) {
|
||||
p = ngtcp2_put_varint(p, (uint64_t)datalen);
|
||||
p = ngtcp2_put_varint(p, datalen);
|
||||
}
|
||||
|
||||
for (i = 0; i < fr->datacnt; ++i) {
|
||||
@@ -2228,8 +2267,8 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
|
||||
|
||||
/* Retry packet is sent at most once per one connection attempt. In
|
||||
the first connection attempt, client has to send random DCID
|
||||
which is at least 8 bytes long. */
|
||||
if (odcid->datalen < 8) {
|
||||
which is at least NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */
|
||||
if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
|
||||
return NGTCP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@@ -2244,10 +2283,16 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
|
||||
return pseudo_retrylen;
|
||||
}
|
||||
|
||||
if (version == NGTCP2_PROTO_VER_V1) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V1:
|
||||
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
|
||||
} else {
|
||||
break;
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
|
||||
break;
|
||||
default:
|
||||
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
|
||||
}
|
||||
@@ -2330,10 +2375,16 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
|
||||
|
||||
pseudo_retrylen = (size_t)(p - pseudo_retry);
|
||||
|
||||
if (version == NGTCP2_PROTO_VER_V1) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V1:
|
||||
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
|
||||
} else {
|
||||
break;
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
|
||||
break;
|
||||
default:
|
||||
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
|
||||
noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
|
||||
}
|
||||
@@ -2353,7 +2404,7 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
|
||||
}
|
||||
|
||||
size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
|
||||
size_t len, size_t left) {
|
||||
uint64_t len, size_t left) {
|
||||
size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) +
|
||||
(offset ? ngtcp2_put_varint_len(offset) : 0);
|
||||
|
||||
@@ -2367,21 +2418,21 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
len = ngtcp2_min(len, 4611686018427387903lu);
|
||||
#endif /* SIZE_MAX > UINT32_MAX */
|
||||
return ngtcp2_min(len, left - 8);
|
||||
return (size_t)ngtcp2_min(len, (uint64_t)(left - 8));
|
||||
}
|
||||
|
||||
if (left > 4 + 16383 && len > 16383) {
|
||||
len = ngtcp2_min(len, 1073741823);
|
||||
return ngtcp2_min(len, left - 4);
|
||||
return (size_t)ngtcp2_min(len, (uint64_t)(left - 4));
|
||||
}
|
||||
|
||||
if (left > 2 + 63 && len > 63) {
|
||||
len = ngtcp2_min(len, 16383);
|
||||
return ngtcp2_min(len, left - 2);
|
||||
return (size_t)ngtcp2_min(len, (uint64_t)(left - 2));
|
||||
}
|
||||
|
||||
len = ngtcp2_min(len, 63);
|
||||
return ngtcp2_min(len, left - 1);
|
||||
return (size_t)ngtcp2_min(len, (uint64_t)(left - 1));
|
||||
}
|
||||
|
||||
size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
|
||||
@@ -2417,11 +2468,101 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
|
||||
}
|
||||
|
||||
size_t ngtcp2_pkt_datagram_framelen(size_t len) {
|
||||
return 1 /* type */ + ngtcp2_put_varint_len((uint64_t)len) + len;
|
||||
return 1 /* type */ + ngtcp2_put_varint_len(len) + len;
|
||||
}
|
||||
|
||||
uint8_t ngtcp2_pkt_get_type_long(uint8_t c) {
|
||||
return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
|
||||
int ngtcp2_is_supported_version(uint32_t version) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V1:
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
return 1;
|
||||
default:
|
||||
return NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
|
||||
version <= NGTCP2_PROTO_VER_DRAFT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_is_reserved_version(uint32_t version) {
|
||||
return (version & NGTCP2_RESERVED_VERSION_MASK) ==
|
||||
NGTCP2_RESERVED_VERSION_MASK;
|
||||
}
|
||||
|
||||
uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) {
|
||||
uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
|
||||
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
switch (pkt_type) {
|
||||
case NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT:
|
||||
return NGTCP2_PKT_INITIAL;
|
||||
case NGTCP2_PKT_TYPE_0RTT_V2_DRAFT:
|
||||
return NGTCP2_PKT_0RTT;
|
||||
case NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT:
|
||||
return NGTCP2_PKT_HANDSHAKE;
|
||||
case NGTCP2_PKT_TYPE_RETRY_V2_DRAFT:
|
||||
return NGTCP2_PKT_RETRY;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
if (!ngtcp2_is_supported_version(version)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* QUIC v1 and draft versions share the same numeric packet
|
||||
types. */
|
||||
switch (pkt_type) {
|
||||
case NGTCP2_PKT_TYPE_INITIAL_V1:
|
||||
return NGTCP2_PKT_INITIAL;
|
||||
case NGTCP2_PKT_TYPE_0RTT_V1:
|
||||
return NGTCP2_PKT_0RTT;
|
||||
case NGTCP2_PKT_TYPE_HANDSHAKE_V1:
|
||||
return NGTCP2_PKT_HANDSHAKE;
|
||||
case NGTCP2_PKT_TYPE_RETRY_V1:
|
||||
return NGTCP2_PKT_RETRY;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) {
|
||||
switch (version) {
|
||||
case NGTCP2_PROTO_VER_V2_DRAFT:
|
||||
switch (pkt_type) {
|
||||
case NGTCP2_PKT_INITIAL:
|
||||
return NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT;
|
||||
case NGTCP2_PKT_0RTT:
|
||||
return NGTCP2_PKT_TYPE_0RTT_V2_DRAFT;
|
||||
case NGTCP2_PKT_HANDSHAKE:
|
||||
return NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT;
|
||||
case NGTCP2_PKT_RETRY:
|
||||
return NGTCP2_PKT_TYPE_RETRY_V2_DRAFT;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
default:
|
||||
/* Assume that unsupported versions share the numeric long packet
|
||||
types with QUIC v1 in order to send a packet to elicit Version
|
||||
Negotiation packet. */
|
||||
|
||||
/* QUIC v1 and draft versions share the same numeric packet
|
||||
types. */
|
||||
switch (pkt_type) {
|
||||
case NGTCP2_PKT_INITIAL:
|
||||
return NGTCP2_PKT_TYPE_INITIAL_V1;
|
||||
case NGTCP2_PKT_0RTT:
|
||||
return NGTCP2_PKT_TYPE_0RTT_V1;
|
||||
case NGTCP2_PKT_HANDSHAKE:
|
||||
return NGTCP2_PKT_TYPE_HANDSHAKE_V1;
|
||||
case NGTCP2_PKT_RETRY:
|
||||
return NGTCP2_PKT_TYPE_RETRY_V1;
|
||||
default:
|
||||
assert(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ngtcp2_pkt_verify_reserved_bits(uint8_t c) {
|
||||
|
||||
Vendored
+51
-6
@@ -103,6 +103,40 @@
|
||||
/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */
|
||||
#define NGTCP2_RETRY_TAGLEN 16
|
||||
|
||||
/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size
|
||||
that this library can write. */
|
||||
#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1)
|
||||
|
||||
/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by
|
||||
Length field in Long packet header. */
|
||||
#define NGTCP2_PKT_LENGTHLEN 4
|
||||
|
||||
/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for
|
||||
QUIC v1. */
|
||||
#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0
|
||||
/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC
|
||||
v1. */
|
||||
#define NGTCP2_PKT_TYPE_0RTT_V1 0x1
|
||||
/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type
|
||||
for QUIC v1. */
|
||||
#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2
|
||||
/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC
|
||||
v1. */
|
||||
#define NGTCP2_PKT_TYPE_RETRY_V1 0x3
|
||||
|
||||
/* NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT is Initial long header packet type
|
||||
for QUIC v2 draft. */
|
||||
#define NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT 0x1
|
||||
/* NGTCP2_PKT_TYPE_0RTT_V2_DRAFT is 0RTT long header packet type for
|
||||
QUIC v2 draft. */
|
||||
#define NGTCP2_PKT_TYPE_0RTT_V2_DRAFT 0x2
|
||||
/* NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT is Handshake long header packet
|
||||
type for QUIC v2 draft. */
|
||||
#define NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT 0x3
|
||||
/* NGTCP2_PKT_TYPE_RETRY_V2_DRAFT is Retry long header packet type for
|
||||
QUIC v2 draft. */
|
||||
#define NGTCP2_PKT_TYPE_RETRY_V2_DRAFT 0x0
|
||||
|
||||
typedef struct ngtcp2_pkt_retry {
|
||||
ngtcp2_cid odcid;
|
||||
ngtcp2_vec token;
|
||||
@@ -258,12 +292,12 @@ typedef struct ngtcp2_stop_sending {
|
||||
|
||||
typedef struct ngtcp2_path_challenge {
|
||||
uint8_t type;
|
||||
uint8_t data[8];
|
||||
uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
|
||||
} ngtcp2_path_challenge;
|
||||
|
||||
typedef struct ngtcp2_path_response {
|
||||
uint8_t type;
|
||||
uint8_t data[8];
|
||||
uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
|
||||
} ngtcp2_path_response;
|
||||
|
||||
typedef struct ngtcp2_crypto {
|
||||
@@ -293,6 +327,8 @@ typedef struct ngtcp2_handshake_done {
|
||||
|
||||
typedef struct ngtcp2_datagram {
|
||||
uint8_t type;
|
||||
/* dgram_id is an opaque identifier chosen by an application. */
|
||||
uint64_t dgram_id;
|
||||
/* datacnt is the number of elements that data contains. */
|
||||
size_t datacnt;
|
||||
/* data is a pointer to ngtcp2_vec array that stores data. */
|
||||
@@ -1123,7 +1159,7 @@ int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr);
|
||||
* small to write STREAM frame, this function returns (size_t)-1.
|
||||
*/
|
||||
size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
|
||||
size_t len, size_t left);
|
||||
uint64_t len, size_t left);
|
||||
|
||||
/*
|
||||
* ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes
|
||||
@@ -1184,12 +1220,21 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
|
||||
const ngtcp2_crypto_aead *aead,
|
||||
const ngtcp2_crypto_aead_ctx *aead_ctx);
|
||||
|
||||
/*
|
||||
* ngtcp2_pkt_versioned_type returns versioned packet type for a
|
||||
* version |version| that corresponds to the version-independent
|
||||
* |pkt_type|.
|
||||
*/
|
||||
uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is
|
||||
* the first byte of Long packet header.
|
||||
* `ngtcp2_pkt_get_type_long` returns the version-independent long
|
||||
* packet type. |version| is the QUIC version. |c| is the first byte
|
||||
* of Long packet header. If |version| is not supported by the
|
||||
* library, it returns 0.
|
||||
*/
|
||||
uint8_t ngtcp2_pkt_get_type_long(uint8_t c);
|
||||
uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c);
|
||||
|
||||
#endif /* NGTCP2_PKT_H */
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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 "ngtcp2_pmtud.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "ngtcp2_mem.h"
|
||||
#include "ngtcp2_macro.h"
|
||||
|
||||
/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent
|
||||
for each probe. */
|
||||
#define NGTCP2_PMTUD_PROBE_NUM_MAX 3
|
||||
|
||||
static size_t mtu_probes[] = {
|
||||
1454 - 48, /* The well known MTU used by a domestic optic fiber
|
||||
service in Japan. */
|
||||
1390 - 48, /* Typical Tunneled MTU */
|
||||
1280 - 48, /* IPv6 minimum MTU */
|
||||
1492 - 48, /* PPPoE */
|
||||
};
|
||||
|
||||
static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]);
|
||||
|
||||
int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
|
||||
size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
|
||||
const ngtcp2_mem *mem) {
|
||||
ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud));
|
||||
|
||||
if (pmtud == NULL) {
|
||||
return NGTCP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
pmtud->mem = mem;
|
||||
pmtud->mtu_idx = 0;
|
||||
pmtud->num_pkts_sent = 0;
|
||||
pmtud->expiry = UINT64_MAX;
|
||||
pmtud->tx_pkt_num = tx_pkt_num;
|
||||
pmtud->max_udp_payload_size = max_udp_payload_size;
|
||||
pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size;
|
||||
pmtud->min_fail_udp_payload_size = SIZE_MAX;
|
||||
|
||||
for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
|
||||
if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
|
||||
continue;
|
||||
}
|
||||
if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*ppmtud = pmtud;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) {
|
||||
if (!pmtud) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngtcp2_mem_free(pmtud->mem, pmtud);
|
||||
}
|
||||
|
||||
size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) {
|
||||
assert(pmtud->mtu_idx < mtu_probeslen);
|
||||
|
||||
return mtu_probes[pmtud->mtu_idx];
|
||||
}
|
||||
|
||||
void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
|
||||
ngtcp2_tstamp ts) {
|
||||
ngtcp2_tstamp timeout;
|
||||
|
||||
if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
|
||||
timeout = pto;
|
||||
} else {
|
||||
timeout = 3 * pto;
|
||||
}
|
||||
|
||||
pmtud->expiry = ts + timeout;
|
||||
}
|
||||
|
||||
int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) {
|
||||
return pmtud->expiry == UINT64_MAX;
|
||||
}
|
||||
|
||||
static void pmtud_next_probe(ngtcp2_pmtud *pmtud) {
|
||||
assert(pmtud->mtu_idx < mtu_probeslen);
|
||||
|
||||
++pmtud->mtu_idx;
|
||||
pmtud->num_pkts_sent = 0;
|
||||
pmtud->expiry = UINT64_MAX;
|
||||
|
||||
for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
|
||||
if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size ||
|
||||
mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) {
|
||||
pmtud->max_udp_payload_size =
|
||||
ngtcp2_max(pmtud->max_udp_payload_size, payloadlen);
|
||||
|
||||
assert(pmtud->mtu_idx < mtu_probeslen);
|
||||
|
||||
if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
pmtud_next_probe(pmtud);
|
||||
}
|
||||
|
||||
void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) {
|
||||
if (ts < pmtud->expiry) {
|
||||
return;
|
||||
}
|
||||
|
||||
pmtud->expiry = UINT64_MAX;
|
||||
|
||||
if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
pmtud->min_fail_udp_payload_size =
|
||||
ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]);
|
||||
|
||||
pmtud_next_probe(pmtud);
|
||||
}
|
||||
|
||||
int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) {
|
||||
return pmtud->mtu_idx >= mtu_probeslen;
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* ngtcp2
|
||||
*
|
||||
* Copyright (c) 2022 ngtcp2 contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NGTCP2_PMTUD_H
|
||||
#define NGTCP2_PMTUD_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
typedef struct ngtcp2_pmtud {
|
||||
const ngtcp2_mem *mem;
|
||||
/* mtu_idx is the index of UDP payload size candidates to try
|
||||
out. */
|
||||
size_t mtu_idx;
|
||||
/* num_pkts_sent is the number of mtu_idx sized UDP datagram payload
|
||||
sent */
|
||||
size_t num_pkts_sent;
|
||||
/* expiry is the expired, if it is reached, send out the next UDP
|
||||
datagram. UINT64_MAX means no expiry, or expiration is canceled.
|
||||
In either case, new probe packet should be sent unless we have
|
||||
done all attempts. */
|
||||
ngtcp2_tstamp expiry;
|
||||
/* tx_pkt_num is the smallest outgoing packet number where the
|
||||
current discovery is performed. In other words, acknowledging
|
||||
packet whose packet number lower than that does not indicate the
|
||||
success of Path MTU Discovery. */
|
||||
int64_t tx_pkt_num;
|
||||
/* max_udp_payload_size is the maximum UDP payload size which is
|
||||
known to work. */
|
||||
size_t max_udp_payload_size;
|
||||
/* hard_max_udp_payload_size is the maximum UDP payload size that is
|
||||
going to be probed. */
|
||||
size_t hard_max_udp_payload_size;
|
||||
/* min_fail_udp_payload_size is the minimum UDP payload size that is
|
||||
known to fail. */
|
||||
size_t min_fail_udp_payload_size;
|
||||
} ngtcp2_pmtud;
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its
|
||||
* pointer to |*ppmtud|. |max_udp_payload_size| is the maximum UDP
|
||||
* payload size that is known to work for the current path.
|
||||
* |tx_pkt_num| should be the next packet number to send, which is
|
||||
* used to differentiate the PMTUD probe packet sent by the previous
|
||||
* PMTUD. PMTUD might finish immediately if |max_udp_payload_size| is
|
||||
* larger than or equal to all UDP payload probe candidates.
|
||||
* Therefore, call ngtcp2_pmtud_finished to check this situation.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGTCP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
|
||||
size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
|
||||
const ngtcp2_mem *mem);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_del deletes |pmtud|.
|
||||
*/
|
||||
void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_probelen returns the length of UDP payload size for a
|
||||
* PMTUD probe packet.
|
||||
*/
|
||||
size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is
|
||||
* sent.
|
||||
*/
|
||||
void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
|
||||
ngtcp2_tstamp ts);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet
|
||||
* should be sent.
|
||||
*/
|
||||
int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe
|
||||
* UDP datagram sized |payloadlen| is acknowledged.
|
||||
*/
|
||||
void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_handle_expiry handles expiry.
|
||||
*/
|
||||
void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts);
|
||||
|
||||
/*
|
||||
* ngtcp2_pmtud_finished returns nonzero if PMTUD has finished.
|
||||
*/
|
||||
int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud);
|
||||
|
||||
#endif /* NGTCP2_PMTUD_H */
|
||||
Vendored
+2
-2
@@ -57,7 +57,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) {
|
||||
if (hd->type == NGTCP2_PKT_INITIAL) {
|
||||
ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len;
|
||||
}
|
||||
ppe->pkt_num_offset = ppe->len_offset + 2;
|
||||
ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN;
|
||||
rv = ngtcp2_pkt_encode_hd_long(
|
||||
buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
|
||||
} else {
|
||||
@@ -115,7 +115,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) {
|
||||
assert(cc->hp_mask);
|
||||
|
||||
if (ppe->len_offset) {
|
||||
ngtcp2_put_varint14(
|
||||
ngtcp2_put_varint30(
|
||||
buf->begin + ppe->len_offset,
|
||||
(uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead));
|
||||
}
|
||||
|
||||
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user