mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	Release 2.28.0
- [API] lsquic_ssl_sess_to_resume_info() is the new way to get session info. - [API] Add user pointer to ea_generate_scid callback. - [API] Add lsquic_dcid_from_packet() -- a fast function to parse out DCID. - [API] Add es_max_batch_size to control outgoing packet batch size. - [BUGFIX] Disallow sending of header while promise is being written. - [BUGFIX] Flush stream when buffered bytes exhaust stream cap. - [BUGFIX] Deactivate HQ frame if writing push promise fails. - Perform sanity check on peer transport parameters and fail the handshake if some flow control limits are too low. This can be turned off, see es_check_tp_sanity. - http_server: fix how requests are read in "hq" mode.
This commit is contained in:
		
							parent
							
								
									9a7f663e1a
								
							
						
					
					
						commit
						c2faf03244
					
				
					 19 changed files with 440 additions and 54 deletions
				
			
		
							
								
								
									
										16
									
								
								CHANGELOG
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								CHANGELOG
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,3 +1,19 @@
 | 
			
		|||
2021-02-03
 | 
			
		||||
    - 2.28.0
 | 
			
		||||
    - [API] lsquic_ssl_sess_to_resume_info() is the new way to get
 | 
			
		||||
      session info.
 | 
			
		||||
    - [API] Add user pointer to ea_generate_scid callback.
 | 
			
		||||
    - [API] Add lsquic_dcid_from_packet() -- a fast function to parse
 | 
			
		||||
      out DCID.
 | 
			
		||||
    - [API] Add es_max_batch_size to control outgoing packet batch size.
 | 
			
		||||
    - [BUGFIX] Disallow sending of header while promise is being written.
 | 
			
		||||
    - [BUGFIX] Flush stream when buffered bytes exhaust stream cap.
 | 
			
		||||
    - [BUGFIX] Deactivate HQ frame if writing push promise fails.
 | 
			
		||||
    - Perform sanity check on peer transport parameters and fail the
 | 
			
		||||
      handshake if some flow control limits are too low.  This can be
 | 
			
		||||
      turned off, see es_check_tp_sanity.
 | 
			
		||||
    - http_server: fix how requests are read in "hq" mode.
 | 
			
		||||
 | 
			
		||||
2021-01-27
 | 
			
		||||
    - 2.27.6
 | 
			
		||||
    - [BUGFIX] Replace dispatch read/write events assertion with a check.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -426,6 +426,7 @@ http_client_on_hsk_done (lsquic_conn_t *conn, enum lsquic_hsk_status status)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Now only used for gQUIC and will be going away after that */
 | 
			
		||||
static void
 | 
			
		||||
http_client_on_sess_resume_info (lsquic_conn_t *conn, const unsigned char *buf,
 | 
			
		||||
                                                                size_t bufsz)
 | 
			
		||||
| 
						 | 
				
			
			@ -1011,7 +1012,6 @@ usage (const char *prog)
 | 
			
		|||
"   -I          Abort on incomplete reponse from server\n"
 | 
			
		||||
"   -4          Prefer IPv4 when resolving hostname\n"
 | 
			
		||||
"   -6          Prefer IPv6 when resolving hostname\n"
 | 
			
		||||
"   -0 FILE     Provide RTT info file (reading or writing)\n"
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
"   -C DIR      Certificate store.  If specified, server certificate will\n"
 | 
			
		||||
"                 be verified.\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -1736,7 +1736,7 @@ main (int argc, char **argv)
 | 
			
		|||
        case '0':
 | 
			
		||||
            http_client_if.on_sess_resume_info = http_client_on_sess_resume_info;
 | 
			
		||||
            client_ctx.hcc_sess_resume_file_name = optarg;
 | 
			
		||||
            break;
 | 
			
		||||
            goto common_opts;
 | 
			
		||||
        case '3':
 | 
			
		||||
            s_abandon_early = strtol(optarg, NULL, 10);
 | 
			
		||||
            break;
 | 
			
		||||
| 
						 | 
				
			
			@ -1788,6 +1788,7 @@ main (int argc, char **argv)
 | 
			
		|||
            prog.prog_api.ea_alpn      = optarg;
 | 
			
		||||
            prog.prog_api.ea_stream_if = &hq_client_if;
 | 
			
		||||
            break;
 | 
			
		||||
        common_opts:
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1066,30 +1066,36 @@ const struct lsquic_stream_if http_server_if = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* XXX Assume we can always read the request in one shot.  This is not a
 | 
			
		||||
 * good assumption to make in a real product.
 | 
			
		||||
 */
 | 
			
		||||
#if HAVE_OPEN_MEMSTREAM
 | 
			
		||||
static void
 | 
			
		||||
hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    char buf[0x400];
 | 
			
		||||
    char tbuf[0x100], *buf;
 | 
			
		||||
    ssize_t nread;
 | 
			
		||||
    char *path, *end, *filename;
 | 
			
		||||
 | 
			
		||||
    nread = lsquic_stream_read(stream, buf, sizeof(buf));
 | 
			
		||||
    if (nread >= (ssize_t) sizeof(buf))
 | 
			
		||||
    if (!st_h->req_fh)
 | 
			
		||||
        st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz);
 | 
			
		||||
 | 
			
		||||
    nread = lsquic_stream_read(stream, tbuf, sizeof(tbuf));
 | 
			
		||||
    if (nread > 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("request too large, at least %zd bytes", sizeof(buf));
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        fwrite(tbuf, 1, nread, st_h->req_fh);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    else if (nread < 0)
 | 
			
		||||
 | 
			
		||||
    if (nread < 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("error reading request from stream: %s", strerror(errno));
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    buf[nread] = '\0';
 | 
			
		||||
 | 
			
		||||
    fwrite("", 1, 1, st_h->req_fh);
 | 
			
		||||
    fclose(st_h->req_fh);
 | 
			
		||||
    LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf);
 | 
			
		||||
 | 
			
		||||
    buf = st_h->req_buf;
 | 
			
		||||
    path = strchr(buf, ' ');
 | 
			
		||||
    if (!path)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1104,8 +1110,8 @@ hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    ++path;
 | 
			
		||||
    for (end = path + nread - 5; end > path
 | 
			
		||||
                                    && (*end == '\r' || *end == '\n'); --end)
 | 
			
		||||
    for (end = buf + st_h->req_sz - 1; end > path
 | 
			
		||||
                && (*end == '\0' || *end == '\r' || *end == '\n'); --end)
 | 
			
		||||
        *end = '\0';
 | 
			
		||||
    LSQ_NOTICE("parsed out request path: %s", path);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1174,6 +1180,7 @@ const struct lsquic_stream_if hq_server_if = {
 | 
			
		|||
    .on_write               = hq_server_on_write,
 | 
			
		||||
    .on_close               = http_server_on_close,
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if HAVE_REGEX
 | 
			
		||||
| 
						 | 
				
			
			@ -1922,7 +1929,11 @@ main (int argc, char **argv)
 | 
			
		|||
    prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
 | 
			
		||||
                                            &http_server_if, &server_ctx);
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:hQ:")))
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h"
 | 
			
		||||
#if HAVE_OPEN_MEMSTREAM
 | 
			
		||||
                                                    "Q:"
 | 
			
		||||
#endif
 | 
			
		||||
                                                                        )))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'n':
 | 
			
		||||
| 
						 | 
				
			
			@ -1965,12 +1976,14 @@ main (int argc, char **argv)
 | 
			
		|||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
#if HAVE_OPEN_MEMSTREAM
 | 
			
		||||
        case 'Q':
 | 
			
		||||
            /* XXX A bit hacky, as `prog' has already been initialized... */
 | 
			
		||||
            prog.prog_engine_flags &= ~LSENG_HTTP;
 | 
			
		||||
            prog.prog_api.ea_stream_if = &hq_server_if;
 | 
			
		||||
            add_alpn(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
#endif
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										60
									
								
								bin/prog.c
									
										
									
									
									
								
							
							
						
						
									
										60
									
								
								bin/prog.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -39,6 +39,7 @@
 | 
			
		|||
 | 
			
		||||
static int prog_stopped;
 | 
			
		||||
static const char *s_keylog_dir;
 | 
			
		||||
static const char *s_sess_resume_file;
 | 
			
		||||
 | 
			
		||||
static SSL_CTX * get_ssl_ctx (void *, const struct sockaddr *);
 | 
			
		||||
static void keylog_log_line (const SSL *, const char *);
 | 
			
		||||
| 
						 | 
				
			
			@ -118,6 +119,7 @@ void
 | 
			
		|||
prog_print_common_options (const struct prog *prog, FILE *out)
 | 
			
		||||
{
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
"   -0 FILE     Provide session resumption file (reading or writing)\n"
 | 
			
		||||
#if HAVE_REGEX
 | 
			
		||||
"   -s SVCPORT  Service port.  Takes on the form of host:port, host,\n"
 | 
			
		||||
"                 or port.  If host is not an IPv4 or IPv6 address, it is\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -338,6 +340,9 @@ prog_set_opt (struct prog *prog, int opt, const char *arg)
 | 
			
		|||
            sport->sp_flags |= SPORT_CONNECT;
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    case '0':
 | 
			
		||||
        s_sess_resume_file = optarg;
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'G':
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
        if (0 == stat(optarg, &st))
 | 
			
		||||
| 
						 | 
				
			
			@ -426,6 +431,53 @@ get_ssl_ctx (void *peer_ctx, const struct sockaddr *unused)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
prog_new_session_cb (SSL *ssl, SSL_SESSION *session)
 | 
			
		||||
{
 | 
			
		||||
    unsigned char *buf;
 | 
			
		||||
    size_t bufsz, nw;
 | 
			
		||||
    FILE *file;
 | 
			
		||||
 | 
			
		||||
    /* Our client is rather limited: only one file and only one ticket
 | 
			
		||||
     * can be saved.  A more flexible client implementation would call
 | 
			
		||||
     * lsquic_ssl_to_conn() and maybe save more tickets based on its
 | 
			
		||||
     * own configuration.
 | 
			
		||||
     */
 | 
			
		||||
    if (!s_sess_resume_file)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    if (0 != lsquic_ssl_sess_to_resume_info(ssl, session, &buf, &bufsz))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("lsquic_ssl_sess_to_resume_info failed");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    file = fopen(s_sess_resume_file, "wb");
 | 
			
		||||
    if (!file)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("cannot open %s for writing: %s",
 | 
			
		||||
            s_sess_resume_file, strerror(errno));
 | 
			
		||||
        free(buf);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nw = fwrite(buf, 1, bufsz, file);
 | 
			
		||||
    if (nw == bufsz)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("wrote %zd bytes of session resumption information to %s",
 | 
			
		||||
            nw, s_sess_resume_file);
 | 
			
		||||
        s_sess_resume_file = NULL;  /* Save just one ticket */
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_WARN("error: fwrite(%s) returns %zd instead of %zd: %s",
 | 
			
		||||
            s_sess_resume_file, nw, bufsz, strerror(errno));
 | 
			
		||||
 | 
			
		||||
    fclose(file);
 | 
			
		||||
    free(buf);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
prog_init_ssl_ctx (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -454,6 +506,14 @@ prog_init_ssl_ctx (struct prog *prog)
 | 
			
		|||
    if (s_keylog_dir)
 | 
			
		||||
        SSL_CTX_set_keylog_callback(prog->prog_ssl_ctx, keylog_log_line);
 | 
			
		||||
 | 
			
		||||
    if (s_sess_resume_file)
 | 
			
		||||
    {
 | 
			
		||||
        SSL_CTX_set_session_cache_mode(prog->prog_ssl_ctx,
 | 
			
		||||
                                                    SSL_SESS_CACHE_CLIENT);
 | 
			
		||||
        SSL_CTX_set_early_data_enabled(prog->prog_ssl_ctx, 1);
 | 
			
		||||
        SSL_CTX_sess_set_new_cb(prog->prog_ssl_ctx, prog_new_session_cb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -158,6 +158,8 @@ static void getExtensionPtrs()
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct packets_in *
 | 
			
		||||
allocate_packets_in (SOCKET_TYPE fd)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -874,6 +874,14 @@ settings structure:
 | 
			
		|||
 | 
			
		||||
       Default value is :macro:`LSQUIC_DF_MAX_BATCH_SIZE`
 | 
			
		||||
 | 
			
		||||
    .. member:: int             es_check_tp_sanity
 | 
			
		||||
 | 
			
		||||
       When true, sanity checks are performed on peer's transport parameter
 | 
			
		||||
       values.  If some limits are set suspiciously low, the connection won't
 | 
			
		||||
       be established.
 | 
			
		||||
 | 
			
		||||
       Default value is :macro:`LSQUIC_DF_CHECK_TP_SANITY`
 | 
			
		||||
 | 
			
		||||
To initialize the settings structure to library defaults, use the following
 | 
			
		||||
convenience function:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1115,6 +1123,10 @@ out of date.  Please check your :file:`lsquic.h` for actual values.*
 | 
			
		|||
    By default, maximum batch size is not specified, leaving it up to the
 | 
			
		||||
    library.
 | 
			
		||||
 | 
			
		||||
.. macro:: LSQUIC_DF_CHECK_TP_SANITY
 | 
			
		||||
 | 
			
		||||
    Transport parameter sanity checks are performed by default.
 | 
			
		||||
 | 
			
		||||
Receiving Packets
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1337,6 +1349,11 @@ the engine to communicate with the user code:
 | 
			
		|||
        This callback lets client record information needed to
 | 
			
		||||
        perform session resumption next time around.
 | 
			
		||||
 | 
			
		||||
        For IETF QUIC, this is called only if :member:`lsquic_engine_api.ea_get_ssl_ctx_st`
 | 
			
		||||
        is *not* set, in which case the library creates its own SSL_CTX.
 | 
			
		||||
 | 
			
		||||
        Note: this callback will be deprecated when gQUIC support is removed.
 | 
			
		||||
 | 
			
		||||
        This callback is optional.
 | 
			
		||||
 | 
			
		||||
    .. member:: ssize_t (*on_dg_write)(lsquic_conn_t *c, void *buf, size_t buf_sz)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,9 +24,9 @@ copyright = u'2021, LiteSpeed Technologies'
 | 
			
		|||
author = u'LiteSpeed Technologies'
 | 
			
		||||
 | 
			
		||||
# The short X.Y version
 | 
			
		||||
version = u'2.27'
 | 
			
		||||
version = u'2.28'
 | 
			
		||||
# The full version, including alpha/beta/rc tags
 | 
			
		||||
release = u'2.27.6'
 | 
			
		||||
release = u'2.28.0'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- General configuration ---------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,8 +24,8 @@ extern "C" {
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#define LSQUIC_MAJOR_VERSION 2
 | 
			
		||||
#define LSQUIC_MINOR_VERSION 27
 | 
			
		||||
#define LSQUIC_PATCH_VERSION 6
 | 
			
		||||
#define LSQUIC_MINOR_VERSION 28
 | 
			
		||||
#define LSQUIC_PATCH_VERSION 0
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Engine flags:
 | 
			
		||||
| 
						 | 
				
			
			@ -208,6 +208,11 @@ struct lsquic_stream_if {
 | 
			
		|||
    /**
 | 
			
		||||
     * This optional callback lets client record information needed to
 | 
			
		||||
     * perform a session resumption next time around.
 | 
			
		||||
     *
 | 
			
		||||
     * For IETF QUIC, this is called only if ea_get_ssl_ctx() is *not* set,
 | 
			
		||||
     * in which case the library creates its own SSL_CTX.
 | 
			
		||||
     *
 | 
			
		||||
     * Note: this callback will be deprecated when gQUIC support is removed.
 | 
			
		||||
     */
 | 
			
		||||
    void (*on_sess_resume_info)(lsquic_conn_t *c, const unsigned char *, size_t);
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +239,7 @@ struct lsquic_stream_if {
 | 
			
		|||
 | 
			
		||||
struct ssl_ctx_st;
 | 
			
		||||
struct ssl_st;
 | 
			
		||||
struct ssl_session_st;
 | 
			
		||||
struct lsxpack_header;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -440,6 +446,9 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
 | 
			
		|||
 */
 | 
			
		||||
#define LSQUIC_DF_MAX_BATCH_SIZE 0
 | 
			
		||||
 | 
			
		||||
/** Transport parameter sanity checks are performed by default. */
 | 
			
		||||
#define LSQUIC_DF_CHECK_TP_SANITY 1
 | 
			
		||||
 | 
			
		||||
struct lsquic_engine_settings {
 | 
			
		||||
    /**
 | 
			
		||||
     * This is a bit mask wherein each bit corresponds to a value in
 | 
			
		||||
| 
						 | 
				
			
			@ -1046,13 +1055,22 @@ struct lsquic_engine_settings {
 | 
			
		|||
    int             es_delay_onclose;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If set to a non-zero value, specifies maximum batch size.  (The
 | 
			
		||||
     * If set to a non-zero value, specified maximum batch size.  (The
 | 
			
		||||
     * batch of packets passed to @ref ea_packets_out() callback).  Must
 | 
			
		||||
     * be no larger than 1024.
 | 
			
		||||
     *
 | 
			
		||||
     * Default value is @ref LSQUIC_DF_MAX_BATCH_SIZE
 | 
			
		||||
     */
 | 
			
		||||
    unsigned        es_max_batch_size;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When true, sanity checks are performed on peer's transport parameter
 | 
			
		||||
     * values.  If some limits are set suspiciously low, the connection won't
 | 
			
		||||
     * be established.
 | 
			
		||||
     *
 | 
			
		||||
     * Default value is @ref LSQUIC_DF_CHECK_TP_SANITY
 | 
			
		||||
     */
 | 
			
		||||
    int             es_check_tp_sanity;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Initialize `settings' to default values */
 | 
			
		||||
| 
						 | 
				
			
			@ -1337,8 +1355,10 @@ struct lsquic_engine_api
 | 
			
		|||
    /**
 | 
			
		||||
     * Optional interface to control the creation of connection IDs
 | 
			
		||||
     */
 | 
			
		||||
    void                               (*ea_generate_scid)(lsquic_conn_t *,
 | 
			
		||||
                                                    lsquic_cid_t *, unsigned);
 | 
			
		||||
    void                               (*ea_generate_scid)(void *ctx,
 | 
			
		||||
                                lsquic_conn_t *, lsquic_cid_t *, unsigned);
 | 
			
		||||
    /** Passed to ea_generate_scid() */
 | 
			
		||||
    void                                *ea_gen_scid_ctx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -2022,6 +2042,18 @@ lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, size_t);
 | 
			
		|||
int
 | 
			
		||||
lsquic_cid_from_packet (const unsigned char *, size_t bufsz, lsquic_cid_t *cid);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * On success, offset to the CID is returned (a non-negative value).
 | 
			
		||||
 * `cid_len' is set to the length of the CID.  The server perspective
 | 
			
		||||
 * is assumed.  `server_cid_len' is set to the length of the CIDs that
 | 
			
		||||
 * server generates.
 | 
			
		||||
 *
 | 
			
		||||
 * On failure, a negative value is returned.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
lsquic_dcid_from_packet (const unsigned char *, size_t bufsz,
 | 
			
		||||
                                unsigned server_cid_len, unsigned *cid_len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns true if there are connections to be processed, false otherwise.
 | 
			
		||||
 * If true, `diff' is set to the difference between the earliest advisory
 | 
			
		||||
| 
						 | 
				
			
			@ -2065,6 +2097,18 @@ lsquic_ver2str[N_LSQVER];
 | 
			
		|||
lsquic_conn_t *
 | 
			
		||||
lsquic_ssl_to_conn (const struct ssl_st *);
 | 
			
		||||
 | 
			
		||||
/* Return session resumption information that can be used on subsequenct
 | 
			
		||||
 * connection as argument to lsquic_engine_connect().  Call from inside
 | 
			
		||||
 * SSL's new session callback.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.  In this case, `buf' is made to point to newly
 | 
			
		||||
 * allocated memory containing `buf_sz' bytes.  It is the caller's
 | 
			
		||||
 * responsibility to free the memory.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
lsquic_ssl_sess_to_resume_info (struct ssl_st *, struct ssl_session_st *,
 | 
			
		||||
                                        unsigned char **buf, size_t *buf_sz);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,7 +239,7 @@ lsquic_generate_cid (lsquic_cid_t *cid, size_t len)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_generate_scid (struct lsquic_conn *lconn, lsquic_cid_t *scid,
 | 
			
		||||
lsquic_generate_scid (void *ctx, struct lsquic_conn *lconn, lsquic_cid_t *scid,
 | 
			
		||||
                                                                unsigned len)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_generate_cid(scid, len);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -393,7 +393,7 @@ void
 | 
			
		|||
lsquic_generate_cid_gquic (lsquic_cid_t *cid);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_generate_scid (struct lsquic_conn *lconn, lsquic_cid_t *scid,
 | 
			
		||||
lsquic_generate_scid (void *, struct lsquic_conn *lconn, lsquic_cid_t *scid,
 | 
			
		||||
                                                                unsigned len);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -979,7 +979,7 @@ iquic_esfi_create_client (const char *hostname,
 | 
			
		|||
    SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
 | 
			
		||||
    SSL_set_connect_state(enc_sess->esi_ssl);
 | 
			
		||||
 | 
			
		||||
    if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
 | 
			
		||||
    if (SSL_CTX_sess_get_new_cb(ssl_ctx))
 | 
			
		||||
        enc_sess->esi_flags |= ESI_WANT_TICKET;
 | 
			
		||||
    enc_sess->esi_alset = alset;
 | 
			
		||||
    lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET,
 | 
			
		||||
| 
						 | 
				
			
			@ -1434,10 +1434,14 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
 | 
			
		|||
} while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Return 0 on success, in which case *buf is newly allocated memory and should
 | 
			
		||||
 * be freed by the caller.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
 | 
			
		||||
iquic_ssl_sess_to_resume_info (struct enc_sess_iquic *enc_sess, SSL *ssl,
 | 
			
		||||
                SSL_SESSION *session, unsigned char **bufp, size_t *buf_szp)
 | 
			
		||||
{
 | 
			
		||||
    struct enc_sess_iquic *enc_sess;
 | 
			
		||||
    uint32_t num;
 | 
			
		||||
    unsigned char *p, *buf;
 | 
			
		||||
    uint8_t *ticket_buf;
 | 
			
		||||
| 
						 | 
				
			
			@ -1446,33 +1450,29 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
 | 
			
		|||
    const uint8_t *trapa_buf;
 | 
			
		||||
    size_t trapa_sz, buf_sz;
 | 
			
		||||
 | 
			
		||||
    enc_sess = SSL_get_ex_data(ssl, s_idx);
 | 
			
		||||
    assert(enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info);
 | 
			
		||||
 | 
			
		||||
    SSL_get_peer_quic_transport_params(enc_sess->esi_ssl, &trapa_buf,
 | 
			
		||||
                                                                &trapa_sz);
 | 
			
		||||
    SSL_get_peer_quic_transport_params(ssl, &trapa_buf, &trapa_sz);
 | 
			
		||||
    if (!(trapa_buf + trapa_sz))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("no transport parameters: cannot generate session "
 | 
			
		||||
                                                    "resumption info");
 | 
			
		||||
        return 0;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (trapa_sz > UINT32_MAX)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("trapa size too large: %zu", trapa_sz);
 | 
			
		||||
        return 0;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!SSL_SESSION_to_bytes(session, &ticket_buf, &ticket_sz))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("could not serialize new session");
 | 
			
		||||
        return 0;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (ticket_sz > UINT32_MAX)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("ticket size too large: %zu", ticket_sz);
 | 
			
		||||
        OPENSSL_free(ticket_buf);
 | 
			
		||||
        return 0;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buf_sz = sizeof(tag) + sizeof(uint32_t) + sizeof(uint32_t)
 | 
			
		||||
| 
						 | 
				
			
			@ -1481,8 +1481,8 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
 | 
			
		|||
    if (!buf)
 | 
			
		||||
    {
 | 
			
		||||
        OPENSSL_free(ticket_buf);
 | 
			
		||||
        LSQ_WARN("%s: malloc failed", __func__);
 | 
			
		||||
        return 0;
 | 
			
		||||
        LSQ_INFO("%s: malloc failed", __func__);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p = buf;
 | 
			
		||||
| 
						 | 
				
			
			@ -1503,8 +1503,26 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
 | 
			
		|||
 | 
			
		||||
    LSQ_DEBUG("generated %zu bytes of session resumption buffer", buf_sz);
 | 
			
		||||
 | 
			
		||||
    enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info(enc_sess->esi_conn,
 | 
			
		||||
                                                                buf, buf_sz);
 | 
			
		||||
    *bufp = buf;
 | 
			
		||||
    *buf_szp = buf_sz;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
 | 
			
		||||
{
 | 
			
		||||
    struct enc_sess_iquic *enc_sess;
 | 
			
		||||
    unsigned char *buf;
 | 
			
		||||
    size_t buf_sz;
 | 
			
		||||
 | 
			
		||||
    enc_sess = SSL_get_ex_data(ssl, s_idx);
 | 
			
		||||
    assert(enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info);
 | 
			
		||||
 | 
			
		||||
    if (0 == iquic_ssl_sess_to_resume_info(enc_sess, ssl, session, &buf,
 | 
			
		||||
                                                                    &buf_sz))
 | 
			
		||||
        enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info(
 | 
			
		||||
                                            enc_sess->esi_conn, buf, buf_sz);
 | 
			
		||||
    free(buf);
 | 
			
		||||
    enc_sess->esi_flags &= ~ESI_WANT_TICKET;
 | 
			
		||||
    lsquic_alarmset_unset(enc_sess->esi_alset, AL_SESS_TICKET);
 | 
			
		||||
| 
						 | 
				
			
			@ -1805,6 +1823,36 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
 | 
			
		|||
            LSQ_DEBUG("greasing turned off: won't grease the QUIC bit");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (enc_sess->esi_enpub->enp_settings.es_check_tp_sanity
 | 
			
		||||
        /* We only care (and know) about HTTP/3.  Other protocols may have
 | 
			
		||||
         * their own limitations.  The most generic way to do this would be
 | 
			
		||||
         * to factor out transport parameter sanity check into a callback.
 | 
			
		||||
         */
 | 
			
		||||
        && enc_sess->esi_alpn && enc_sess->esi_alpn[0] >= 2
 | 
			
		||||
        && enc_sess->esi_alpn[1] == 'h'
 | 
			
		||||
        && enc_sess->esi_alpn[2] == '3')
 | 
			
		||||
    {
 | 
			
		||||
        const enum transport_param_id stream_data = enc_sess->esi_flags
 | 
			
		||||
            & ESI_SERVER ? TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL
 | 
			
		||||
                         : TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE;
 | 
			
		||||
        if (!((trans_params->tp_set & (1 << stream_data))
 | 
			
		||||
                        && trans_params->tp_numerics[stream_data] >= 0x1000))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_INFO("peer transport parameters: %s=%"PRIu64" does not pass "
 | 
			
		||||
                "sanity check", lsquic_tpi2str[stream_data],
 | 
			
		||||
                trans_params->tp_numerics[stream_data]);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        if (!((trans_params->tp_set & (1 << TPI_INIT_MAX_DATA))
 | 
			
		||||
                    && trans_params->tp_numerics[TPI_INIT_MAX_DATA] >= 0x1000))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_INFO("peer transport parameters: %s=%"PRIu64" does not pass "
 | 
			
		||||
                "sanity check", lsquic_tpi2str[TPI_INIT_MAX_DATA],
 | 
			
		||||
                trans_params->tp_numerics[TPI_INIT_MAX_DATA]);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3400,3 +3448,28 @@ lsquic_ssl_to_conn (const struct ssl_st *ssl)
 | 
			
		|||
 | 
			
		||||
    return enc_sess->esi_conn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
lsquic_ssl_sess_to_resume_info (SSL *ssl, SSL_SESSION *session,
 | 
			
		||||
                                        unsigned char **buf, size_t *buf_sz)
 | 
			
		||||
{
 | 
			
		||||
    struct enc_sess_iquic *enc_sess;
 | 
			
		||||
    int status;
 | 
			
		||||
 | 
			
		||||
    if (s_idx < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    enc_sess = SSL_get_ex_data(ssl, s_idx);
 | 
			
		||||
    if (!enc_sess)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    status = iquic_ssl_sess_to_resume_info(enc_sess, ssl, session, buf, buf_sz);
 | 
			
		||||
    if (status == 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("%s called successfully, unset WANT_TICKET flag", __func__);
 | 
			
		||||
        enc_sess->esi_flags &= ~ESI_WANT_TICKET;
 | 
			
		||||
        lsquic_alarmset_unset(enc_sess->esi_alset, AL_SESS_TICKET);
 | 
			
		||||
    }
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -393,6 +393,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
 | 
			
		|||
    settings->es_ptpc_err_thresh = LSQUIC_DF_PTPC_ERR_THRESH;
 | 
			
		||||
    settings->es_ptpc_err_divisor= LSQUIC_DF_PTPC_ERR_DIVISOR;
 | 
			
		||||
    settings->es_delay_onclose   = LSQUIC_DF_DELAY_ONCLOSE;
 | 
			
		||||
    settings->es_check_tp_sanity = LSQUIC_DF_CHECK_TP_SANITY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -616,7 +617,10 @@ lsquic_engine_new (unsigned flags,
 | 
			
		|||
    engine->pub.enp_get_ssl_ctx  = api->ea_get_ssl_ctx;
 | 
			
		||||
 | 
			
		||||
    if (api->ea_generate_scid)
 | 
			
		||||
    {
 | 
			
		||||
        engine->pub.enp_generate_scid = api->ea_generate_scid;
 | 
			
		||||
        engine->pub.enp_gen_scid_ctx  = api->ea_gen_scid_ctx;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        engine->pub.enp_generate_scid = lsquic_generate_scid;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,8 +48,9 @@ struct lsquic_engine_public {
 | 
			
		|||
    void                           *enp_stream_if_ctx;
 | 
			
		||||
    const struct lsquic_hset_if    *enp_hsi_if;
 | 
			
		||||
    void                           *enp_hsi_ctx;
 | 
			
		||||
    void                          (*enp_generate_scid)(struct lsquic_conn *,
 | 
			
		||||
                                            struct lsquic_cid *, unsigned);
 | 
			
		||||
    void                          (*enp_generate_scid)(void *,
 | 
			
		||||
                        struct lsquic_conn *, struct lsquic_cid *, unsigned);
 | 
			
		||||
    void                           *enp_gen_scid_ctx;
 | 
			
		||||
    int                           (*enp_verify_cert)(void *verify_ctx,
 | 
			
		||||
                                            struct stack_st_X509 *chain);
 | 
			
		||||
    void                           *enp_verify_ctx;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -336,6 +336,7 @@ recent_packet_hist_new (struct full_conn *conn, unsigned out,
 | 
			
		|||
    conn->fc_recent_packets[out].els[idx].time = time;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
recent_packet_hist_frames (struct full_conn *conn, unsigned out,
 | 
			
		||||
                                                enum quic_ft_bit frame_types)
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +345,8 @@ recent_packet_hist_frames (struct full_conn *conn, unsigned out,
 | 
			
		|||
    idx = (conn->fc_recent_packets[out].idx - 1) % KEEP_PACKET_HISTORY;
 | 
			
		||||
    conn->fc_recent_packets[out].els[idx].frame_types |= frame_types;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
#define recent_packet_hist_new(conn, out, time)
 | 
			
		||||
#define recent_packet_hist_frames(conn, out, frames)
 | 
			
		||||
| 
						 | 
				
			
			@ -543,6 +546,7 @@ apply_peer_settings (struct full_conn *conn)
 | 
			
		|||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const struct conn_iface *full_conn_iface_ptr;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1248,6 +1252,8 @@ verify_ack_frame (struct full_conn *conn, const unsigned char *buf, int bufsz)
 | 
			
		|||
    assert(i == ack_info->n_ranges);
 | 
			
		||||
    LSQ_DEBUG("Sent ACK frame %s", ack_buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4059,7 +4065,6 @@ headers_stream_on_priority (void *ctx, lsquic_stream_id_t stream_id,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define STRLEN(s) (sizeof(s) - 1)
 | 
			
		||||
 | 
			
		||||
static struct uncompressed_headers *
 | 
			
		||||
| 
						 | 
				
			
			@ -4456,6 +4461,7 @@ full_conn_ci_get_stats (struct lsquic_conn *lconn)
 | 
			
		|||
    return &conn->fc_stats;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "lsquic_cong_ctl.h"
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
| 
						 | 
				
			
			@ -4493,6 +4499,8 @@ full_conn_ci_log_stats (struct lsquic_conn *lconn)
 | 
			
		|||
    *conn->fc_last_stats = conn->fc_stats;
 | 
			
		||||
    memset(bs, 0, sizeof(*bs));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1184,7 +1184,7 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (enpub->enp_settings.es_scid_len)
 | 
			
		||||
        enpub->enp_generate_scid(lconn, &cce->cce_cid,
 | 
			
		||||
        enpub->enp_generate_scid(enpub->enp_gen_scid_ctx, lconn, &cce->cce_cid,
 | 
			
		||||
                                            enpub->enp_settings.es_scid_len);
 | 
			
		||||
 | 
			
		||||
    cce->cce_seqno = conn->ifc_scid_seqno++;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -500,7 +500,7 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
 | 
			
		|||
    /* Generate new SCID. Since is not the original SCID, it is given
 | 
			
		||||
     * a sequence number (0) and therefore can be retired by the client.
 | 
			
		||||
     */
 | 
			
		||||
    enpub->enp_generate_scid(&conn->imc_conn,
 | 
			
		||||
    enpub->enp_generate_scid(enpub->enp_gen_scid_ctx, &conn->imc_conn,
 | 
			
		||||
        &conn->imc_conn.cn_cces[1].cce_cid, enpub->enp_settings.es_scid_len);
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUGC("generated SCID %"CID_FMT" at index %u, switching to it",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -217,6 +217,122 @@ lsquic_cid_from_packet (const unsigned char *buf, size_t bufsz,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
lsquic_dcid_from_packet (const unsigned char *buf, size_t bufsz,
 | 
			
		||||
                                unsigned server_cid_len, unsigned *cid_len)
 | 
			
		||||
{
 | 
			
		||||
    const unsigned char *p;
 | 
			
		||||
    unsigned dcil, scil;
 | 
			
		||||
 | 
			
		||||
    if (bufsz < 9)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    switch (buf[0] >> 3)
 | 
			
		||||
    {
 | 
			
		||||
    /* Xs vary, Gs are iGnored: */
 | 
			
		||||
    /* 1X11 XGGG: */
 | 
			
		||||
    case (0x80|0x40|0x20|0x10|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x20|0x10|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x40|0x20|0x10|0x00) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x20|0x10|0x00) >> 3:
 | 
			
		||||
  Q046_long:
 | 
			
		||||
        /* lsquic_Q046_parse_packet_in_long_begin */
 | 
			
		||||
        if (bufsz < 14)
 | 
			
		||||
            return -1;
 | 
			
		||||
        p = buf + 5;
 | 
			
		||||
        dcil = p[0] >> 4;
 | 
			
		||||
        if (dcil)
 | 
			
		||||
            dcil += 3;
 | 
			
		||||
        scil = p[0] & 0xF;
 | 
			
		||||
        if (scil)
 | 
			
		||||
            scil += 3;
 | 
			
		||||
        ++p;
 | 
			
		||||
        if (dcil == GQUIC_CID_LEN && scil == 0)
 | 
			
		||||
        {
 | 
			
		||||
            *cid_len = GQUIC_CID_LEN;
 | 
			
		||||
            return (unsigned) (p - buf);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return -1;
 | 
			
		||||
    /* 1X00 XGGG: */
 | 
			
		||||
    /*
 | 
			
		||||
    case (0x80|0x40|0x00|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x00|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x40|0x00|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x00|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x80|0x40|0x00|0x10|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x00|0x10|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x40|0x00|0x10|0x00) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x00|0x10|0x00) >> 3:
 | 
			
		||||
    case (0x80|0x40|0x20|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x20|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x80|0x40|0x20|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x80|0x00|0x20|0x00|0x00) >> 3:
 | 
			
		||||
    */
 | 
			
		||||
    default:
 | 
			
		||||
        /* parse_ietf_v1_or_Q046plus_long_begin */
 | 
			
		||||
        if (buf[4] == (unsigned) '6')
 | 
			
		||||
            goto Q046_long;
 | 
			
		||||
        /* lsquic_Q050_parse_packet_in_long_begin or
 | 
			
		||||
            lsquic_ietf_v1_parse_packet_in_long_begin */
 | 
			
		||||
        if (bufsz < 14)
 | 
			
		||||
            return -1;
 | 
			
		||||
        dcil = buf[5];
 | 
			
		||||
        if (dcil <= MAX_CID_LEN && 6 + dcil < bufsz)
 | 
			
		||||
        {
 | 
			
		||||
            *cid_len = dcil;
 | 
			
		||||
            return 6;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return -1;
 | 
			
		||||
    /* 01XX XGGG */
 | 
			
		||||
    case (0x00|0x40|0x00|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x00|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x00|0x10|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x00|0x10|0x08) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x20|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x20|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x20|0x10|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x40|0x20|0x10|0x08) >> 3:
 | 
			
		||||
        /* lsquic_ietf_v1_parse_packet_in_short_begin */
 | 
			
		||||
        if (1 + server_cid_len <= bufsz)
 | 
			
		||||
        {
 | 
			
		||||
            *cid_len = server_cid_len;
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return -1;
 | 
			
		||||
    /* 00XX 0GGG */
 | 
			
		||||
    case (0x00|0x00|0x00|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x00|0x00|0x10|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x00|0x20|0x00|0x00) >> 3:
 | 
			
		||||
    case (0x00|0x00|0x20|0x10|0x00) >> 3:
 | 
			
		||||
        /* lsquic_Q046_parse_packet_in_short_begin */
 | 
			
		||||
        if (1 + server_cid_len <= bufsz && (buf[0] & 0x40))
 | 
			
		||||
        {
 | 
			
		||||
            *cid_len = server_cid_len;
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return -1;
 | 
			
		||||
    /* 00XX 1GGG */
 | 
			
		||||
    case (0x00|0x00|0x00|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x00|0x00|0x00|0x10|0x08) >> 3:
 | 
			
		||||
    case (0x00|0x00|0x20|0x00|0x08) >> 3:
 | 
			
		||||
    case (0x00|0x00|0x20|0x10|0x08) >> 3:
 | 
			
		||||
        /* lsquic_gquic_parse_packet_in_begin */
 | 
			
		||||
        if (1 + GQUIC_CID_LEN <= bufsz
 | 
			
		||||
                        && (buf[0] & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID))
 | 
			
		||||
        {
 | 
			
		||||
            *cid_len = server_cid_len;
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* See [draft-ietf-quic-transport-28], Section 12.4 (Table 3) */
 | 
			
		||||
const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3469,21 +3469,30 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Perform an implicit flush when we hit connection limit while buffering
 | 
			
		||||
 * data.  This is to prevent a (theoretical) stall:
 | 
			
		||||
/* Perform an implicit flush when we hit connection or stream flow control
 | 
			
		||||
 * limit while buffering data.
 | 
			
		||||
 *
 | 
			
		||||
 * This is to prevent a (theoretical) stall.  Scenario 1:
 | 
			
		||||
 *
 | 
			
		||||
 * Imagine a number of streams, all of which buffered some data.  The buffered
 | 
			
		||||
 * data is up to connection cap, which means no further writes are possible.
 | 
			
		||||
 * None of them flushes, which means that data is not sent and connection
 | 
			
		||||
 * WINDOW_UPDATE frame never arrives from peer.  Stall.
 | 
			
		||||
 *
 | 
			
		||||
 * Scenario 2:
 | 
			
		||||
 *
 | 
			
		||||
 * Stream flow control window is smaller than the packetizing threshold.  In
 | 
			
		||||
 * this case, without a flush, the peer will never send a WINDOW_UPDATE.  Stall.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
maybe_flush_stream (struct lsquic_stream *stream)
 | 
			
		||||
{
 | 
			
		||||
    if (stream->sm_n_buffered > 0
 | 
			
		||||
          && (stream->sm_bflags & SMBF_CONN_LIMITED)
 | 
			
		||||
            && lsquic_conn_cap_avail(&stream->conn_pub->conn_cap) == 0)
 | 
			
		||||
    if (stream->sm_n_buffered > 0 && stream->sm_write_avail(stream) == 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("out of flow control credits, flush %zu buffered bytes",
 | 
			
		||||
            stream->sm_n_buffered + active_hq_frame_sizes(stream));
 | 
			
		||||
        return stream_flush_nocheck(stream);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4033,7 +4042,12 @@ send_headers_ietf (struct lsquic_stream *stream,
 | 
			
		|||
        return -1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    stream->stream_flags &= ~STREAM_PUSHING;
 | 
			
		||||
    if (stream->stream_flags & STREAM_PUSHING)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("push promise still being written, cannot send header now");
 | 
			
		||||
        errno = EBADMSG;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    stream->stream_flags |= STREAM_NOPUSH;
 | 
			
		||||
 | 
			
		||||
    /* TODO: Optimize for the common case: write directly to sm_buf and fall
 | 
			
		||||
| 
						 | 
				
			
			@ -5326,6 +5340,7 @@ on_write_pp_wrapper (struct lsquic_stream *stream, lsquic_stream_ctx_t *h)
 | 
			
		|||
            nw, promise->pp_write_state == PPWS_DONE ? "done" : "not done");
 | 
			
		||||
        if (promise->pp_write_state == PPWS_DONE)
 | 
			
		||||
        {
 | 
			
		||||
            stream->stream_flags &= ~STREAM_PUSHING;
 | 
			
		||||
            /* Restore want_write flag */
 | 
			
		||||
            want_write = !!(stream->sm_qflags & SMQF_WANT_WRITE);
 | 
			
		||||
            if (want_write != stream->sm_saved_want_write)
 | 
			
		||||
| 
						 | 
				
			
			@ -5353,6 +5368,7 @@ lsquic_stream_push_promise (struct lsquic_stream *stream,
 | 
			
		|||
                                                struct push_promise *promise)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_reader pp_reader;
 | 
			
		||||
    struct stream_hq_frame *shf;
 | 
			
		||||
    unsigned bits, len;
 | 
			
		||||
    ssize_t nw;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5365,21 +5381,33 @@ lsquic_stream_push_promise (struct lsquic_stream *stream,
 | 
			
		|||
    vint_write(promise->pp_encoded_push_id + 8 - len, promise->pp_id,
 | 
			
		||||
                                                            bits, 1 << bits);
 | 
			
		||||
 | 
			
		||||
    if (!stream_activate_hq_frame(stream,
 | 
			
		||||
    shf = stream_activate_hq_frame(stream,
 | 
			
		||||
                stream->sm_payload + stream->sm_n_buffered, HQFT_PUSH_PROMISE,
 | 
			
		||||
                SHF_FIXED_SIZE, pp_reader_size(promise)))
 | 
			
		||||
                SHF_FIXED_SIZE, pp_reader_size(promise));
 | 
			
		||||
    if (!shf)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    stream->stream_flags |= STREAM_PUSHING;
 | 
			
		||||
 | 
			
		||||
    init_pp_reader(promise, &pp_reader);
 | 
			
		||||
#ifdef FIU_ENABLE
 | 
			
		||||
    if (fiu_fail("stream/fail_initial_pp_write"))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("%s: failed to write push promise (fiu)", __func__);
 | 
			
		||||
        nw = -1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
#endif
 | 
			
		||||
    nw = stream_write(stream, &pp_reader, SWO_BUFFER);
 | 
			
		||||
    if (nw > 0)
 | 
			
		||||
    {
 | 
			
		||||
        SLIST_INSERT_HEAD(&stream->sm_promises, promise, pp_next);
 | 
			
		||||
        ++promise->pp_refcnt;
 | 
			
		||||
        if (promise->pp_write_state == PPWS_DONE)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_DEBUG("fully wrote promise %"PRIu64, promise->pp_id);
 | 
			
		||||
            stream->stream_flags &= ~STREAM_PUSHING;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_DEBUG("partially wrote promise %"PRIu64" (state: %d, off: %u)"
 | 
			
		||||
| 
						 | 
				
			
			@ -5388,6 +5416,7 @@ lsquic_stream_push_promise (struct lsquic_stream *stream,
 | 
			
		|||
            stream->stream_flags |= STREAM_NOPUSH;
 | 
			
		||||
            stream->sm_saved_want_write =
 | 
			
		||||
                                    !!(stream->sm_qflags & SMQF_WANT_WRITE);
 | 
			
		||||
            lsquic_stream_flush(stream);
 | 
			
		||||
            stream_wantwrite(stream, 1);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -5396,6 +5425,7 @@ lsquic_stream_push_promise (struct lsquic_stream *stream,
 | 
			
		|||
    {
 | 
			
		||||
        if (nw < 0)
 | 
			
		||||
            LSQ_WARN("failure writing push promise");
 | 
			
		||||
        stream_hq_frame_put(stream, shf);
 | 
			
		||||
        stream->stream_flags |= STREAM_NOPUSH;
 | 
			
		||||
        stream->stream_flags &= ~STREAM_PUSHING;
 | 
			
		||||
        return -1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -531,7 +531,8 @@ test_pp_wantwrite_restoration (const int want_write)
 | 
			
		|||
    lsquic_stream_window_update(stream, 100);
 | 
			
		||||
    lsquic_stream_dispatch_write_events(stream);
 | 
			
		||||
    assert((stream->stream_flags & (STREAM_NOPUSH|STREAM_PUSHING))
 | 
			
		||||
                                        == (STREAM_NOPUSH|STREAM_PUSHING));
 | 
			
		||||
        /* After push promise was all written, STREAM_PUSHING is no longer set */
 | 
			
		||||
                                        == STREAM_NOPUSH);
 | 
			
		||||
    assert(SLIST_FIRST(&stream->sm_promises)->pp_write_state == PPWS_DONE); /* Done! */
 | 
			
		||||
    assert(want_write == s_onwrite_called); /* Restored: and on_write called */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue