mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	Rename test/unittests to tests/ and test/ to bin/
This commit is contained in:
		
							parent
							
								
									ecfd688117
								
							
						
					
					
						commit
						9a690580c9
					
				
					 92 changed files with 38 additions and 39 deletions
				
			
		
							
								
								
									
										26
									
								
								bin/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								bin/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
# Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE.
 | 
			
		||||
INCLUDE(CheckFunctionExists)
 | 
			
		||||
CHECK_FUNCTION_EXISTS(sendmmsg HAVE_SENDMMSG)
 | 
			
		||||
CHECK_FUNCTION_EXISTS(recvmmsg HAVE_RECVMMSG)
 | 
			
		||||
CHECK_FUNCTION_EXISTS(open_memstream HAVE_OPEN_MEMSTREAM)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
INCLUDE(CheckSymbolExists)
 | 
			
		||||
 | 
			
		||||
CHECK_SYMBOL_EXISTS(
 | 
			
		||||
    IP_MTU_DISCOVER
 | 
			
		||||
    "netinet/in.h"
 | 
			
		||||
    HAVE_IP_MTU_DISCOVER
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CHECK_SYMBOL_EXISTS(
 | 
			
		||||
    IP_DONTFRAG
 | 
			
		||||
    "netinet/in.h"
 | 
			
		||||
    HAVE_IP_DONTFRAG
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
INCLUDE(CheckIncludeFiles)
 | 
			
		||||
 | 
			
		||||
CHECK_INCLUDE_FILES(regex.h HAVE_REGEX)
 | 
			
		||||
 | 
			
		||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/test_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/test_config.h)
 | 
			
		||||
							
								
								
									
										245
									
								
								bin/echo_client.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								bin/echo_client.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,245 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * echo_client.c -- This is really a "line client:" it connects to QUIC server
 | 
			
		||||
 * and sends it stuff, line by line.  It works in tandem with echo_server.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
#include <event2/event.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx;
 | 
			
		||||
 | 
			
		||||
struct echo_client_ctx {
 | 
			
		||||
    struct lsquic_conn_ctx  *conn_h;
 | 
			
		||||
    struct prog                 *prog;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx {
 | 
			
		||||
    lsquic_conn_t       *conn;
 | 
			
		||||
    struct echo_client_ctx   *client_ctx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
echo_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    struct echo_client_ctx *client_ctx = stream_if_ctx;
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h));
 | 
			
		||||
    conn_h->conn = conn;
 | 
			
		||||
    conn_h->client_ctx = client_ctx;
 | 
			
		||||
    client_ctx->conn_h = conn_h;
 | 
			
		||||
    lsquic_conn_make_stream(conn);
 | 
			
		||||
    return conn_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_client_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
    LSQ_NOTICE("Connection closed");
 | 
			
		||||
    prog_stop(conn_h->client_ctx->prog);
 | 
			
		||||
    free(conn_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct lsquic_stream_ctx {
 | 
			
		||||
    lsquic_stream_t     *stream;
 | 
			
		||||
    struct echo_client_ctx   *client_ctx;
 | 
			
		||||
    struct event        *read_stdin_ev;
 | 
			
		||||
    char                 buf[0x100];
 | 
			
		||||
    size_t               buf_off;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
read_stdin (int fd, short what, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t nr;
 | 
			
		||||
    lsquic_stream_ctx_t *st_h = ctx;
 | 
			
		||||
 | 
			
		||||
    nr = read(fd, st_h->buf + st_h->buf_off++, 1);
 | 
			
		||||
    LSQ_DEBUG("read %zd bytes from stdin", nr);
 | 
			
		||||
    if (0 == nr)
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_shutdown(st_h->stream, 2);
 | 
			
		||||
    }
 | 
			
		||||
    else if (-1 == nr)
 | 
			
		||||
    {
 | 
			
		||||
        perror("read");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    else if ('\n' == st_h->buf[ st_h->buf_off - 1 ])
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("read newline: wantwrite");
 | 
			
		||||
        lsquic_stream_wantwrite(st_h->stream, 1);
 | 
			
		||||
        lsquic_engine_process_conns(st_h->client_ctx->prog->prog_engine);
 | 
			
		||||
    }
 | 
			
		||||
    else if (st_h->buf_off == sizeof(st_h->buf))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("line too long");
 | 
			
		||||
        exit(2);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        event_add(st_h->read_stdin_ev, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_stream_ctx_t *
 | 
			
		||||
echo_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
 | 
			
		||||
    st_h->stream = stream;
 | 
			
		||||
    st_h->client_ctx = stream_if_ctx;
 | 
			
		||||
    st_h->buf_off = 0;
 | 
			
		||||
    st_h->read_stdin_ev = event_new(prog_eb(st_h->client_ctx->prog),
 | 
			
		||||
                                    STDIN_FILENO, EV_READ, read_stdin, st_h);
 | 
			
		||||
    event_add(st_h->read_stdin_ev, NULL);
 | 
			
		||||
    return st_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    char c;
 | 
			
		||||
    size_t nr;
 | 
			
		||||
 | 
			
		||||
    nr = lsquic_stream_read(stream, &c, 1);
 | 
			
		||||
    if (0 == nr)
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_shutdown(stream, 2);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    printf("%c", c);
 | 
			
		||||
    fflush(stdout);
 | 
			
		||||
    if ('\n' == c)
 | 
			
		||||
    {
 | 
			
		||||
        event_add(st_h->read_stdin_ev, NULL);
 | 
			
		||||
        lsquic_stream_wantread(stream, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    /* Here we make an assumption that we can write the whole buffer.
 | 
			
		||||
     * Don't do it in a real program.
 | 
			
		||||
     */
 | 
			
		||||
    lsquic_stream_write(stream, st_h->buf, st_h->buf_off);
 | 
			
		||||
    st_h->buf_off = 0;
 | 
			
		||||
 | 
			
		||||
    lsquic_stream_flush(stream);
 | 
			
		||||
    lsquic_stream_wantwrite(stream, 0);
 | 
			
		||||
    lsquic_stream_wantread(stream, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    LSQ_NOTICE("%s called", __func__);
 | 
			
		||||
    if (st_h->read_stdin_ev)
 | 
			
		||||
    {
 | 
			
		||||
        event_del(st_h->read_stdin_ev);
 | 
			
		||||
        event_free(st_h->read_stdin_ev);
 | 
			
		||||
    }
 | 
			
		||||
    free(st_h);
 | 
			
		||||
    lsquic_conn_close(lsquic_stream_conn(stream));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if client_echo_stream_if = {
 | 
			
		||||
    .on_new_conn            = echo_client_on_new_conn,
 | 
			
		||||
    .on_conn_closed         = echo_client_on_conn_closed,
 | 
			
		||||
    .on_new_stream          = echo_client_on_new_stream,
 | 
			
		||||
    .on_read                = echo_client_on_read,
 | 
			
		||||
    .on_write               = echo_client_on_write,
 | 
			
		||||
    .on_close               = echo_client_on_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    LSQ_NOTICE(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
            , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
    struct echo_client_ctx client_ctx;
 | 
			
		||||
 | 
			
		||||
    memset(&client_ctx, 0, sizeof(client_ctx));
 | 
			
		||||
    client_ctx.prog = &prog;
 | 
			
		||||
 | 
			
		||||
    TAILQ_INIT(&sports);
 | 
			
		||||
    prog_init(&prog, 0, &sports, &client_echo_stream_if, &client_ctx);
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int flags = fcntl(STDIN_FILENO, F_GETFL);
 | 
			
		||||
    flags |= O_NONBLOCK;
 | 
			
		||||
    if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
 | 
			
		||||
    {
 | 
			
		||||
        perror("fcntl");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
    if (0 != prog_connect(&prog, NULL, 0))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not connect");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										228
									
								
								bin/echo_server.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								bin/echo_server.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,228 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * echo_server.c -- QUIC server that echoes back input line by line
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx;
 | 
			
		||||
 | 
			
		||||
struct echo_server_ctx {
 | 
			
		||||
    TAILQ_HEAD(, lsquic_conn_ctx)   conn_ctxs;
 | 
			
		||||
    unsigned max_reqs;
 | 
			
		||||
    int n_conn;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
    struct prog *prog;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx {
 | 
			
		||||
    TAILQ_ENTRY(lsquic_conn_ctx)    next_connh;
 | 
			
		||||
    lsquic_conn_t       *conn;
 | 
			
		||||
    struct echo_server_ctx   *server_ctx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
echo_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    struct echo_server_ctx *server_ctx = stream_if_ctx;
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
 | 
			
		||||
    conn_h->conn = conn;
 | 
			
		||||
    conn_h->server_ctx = server_ctx;
 | 
			
		||||
    TAILQ_INSERT_TAIL(&server_ctx->conn_ctxs, conn_h, next_connh);
 | 
			
		||||
    LSQ_NOTICE("New connection!");
 | 
			
		||||
    print_conn_info(conn);
 | 
			
		||||
    return conn_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_server_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
    if (conn_h->server_ctx->n_conn)
 | 
			
		||||
    {
 | 
			
		||||
        --conn_h->server_ctx->n_conn;
 | 
			
		||||
        LSQ_NOTICE("Connection closed, remaining: %d", conn_h->server_ctx->n_conn);
 | 
			
		||||
        if (0 == conn_h->server_ctx->n_conn)
 | 
			
		||||
            prog_stop(conn_h->server_ctx->prog);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_NOTICE("Connection closed");
 | 
			
		||||
    TAILQ_REMOVE(&conn_h->server_ctx->conn_ctxs, conn_h, next_connh);
 | 
			
		||||
    free(conn_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct lsquic_stream_ctx {
 | 
			
		||||
    lsquic_stream_t     *stream;
 | 
			
		||||
    struct echo_server_ctx   *server_ctx;
 | 
			
		||||
    char                 buf[0x100];
 | 
			
		||||
    size_t               buf_off;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_stream_ctx_t *
 | 
			
		||||
echo_server_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_stream_ctx_t *st_h = malloc(sizeof(*st_h));
 | 
			
		||||
    st_h->stream = stream;
 | 
			
		||||
    st_h->server_ctx = stream_if_ctx;
 | 
			
		||||
    st_h->buf_off = 0;
 | 
			
		||||
    lsquic_stream_wantread(stream, 1);
 | 
			
		||||
    return st_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct lsquic_conn_ctx *
 | 
			
		||||
find_conn_h (const struct echo_server_ctx *server_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn_ctx *conn_h;
 | 
			
		||||
    lsquic_conn_t *conn;
 | 
			
		||||
 | 
			
		||||
    conn = lsquic_stream_conn(stream);
 | 
			
		||||
    TAILQ_FOREACH(conn_h, &server_ctx->conn_ctxs, next_connh)
 | 
			
		||||
        if (conn_h->conn == conn)
 | 
			
		||||
            return conn_h;
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_server_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn_ctx *conn_h;
 | 
			
		||||
    size_t nr;
 | 
			
		||||
 | 
			
		||||
    nr = lsquic_stream_read(stream, st_h->buf + st_h->buf_off++, 1);
 | 
			
		||||
    if (0 == nr)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("EOF: closing connection");
 | 
			
		||||
        lsquic_stream_shutdown(stream, 2);
 | 
			
		||||
        conn_h = find_conn_h(st_h->server_ctx, stream);
 | 
			
		||||
        lsquic_conn_close(conn_h->conn);
 | 
			
		||||
    }
 | 
			
		||||
    else if ('\n' == st_h->buf[ st_h->buf_off - 1 ])
 | 
			
		||||
    {
 | 
			
		||||
        /* Found end of line: echo it back */
 | 
			
		||||
        lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
        lsquic_stream_wantread(stream, 0);
 | 
			
		||||
    }
 | 
			
		||||
    else if (st_h->buf_off == sizeof(st_h->buf))
 | 
			
		||||
    {
 | 
			
		||||
        /* Out of buffer space: line too long */
 | 
			
		||||
        LSQ_NOTICE("run out of buffer space");
 | 
			
		||||
        lsquic_stream_shutdown(stream, 2);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        /* Keep reading */;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_stream_write(stream, st_h->buf, st_h->buf_off);
 | 
			
		||||
    st_h->buf_off = 0;
 | 
			
		||||
    lsquic_stream_flush(stream);
 | 
			
		||||
    lsquic_stream_wantwrite(stream, 0);
 | 
			
		||||
    lsquic_stream_wantread(stream, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
echo_server_on_stream_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn_ctx *conn_h;
 | 
			
		||||
    LSQ_NOTICE("%s called", __func__);
 | 
			
		||||
    conn_h = find_conn_h(st_h->server_ctx, stream);
 | 
			
		||||
    LSQ_WARN("%s: TODO: free connection handler %p", __func__, conn_h);
 | 
			
		||||
    free(st_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if server_echo_stream_if = {
 | 
			
		||||
    .on_new_conn            = echo_server_on_new_conn,
 | 
			
		||||
    .on_conn_closed         = echo_server_on_conn_closed,
 | 
			
		||||
    .on_new_stream          = echo_server_on_new_stream,
 | 
			
		||||
    .on_read                = echo_server_on_read,
 | 
			
		||||
    .on_write               = echo_server_on_write,
 | 
			
		||||
    .on_close               = echo_server_on_stream_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    printf(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
                , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
    struct echo_server_ctx server_ctx;
 | 
			
		||||
 | 
			
		||||
    memset(&server_ctx, 0, sizeof(server_ctx));
 | 
			
		||||
    server_ctx.prog = &prog;
 | 
			
		||||
    TAILQ_INIT(&server_ctx.sports);
 | 
			
		||||
    TAILQ_INIT(&server_ctx.conn_ctxs);
 | 
			
		||||
 | 
			
		||||
    prog_init(&prog, LSENG_SERVER, &server_ctx.sports,
 | 
			
		||||
                                        &server_echo_stream_if, &server_ctx);
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "hn:")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'n':
 | 
			
		||||
            server_ctx.n_conn = atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1641
									
								
								bin/http_client.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1641
									
								
								bin/http_client.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1700
									
								
								bin/http_server.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1700
									
								
								bin/http_server.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										518
									
								
								bin/md5_client.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										518
									
								
								bin/md5_client.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,518 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * md5_client.c -- This client sends one or more files to MD5 QUIC server
 | 
			
		||||
 *                 for MD5 sum calculation.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
#include <event2/event.h>
 | 
			
		||||
#include <openssl/md5.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_int_types.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_varint.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_hq.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_sfcw.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_hash.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_stream.h"
 | 
			
		||||
 | 
			
		||||
/* Set to non-zero value to test out what happens when reset is sent */
 | 
			
		||||
#define RESET_AFTER_N_WRITES 0
 | 
			
		||||
 | 
			
		||||
static int g_write_file = 1;
 | 
			
		||||
 | 
			
		||||
#define LOCAL_BUF_SIZE 0x100
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    unsigned    stream_id;  /* If set, reset this stream ID */
 | 
			
		||||
    off_t       offset;     /* Reset it after writing this many bytes */
 | 
			
		||||
} g_reset_stream;
 | 
			
		||||
 | 
			
		||||
struct file {
 | 
			
		||||
    LIST_ENTRY(file)        next_file;
 | 
			
		||||
    const char             *filename;
 | 
			
		||||
    struct lsquic_reader    reader;
 | 
			
		||||
    int                     fd;
 | 
			
		||||
    unsigned                priority;
 | 
			
		||||
    enum {
 | 
			
		||||
        FILE_RESET  = (1 << 0),
 | 
			
		||||
    }                       file_flags;
 | 
			
		||||
    size_t                  md5_off;
 | 
			
		||||
    char                    md5str[MD5_DIGEST_LENGTH * 2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx;
 | 
			
		||||
 | 
			
		||||
struct client_ctx {
 | 
			
		||||
    struct lsquic_conn_ctx  *conn_h;
 | 
			
		||||
    LIST_HEAD(, file)            files;
 | 
			
		||||
    unsigned                     n_files;
 | 
			
		||||
    struct file                 *cur_file;
 | 
			
		||||
    lsquic_engine_t             *engine;
 | 
			
		||||
    struct service_port         *sport;
 | 
			
		||||
    struct prog                 *prog;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx {
 | 
			
		||||
    lsquic_conn_t       *conn;
 | 
			
		||||
    struct client_ctx   *client_ctx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    struct client_ctx *client_ctx = stream_if_ctx;
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h));
 | 
			
		||||
    conn_h->conn = conn;
 | 
			
		||||
    conn_h->client_ctx = client_ctx;
 | 
			
		||||
    client_ctx->conn_h = conn_h;
 | 
			
		||||
    assert(client_ctx->n_files > 0);
 | 
			
		||||
    unsigned n = client_ctx->n_files;
 | 
			
		||||
    while (n--)
 | 
			
		||||
        lsquic_conn_make_stream(conn);
 | 
			
		||||
    print_conn_info(conn);
 | 
			
		||||
    return conn_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_on_goaway_received (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    LSQ_NOTICE("GOAWAY received");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
    LSQ_NOTICE("Connection closed");
 | 
			
		||||
    prog_stop(conn_h->client_ctx->prog);
 | 
			
		||||
    free(conn_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct lsquic_stream_ctx {
 | 
			
		||||
    lsquic_stream_t     *stream;
 | 
			
		||||
    struct client_ctx   *client_ctx;
 | 
			
		||||
    struct file         *file;
 | 
			
		||||
    struct event        *read_stdin_ev;
 | 
			
		||||
    struct {
 | 
			
		||||
        int         initialized;
 | 
			
		||||
        size_t      size,
 | 
			
		||||
                    off;
 | 
			
		||||
    }                    small;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_stream_ctx_t *
 | 
			
		||||
client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    struct client_ctx *const client_ctx = stream_if_ctx;
 | 
			
		||||
    if (!stream)
 | 
			
		||||
    {
 | 
			
		||||
        assert(client_ctx->n_files > 0);
 | 
			
		||||
        LSQ_NOTICE("%s: got null stream: no more streams possible; # files: %u",
 | 
			
		||||
                                                 __func__, client_ctx->n_files);
 | 
			
		||||
        --client_ctx->n_files;
 | 
			
		||||
        if (0 == client_ctx->n_files)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_DEBUG("closing connection");
 | 
			
		||||
            lsquic_conn_close(client_ctx->conn_h->conn);
 | 
			
		||||
        }
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
 | 
			
		||||
    st_h->stream = stream;
 | 
			
		||||
    st_h->client_ctx = stream_if_ctx;
 | 
			
		||||
    if (LIST_EMPTY(&st_h->client_ctx->files))
 | 
			
		||||
    {
 | 
			
		||||
        /* XXX: perhaps we should not be able to write immediately: there may
 | 
			
		||||
         * be internal memory constraints...
 | 
			
		||||
         */
 | 
			
		||||
        lsquic_stream_write(stream, "client request", 14);
 | 
			
		||||
        (void) lsquic_stream_flush(stream);
 | 
			
		||||
        lsquic_stream_wantwrite(stream, 0);
 | 
			
		||||
        lsquic_stream_wantread(stream, 1);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        st_h->file = LIST_FIRST(&st_h->client_ctx->files);
 | 
			
		||||
        if (g_write_file)
 | 
			
		||||
        {
 | 
			
		||||
            st_h->file->fd = -1;
 | 
			
		||||
            st_h->file->reader.lsqr_read = test_reader_read;
 | 
			
		||||
            st_h->file->reader.lsqr_size = test_reader_size;
 | 
			
		||||
            st_h->file->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->file->filename);
 | 
			
		||||
            if (!st_h->file->reader.lsqr_ctx)
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            st_h->file->fd = open(st_h->file->filename, O_RDONLY);
 | 
			
		||||
            if (st_h->file->fd < 0)
 | 
			
		||||
            {
 | 
			
		||||
                LSQ_ERROR("could not open %s for reading: %s",
 | 
			
		||||
                          st_h->file->filename, strerror(errno));
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        LIST_REMOVE(st_h->file, next_file);
 | 
			
		||||
        lsquic_stream_set_priority(stream, st_h->file->priority);
 | 
			
		||||
        lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
    }
 | 
			
		||||
    return st_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static size_t
 | 
			
		||||
buf_reader_size (void *reader_ctx)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_stream_ctx_t *const st_h = reader_ctx;
 | 
			
		||||
    struct stat st;
 | 
			
		||||
    off_t off;
 | 
			
		||||
 | 
			
		||||
    if (st_h->small.initialized)
 | 
			
		||||
        goto initialized;
 | 
			
		||||
 | 
			
		||||
    if (0 != fstat(st_h->file->fd, &st))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("fstat failed: %s", strerror(errno));
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    off = lseek(st_h->file->fd, 0, SEEK_CUR);
 | 
			
		||||
    if (off == (off_t) -1)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("lseek failed: %s", strerror(errno));
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (st.st_size < off)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("size mismatch");
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    st_h->small.initialized = 1;
 | 
			
		||||
    st_h->small.off = off;
 | 
			
		||||
    st_h->small.size = st.st_size;
 | 
			
		||||
 | 
			
		||||
  initialized:
 | 
			
		||||
    if (st_h->small.size - st_h->small.off > LOCAL_BUF_SIZE)
 | 
			
		||||
        return LOCAL_BUF_SIZE;
 | 
			
		||||
    else
 | 
			
		||||
        return st_h->small.size - st_h->small.off;
 | 
			
		||||
 | 
			
		||||
  err:
 | 
			
		||||
    close(st_h->file->fd);
 | 
			
		||||
    st_h->file->fd = 0;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static size_t
 | 
			
		||||
buf_reader_read (void *reader_ctx, void *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_stream_ctx_t *const st_h = reader_ctx;
 | 
			
		||||
    ssize_t nr;
 | 
			
		||||
    unsigned char local_buf[LOCAL_BUF_SIZE];
 | 
			
		||||
 | 
			
		||||
    assert(st_h->small.initialized);
 | 
			
		||||
 | 
			
		||||
    if (count > sizeof(local_buf))
 | 
			
		||||
        count = sizeof(local_buf);
 | 
			
		||||
 | 
			
		||||
    nr = read(st_h->file->fd, local_buf, count);
 | 
			
		||||
    if (nr < 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("read: %s", strerror(errno));
 | 
			
		||||
        close(st_h->file->fd);
 | 
			
		||||
        st_h->file->fd = 0;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(buf, local_buf, nr);
 | 
			
		||||
    st_h->small.off += nr;
 | 
			
		||||
    return nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_file_on_write_buf (lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t nw;
 | 
			
		||||
    struct lsquic_reader reader = {
 | 
			
		||||
        .lsqr_read = buf_reader_read,
 | 
			
		||||
        .lsqr_size = buf_reader_size,
 | 
			
		||||
        .lsqr_ctx  = st_h,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (g_reset_stream.stream_id == lsquic_stream_id(st_h->stream) &&
 | 
			
		||||
        lseek(st_h->file->fd, 0, SEEK_CUR) >= g_reset_stream.offset)
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_reset(st_h->stream, 0x01    /* QUIC_INTERNAL_ERROR */);
 | 
			
		||||
        g_reset_stream.stream_id = 0;   /* Reset only once */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nw = lsquic_stream_writef(st_h->stream, &reader);
 | 
			
		||||
    if (-1 == nw)
 | 
			
		||||
    {
 | 
			
		||||
        if (ECONNRESET == errno)
 | 
			
		||||
            st_h->file->file_flags |= FILE_RESET;
 | 
			
		||||
        LSQ_WARN("lsquic_stream_read: %s", strerror(errno));
 | 
			
		||||
        lsquic_stream_close(st_h->stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if RESET_AFTER_N_WRITES
 | 
			
		||||
    static int write_count = 0;
 | 
			
		||||
    if (write_count++ > RESET_AFTER_N_WRITES)
 | 
			
		||||
        lsquic_stream_reset(st_h->stream, 0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (0 == nw)
 | 
			
		||||
    {
 | 
			
		||||
        (void) close(st_h->file->fd);
 | 
			
		||||
        if (0 == lsquic_stream_shutdown(st_h->stream, 1))
 | 
			
		||||
            lsquic_stream_wantread(st_h->stream, 1);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (ECONNRESET == errno)
 | 
			
		||||
                st_h->file->file_flags |= FILE_RESET;
 | 
			
		||||
            LSQ_WARN("lsquic_stream_shutdown: %s", strerror(errno));
 | 
			
		||||
            lsquic_stream_close(st_h->stream);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_file_on_write_efficient (lsquic_stream_t *stream,
 | 
			
		||||
                                                lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t nw;
 | 
			
		||||
 | 
			
		||||
    nw = lsquic_stream_writef(stream, &st_h->file->reader);
 | 
			
		||||
    if (nw < 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("write error: %s", strerror(errno));
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (nw == 0)
 | 
			
		||||
    {
 | 
			
		||||
        destroy_lsquic_reader_ctx(st_h->file->reader.lsqr_ctx);
 | 
			
		||||
        st_h->file->reader.lsqr_ctx = NULL;
 | 
			
		||||
        if (0 == lsquic_stream_shutdown(st_h->stream, 1))
 | 
			
		||||
            lsquic_stream_wantread(st_h->stream, 1);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (ECONNRESET == errno)
 | 
			
		||||
                st_h->file->file_flags |= FILE_RESET;
 | 
			
		||||
            LSQ_WARN("lsquic_stream_shutdown: %s", strerror(errno));
 | 
			
		||||
            lsquic_stream_close(st_h->stream);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_file_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    if (g_write_file)
 | 
			
		||||
        client_file_on_write_efficient(stream, st_h);
 | 
			
		||||
    else
 | 
			
		||||
        client_file_on_write_buf(st_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_file_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    char buf;
 | 
			
		||||
    /* We expect to read in 32-character MD5 string */
 | 
			
		||||
    size_t ntoread = sizeof(st_h->file->md5str) - st_h->file->md5_off;
 | 
			
		||||
    if (0 == ntoread)
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_wantread(stream, 0);
 | 
			
		||||
        /* XXX What about an error (due to RST_STREAM) here: how are we to
 | 
			
		||||
         *     handle it?
 | 
			
		||||
         */
 | 
			
		||||
        /* Expect a FIN */
 | 
			
		||||
        if (0 == lsquic_stream_read(stream, &buf, sizeof(buf)))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_NOTICE("%.*s  %s", (int) sizeof(st_h->file->md5str),
 | 
			
		||||
                                                    st_h->file->md5str,
 | 
			
		||||
                                                    st_h->file->filename);
 | 
			
		||||
            fflush(stdout);
 | 
			
		||||
            LSQ_DEBUG("# of files: %d", st_h->client_ctx->n_files);
 | 
			
		||||
            lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            LSQ_ERROR("expected FIN from stream!");
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        ssize_t nr = lsquic_stream_read(stream,
 | 
			
		||||
            st_h->file->md5str + st_h->file->md5_off, ntoread);
 | 
			
		||||
        if (-1 == nr)
 | 
			
		||||
        {
 | 
			
		||||
            if (ECONNRESET == errno)
 | 
			
		||||
                st_h->file->file_flags |= FILE_RESET;
 | 
			
		||||
            LSQ_WARN("lsquic_stream_read: %s", strerror(errno));
 | 
			
		||||
            lsquic_stream_close(stream);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            st_h->file->md5_off += nr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_file_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    --st_h->client_ctx->n_files;
 | 
			
		||||
    LSQ_NOTICE("%s called for stream %"PRIu64", # files: %u", __func__,
 | 
			
		||||
                        lsquic_stream_id(stream), st_h->client_ctx->n_files);
 | 
			
		||||
    if (0 == st_h->client_ctx->n_files)
 | 
			
		||||
        lsquic_conn_close(st_h->client_ctx->conn_h->conn);
 | 
			
		||||
    if (!(st_h->file->file_flags & FILE_RESET) && 0 == RESET_AFTER_N_WRITES)
 | 
			
		||||
        assert(st_h->file->md5_off == sizeof(st_h->file->md5str));
 | 
			
		||||
    if (st_h->file->reader.lsqr_ctx)
 | 
			
		||||
    {
 | 
			
		||||
        destroy_lsquic_reader_ctx(st_h->file->reader.lsqr_ctx);
 | 
			
		||||
        st_h->file->reader.lsqr_ctx = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (st_h->file->fd >= 0)
 | 
			
		||||
        (void) close(st_h->file->fd);
 | 
			
		||||
    free(st_h->file);
 | 
			
		||||
    free(st_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if client_file_stream_if = {
 | 
			
		||||
    .on_new_conn            = client_on_new_conn,
 | 
			
		||||
    .on_goaway_received     = client_on_goaway_received,
 | 
			
		||||
    .on_conn_closed         = client_on_conn_closed,
 | 
			
		||||
    .on_new_stream          = client_on_new_stream,
 | 
			
		||||
    .on_read                = client_file_on_read,
 | 
			
		||||
    .on_write               = client_file_on_write,
 | 
			
		||||
    .on_close               = client_file_on_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    printf(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
"   -f FILE     File to send to the server -- must be specified at least\n"
 | 
			
		||||
"                 once.\n"
 | 
			
		||||
"   -b          Use buffering API for sending files over rather than\n"
 | 
			
		||||
"                 the efficient version.\n"
 | 
			
		||||
"   -p PRIORITY Applicatble to previous file specified with -f\n"
 | 
			
		||||
"   -r STREAM_ID:OFFSET\n"
 | 
			
		||||
"               Reset stream STREAM_ID after sending more that OFFSET bytes.\n"
 | 
			
		||||
            , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
    struct client_ctx client_ctx;
 | 
			
		||||
    struct file *file;
 | 
			
		||||
 | 
			
		||||
    file = NULL;
 | 
			
		||||
    memset(&client_ctx, 0, sizeof(client_ctx));
 | 
			
		||||
    client_ctx.prog = &prog;
 | 
			
		||||
 | 
			
		||||
    TAILQ_INIT(&sports);
 | 
			
		||||
    prog_init(&prog, 0, &sports, &client_file_stream_if, &client_ctx);
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "bhr:f:p:")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'p':
 | 
			
		||||
            if (file)
 | 
			
		||||
                file->priority = atoi(optarg);
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                fprintf(stderr, "No file to apply priority to\n");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'b':
 | 
			
		||||
            g_write_file = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'f':
 | 
			
		||||
            file = calloc(1, sizeof(*file));
 | 
			
		||||
            LIST_INSERT_HEAD(&client_ctx.files, file, next_file);
 | 
			
		||||
            ++client_ctx.n_files;
 | 
			
		||||
            file->filename = optarg;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'r':
 | 
			
		||||
            g_reset_stream.stream_id = atoi(optarg);
 | 
			
		||||
            g_reset_stream.offset = atoi(strchr(optarg, ':') + 1);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (LIST_EMPTY(&client_ctx.files))
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "please specify one of more files using -f\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
    client_ctx.sport = TAILQ_FIRST(&sports);
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_connect(&prog, NULL, 0))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not connect");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										340
									
								
								bin/md5_server.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								bin/md5_server.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,340 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * md5_server.c -- Read one or more streams from the client and return
 | 
			
		||||
 *                 MD5 sum of the payload.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <openssl/md5.h>
 | 
			
		||||
 | 
			
		||||
#include <event2/event.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int g_really_calculate_md5 = 1;
 | 
			
		||||
 | 
			
		||||
/* Turn on to test whether stream reset is being sent when stream is closed
 | 
			
		||||
 * prematurely.
 | 
			
		||||
 */
 | 
			
		||||
static struct {
 | 
			
		||||
    unsigned        stream_id;
 | 
			
		||||
    unsigned long   limit;
 | 
			
		||||
    unsigned long   n_read;
 | 
			
		||||
} g_premature_close;
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx;
 | 
			
		||||
 | 
			
		||||
struct server_ctx {
 | 
			
		||||
    TAILQ_HEAD(, lsquic_conn_ctx)   conn_ctxs;
 | 
			
		||||
    unsigned max_reqs;
 | 
			
		||||
    int n_conn;
 | 
			
		||||
    time_t expiry;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
    struct prog *prog;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx {
 | 
			
		||||
    TAILQ_ENTRY(lsquic_conn_ctx)    next_connh;
 | 
			
		||||
    lsquic_conn_t       *conn;
 | 
			
		||||
    unsigned             n_reqs, n_closed;
 | 
			
		||||
    struct server_ctx   *server_ctx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    struct server_ctx *server_ctx = stream_if_ctx;
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
 | 
			
		||||
    conn_h->conn = conn;
 | 
			
		||||
    conn_h->server_ctx = server_ctx;
 | 
			
		||||
    TAILQ_INSERT_TAIL(&server_ctx->conn_ctxs, conn_h, next_connh);
 | 
			
		||||
    LSQ_NOTICE("New connection!");
 | 
			
		||||
    print_conn_info(conn);
 | 
			
		||||
    return conn_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
    int stopped;
 | 
			
		||||
 | 
			
		||||
    if (conn_h->server_ctx->expiry && conn_h->server_ctx->expiry < time(NULL))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("reached engine expiration time, shut down");
 | 
			
		||||
        prog_stop(conn_h->server_ctx->prog);
 | 
			
		||||
        stopped = 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        stopped = 0;
 | 
			
		||||
 | 
			
		||||
    if (conn_h->server_ctx->n_conn)
 | 
			
		||||
    {
 | 
			
		||||
        --conn_h->server_ctx->n_conn;
 | 
			
		||||
        LSQ_NOTICE("Connection closed, remaining: %d", conn_h->server_ctx->n_conn);
 | 
			
		||||
        if (0 == conn_h->server_ctx->n_conn && !stopped)
 | 
			
		||||
            prog_stop(conn_h->server_ctx->prog);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_NOTICE("Connection closed");
 | 
			
		||||
    TAILQ_REMOVE(&conn_h->server_ctx->conn_ctxs, conn_h, next_connh);
 | 
			
		||||
    free(conn_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct lsquic_stream_ctx {
 | 
			
		||||
    lsquic_stream_t     *stream;
 | 
			
		||||
    struct server_ctx   *server_ctx;
 | 
			
		||||
    MD5_CTX              md5ctx;
 | 
			
		||||
    unsigned char        md5sum[MD5_DIGEST_LENGTH];
 | 
			
		||||
    char                 md5str[MD5_DIGEST_LENGTH * 2 + 1];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct lsquic_conn_ctx *
 | 
			
		||||
find_conn_h (const struct server_ctx *server_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn_ctx *conn_h;
 | 
			
		||||
    lsquic_conn_t *conn;
 | 
			
		||||
 | 
			
		||||
    conn = lsquic_stream_conn(stream);
 | 
			
		||||
    TAILQ_FOREACH(conn_h, &server_ctx->conn_ctxs, next_connh)
 | 
			
		||||
        if (conn_h->conn == conn)
 | 
			
		||||
            return conn_h;
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_stream_ctx_t *
 | 
			
		||||
server_md5_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn_ctx *conn_h;
 | 
			
		||||
    lsquic_stream_ctx_t *st_h = malloc(sizeof(*st_h));
 | 
			
		||||
    st_h->stream = stream;
 | 
			
		||||
    st_h->server_ctx = stream_if_ctx;
 | 
			
		||||
    lsquic_stream_wantread(stream, 1);
 | 
			
		||||
    if (g_really_calculate_md5)
 | 
			
		||||
        MD5_Init(&st_h->md5ctx);
 | 
			
		||||
    conn_h = find_conn_h(st_h->server_ctx, stream);
 | 
			
		||||
    assert(conn_h);
 | 
			
		||||
    conn_h->n_reqs++;
 | 
			
		||||
    LSQ_NOTICE("request #%u", conn_h->n_reqs);
 | 
			
		||||
    if (st_h->server_ctx->max_reqs &&
 | 
			
		||||
        conn_h->n_reqs >= st_h->server_ctx->max_reqs)
 | 
			
		||||
    {
 | 
			
		||||
        /* The assert guards the assumption that after the we mark the
 | 
			
		||||
         * connection as going away, no new streams are opened and thus
 | 
			
		||||
         * this callback is not called.
 | 
			
		||||
         */
 | 
			
		||||
        assert(conn_h->n_reqs == st_h->server_ctx->max_reqs);
 | 
			
		||||
        LSQ_NOTICE("reached maximum requests: %u, going away",
 | 
			
		||||
            st_h->server_ctx->max_reqs);
 | 
			
		||||
        lsquic_conn_going_away(conn_h->conn);
 | 
			
		||||
    }
 | 
			
		||||
    return st_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_md5_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    char buf[0x1000];
 | 
			
		||||
    ssize_t nr;
 | 
			
		||||
 | 
			
		||||
    nr = lsquic_stream_read(stream, buf, sizeof(buf));
 | 
			
		||||
    if (-1 == nr)
 | 
			
		||||
    {
 | 
			
		||||
        /* This should never return an error if we only call read() once
 | 
			
		||||
         * per callback.
 | 
			
		||||
         */
 | 
			
		||||
        perror("lsquic_stream_read");
 | 
			
		||||
        lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_premature_close.limit &&
 | 
			
		||||
                g_premature_close.stream_id == lsquic_stream_id(stream))
 | 
			
		||||
    {
 | 
			
		||||
        g_premature_close.n_read += nr;
 | 
			
		||||
        if (g_premature_close.n_read > g_premature_close.limit)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_WARN("Done after reading %lu bytes", g_premature_close.n_read);
 | 
			
		||||
            lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (nr)
 | 
			
		||||
    {
 | 
			
		||||
        if (g_really_calculate_md5)
 | 
			
		||||
            MD5_Update(&st_h->md5ctx, buf, nr);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_wantread(stream, 0);
 | 
			
		||||
        if (g_really_calculate_md5)
 | 
			
		||||
        {
 | 
			
		||||
            MD5_Final(st_h->md5sum, &st_h->md5ctx);
 | 
			
		||||
            snprintf(st_h->md5str, sizeof(st_h->md5str),
 | 
			
		||||
                "%02x%02x%02x%02x%02x%02x%02x%02x"
 | 
			
		||||
                "%02x%02x%02x%02x%02x%02x%02x%02x"
 | 
			
		||||
                , st_h->md5sum[0]
 | 
			
		||||
                , st_h->md5sum[1]
 | 
			
		||||
                , st_h->md5sum[2]
 | 
			
		||||
                , st_h->md5sum[3]
 | 
			
		||||
                , st_h->md5sum[4]
 | 
			
		||||
                , st_h->md5sum[5]
 | 
			
		||||
                , st_h->md5sum[6]
 | 
			
		||||
                , st_h->md5sum[7]
 | 
			
		||||
                , st_h->md5sum[8]
 | 
			
		||||
                , st_h->md5sum[9]
 | 
			
		||||
                , st_h->md5sum[10]
 | 
			
		||||
                , st_h->md5sum[11]
 | 
			
		||||
                , st_h->md5sum[12]
 | 
			
		||||
                , st_h->md5sum[13]
 | 
			
		||||
                , st_h->md5sum[14]
 | 
			
		||||
                , st_h->md5sum[15]
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            memset(st_h->md5str, '0', sizeof(st_h->md5str) - 1);
 | 
			
		||||
            st_h->md5str[sizeof(st_h->md5str) - 1] = '\0';
 | 
			
		||||
        }
 | 
			
		||||
        lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
        lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_md5_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t nw;
 | 
			
		||||
    nw = lsquic_stream_write(stream, st_h->md5str, sizeof(st_h->md5str) - 1);
 | 
			
		||||
    if (-1 == nw)
 | 
			
		||||
    {
 | 
			
		||||
        perror("lsquic_stream_write");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    lsquic_stream_wantwrite(stream, 0);
 | 
			
		||||
    lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn_ctx *conn_h;
 | 
			
		||||
    LSQ_NOTICE("%s called", __func__);
 | 
			
		||||
    conn_h = find_conn_h(st_h->server_ctx, stream);
 | 
			
		||||
    conn_h->n_closed++;
 | 
			
		||||
    if (st_h->server_ctx->max_reqs &&
 | 
			
		||||
        conn_h->n_closed >= st_h->server_ctx->max_reqs)
 | 
			
		||||
    {
 | 
			
		||||
        assert(conn_h->n_closed == st_h->server_ctx->max_reqs);
 | 
			
		||||
        LSQ_NOTICE("closing connection after completing %u requests",
 | 
			
		||||
            conn_h->n_closed);
 | 
			
		||||
        lsquic_conn_close(conn_h->conn);
 | 
			
		||||
    }
 | 
			
		||||
    free(st_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if server_md5_stream_if = {
 | 
			
		||||
    .on_new_conn            = server_on_new_conn,
 | 
			
		||||
    .on_conn_closed         = server_on_conn_closed,
 | 
			
		||||
    .on_new_stream          = server_md5_on_new_stream,
 | 
			
		||||
    .on_read                = server_md5_on_read,
 | 
			
		||||
    .on_write               = server_md5_on_write,
 | 
			
		||||
    .on_close               = server_on_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    printf(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
"   -e EXPIRY   Stop engine after this many seconds.  The expiration is\n"
 | 
			
		||||
"                 checked when connections are closed.\n"
 | 
			
		||||
                , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
    struct server_ctx server_ctx;
 | 
			
		||||
 | 
			
		||||
    memset(&server_ctx, 0, sizeof(server_ctx));
 | 
			
		||||
    TAILQ_INIT(&server_ctx.conn_ctxs);
 | 
			
		||||
    server_ctx.prog = &prog;
 | 
			
		||||
    TAILQ_INIT(&server_ctx.sports);
 | 
			
		||||
    prog_init(&prog, LSENG_SERVER, &server_ctx.sports,
 | 
			
		||||
                                    &server_md5_stream_if, &server_ctx);
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "hr:Fn:e:p:")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'F':
 | 
			
		||||
            g_really_calculate_md5 = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'p':
 | 
			
		||||
            g_premature_close.stream_id = atoi(optarg);
 | 
			
		||||
            g_premature_close.limit = atoi(strchr(optarg, ':') + 1);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'r':
 | 
			
		||||
            server_ctx.max_reqs = atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'e':
 | 
			
		||||
            server_ctx.expiry = time(NULL) + atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'n':
 | 
			
		||||
            server_ctx.n_conn = atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										724
									
								
								bin/prog.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										724
									
								
								bin/prog.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,724 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#pragma warning(disable:4028)
 | 
			
		||||
#endif// WIN32
 | 
			
		||||
 | 
			
		||||
#include <event2/event.h>
 | 
			
		||||
 | 
			
		||||
#include <lsquic.h>
 | 
			
		||||
 | 
			
		||||
#include <openssl/ssl.h>
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_hash.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_int_types.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_util.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
#include "test_config.h"
 | 
			
		||||
#include "test_cert.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
static int prog_stopped;
 | 
			
		||||
 | 
			
		||||
static SSL_CTX * get_ssl_ctx (void *);
 | 
			
		||||
 | 
			
		||||
static const struct lsquic_packout_mem_if pmi = {
 | 
			
		||||
    .pmi_allocate = pba_allocate,
 | 
			
		||||
    .pmi_release  = pba_release,
 | 
			
		||||
    .pmi_return   = pba_release,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_init (struct prog *prog, unsigned flags,
 | 
			
		||||
           struct sport_head *sports,
 | 
			
		||||
           const struct lsquic_stream_if *stream_if, void *stream_if_ctx)
 | 
			
		||||
{
 | 
			
		||||
    /* prog-specific initialization: */
 | 
			
		||||
    memset(prog, 0, sizeof(*prog));
 | 
			
		||||
    prog->prog_engine_flags = flags;
 | 
			
		||||
    prog->prog_sports       = sports;
 | 
			
		||||
    lsquic_engine_init_settings(&prog->prog_settings, flags);
 | 
			
		||||
#if ECN_SUPPORTED
 | 
			
		||||
    prog->prog_settings.es_ecn      = LSQUIC_DF_ECN;
 | 
			
		||||
#else
 | 
			
		||||
    prog->prog_settings.es_ecn      = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    prog->prog_api.ea_settings      = &prog->prog_settings;
 | 
			
		||||
    prog->prog_api.ea_stream_if     = stream_if;
 | 
			
		||||
    prog->prog_api.ea_stream_if_ctx = stream_if_ctx;
 | 
			
		||||
    prog->prog_api.ea_packets_out   = sport_packets_out;
 | 
			
		||||
    prog->prog_api.ea_packets_out_ctx
 | 
			
		||||
                                    = prog;
 | 
			
		||||
    prog->prog_api.ea_pmi           = &pmi;
 | 
			
		||||
    prog->prog_api.ea_pmi_ctx       = &prog->prog_pba;
 | 
			
		||||
    prog->prog_api.ea_get_ssl_ctx   = get_ssl_ctx;
 | 
			
		||||
#if LSQUIC_PREFERRED_ADDR
 | 
			
		||||
    if (getenv("LSQUIC_PREFERRED_ADDR4") || getenv("LSQUIC_PREFERRED_ADDR6"))
 | 
			
		||||
        prog->prog_flags |= PROG_SEARCH_ADDRS;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Non prog-specific initialization: */
 | 
			
		||||
    lsquic_global_init(flags & LSENG_SERVER ? LSQUIC_GLOBAL_SERVER :
 | 
			
		||||
                                                    LSQUIC_GLOBAL_CLIENT);
 | 
			
		||||
    lsquic_log_to_fstream(stderr, LLTS_HHMMSSMS);
 | 
			
		||||
    lsquic_logger_lopt("=notice");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
prog_add_sport (struct prog *prog, const char *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct service_port *sport;
 | 
			
		||||
    sport = sport_new(arg, prog);
 | 
			
		||||
    if (!sport)
 | 
			
		||||
        return -1;
 | 
			
		||||
    /* Default settings: */
 | 
			
		||||
    sport->sp_flags = prog->prog_dummy_sport.sp_flags;
 | 
			
		||||
    sport->sp_sndbuf = prog->prog_dummy_sport.sp_sndbuf;
 | 
			
		||||
    sport->sp_rcvbuf = prog->prog_dummy_sport.sp_rcvbuf;
 | 
			
		||||
    TAILQ_INSERT_TAIL(prog->prog_sports, sport, next_sport);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_print_common_options (const struct prog *prog, FILE *out)
 | 
			
		||||
{
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
#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"
 | 
			
		||||
"                 resolved.  If host is not set, the value of SNI is\n"
 | 
			
		||||
"                 used (see the -H flag).  If port is not set, the default\n"
 | 
			
		||||
"                 is 443.\n"
 | 
			
		||||
#else
 | 
			
		||||
"   -s SVCPORT  Service port.  Takes on the form of host:port or host.\n"
 | 
			
		||||
"                 If host is not an IPv4 or IPv6 address, it is resolved.\n"
 | 
			
		||||
"                 If port is not set, the default is 443.\n"
 | 
			
		||||
#endif
 | 
			
		||||
"                 Examples:\n"
 | 
			
		||||
"                     127.0.0.1:12345\n"
 | 
			
		||||
"                     ::1:443\n"
 | 
			
		||||
"                     example.com\n"
 | 
			
		||||
"                     example.com:8443\n"
 | 
			
		||||
#if HAVE_REGEX
 | 
			
		||||
"                     8443\n"
 | 
			
		||||
#endif
 | 
			
		||||
"                 If no -s option is given, 0.0.0.0:12345 address\n"
 | 
			
		||||
"                 is used.\n"
 | 
			
		||||
#if LSQUIC_DONTFRAG_SUPPORTED
 | 
			
		||||
"   -D          Do not set `do not fragment' flag on outgoing UDP packets\n"
 | 
			
		||||
#endif
 | 
			
		||||
"   -z BYTES    Maximum size of outgoing UDP packets.  The default is 1370\n"
 | 
			
		||||
"                 bytes for IPv4 socket and 1350 bytes for IPv6 socket\n"
 | 
			
		||||
"   -L LEVEL    Log level for all modules.  Possible values are `debug',\n"
 | 
			
		||||
"                 `info', `notice', `warn', `error', `alert', `emerg',\n"
 | 
			
		||||
"                 and `crit'.\n"
 | 
			
		||||
"   -l LEVELS   Log levels for modules, e.g.\n"
 | 
			
		||||
"                 -l event=info,engine=debug\n"
 | 
			
		||||
"               Can be specified more than once.\n"
 | 
			
		||||
"   -m MAX      Maximum number of outgoing packet buffers that can be\n"
 | 
			
		||||
"                 assigned at any one time.  By default, there is no max.\n"
 | 
			
		||||
"   -y style    Timestamp style used in log messages.  The following styles\n"
 | 
			
		||||
"                 are supported:\n"
 | 
			
		||||
"                   0   No timestamp\n"
 | 
			
		||||
"                   1   Millisecond time (this is the default).\n"
 | 
			
		||||
"                         Example: 11:04:05.196\n"
 | 
			
		||||
"                   2   Full date and millisecond time.\n"
 | 
			
		||||
"                         Example: 2017-03-21 13:43:46.671\n"
 | 
			
		||||
"                   3   Chrome-like timestamp: date/time.microseconds.\n"
 | 
			
		||||
"                         Example: 1223/104613.946956\n"
 | 
			
		||||
"                   4   Microsecond time.\n"
 | 
			
		||||
"                         Example: 11:04:05.196308\n"
 | 
			
		||||
"                   5   Full date and microsecond time.\n"
 | 
			
		||||
"                         Example: 2017-03-21 13:43:46.671345\n"
 | 
			
		||||
"   -S opt=val  Socket options.  Supported options:\n"
 | 
			
		||||
"                   sndbuf=12345    # Sets SO_SNDBUF\n"
 | 
			
		||||
"                   rcvbuf=12345    # Sets SO_RCVBUF\n"
 | 
			
		||||
"   -W          Use stock PMI (malloc & free)\n"
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
#if HAVE_SENDMMSG
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
"   -g          Use sendmmsg() to send packets.\n"
 | 
			
		||||
    );
 | 
			
		||||
#endif
 | 
			
		||||
#if HAVE_RECVMMSG
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
"   -j          Use recvmmsg() to receive packets.\n"
 | 
			
		||||
    );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (prog->prog_engine_flags & LSENG_SERVER)
 | 
			
		||||
        fprintf(out,
 | 
			
		||||
"   -c CERTSPEC Service specification.  The specification is three values\n"
 | 
			
		||||
"                 separated by commas.  The values are:\n"
 | 
			
		||||
"                   * Domain name\n"
 | 
			
		||||
"                   * File containing cert in PEM format\n"
 | 
			
		||||
"                   * File containing private key in DER or PEM format\n"
 | 
			
		||||
"                 Example:\n"
 | 
			
		||||
"                   -c www.example.com,/tmp/cert.pem,/tmp/key.pkcs8\n"
 | 
			
		||||
        );
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (prog->prog_engine_flags & LSENG_HTTP)
 | 
			
		||||
            fprintf(out,
 | 
			
		||||
"   -H host     Value of `host' HTTP header.  This is also used as SNI\n"
 | 
			
		||||
"                 in Client Hello.  This option is used to override the\n"
 | 
			
		||||
"                 `host' part of the address specified using -s flag.\n"
 | 
			
		||||
            );
 | 
			
		||||
        else
 | 
			
		||||
            fprintf(out,
 | 
			
		||||
"   -H host     Value of SNI in CHLO.\n"
 | 
			
		||||
            );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
"   -G dir      SSL keys will be logged to files in this directory.\n"
 | 
			
		||||
    );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
"   -k          Connect UDP socket.  Only meant to be used with clients\n"
 | 
			
		||||
"                 to pick up ICMP errors.\n"
 | 
			
		||||
"   -i USECS    Clock granularity in microseconds.  Defaults to %u.\n",
 | 
			
		||||
        LSQUIC_DF_CLOCK_GRANULARITY
 | 
			
		||||
    );
 | 
			
		||||
    fprintf(out,
 | 
			
		||||
"   -h          Print this help screen and exit\n"
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_set_opt (struct prog *prog, int opt, const char *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct stat st;
 | 
			
		||||
    int s;
 | 
			
		||||
 | 
			
		||||
    switch (opt)
 | 
			
		||||
    {
 | 
			
		||||
#if LSQUIC_DONTFRAG_SUPPORTED
 | 
			
		||||
    case 'D':
 | 
			
		||||
        {
 | 
			
		||||
            struct service_port *sport = TAILQ_LAST(prog->prog_sports, sport_head);
 | 
			
		||||
            if (!sport)
 | 
			
		||||
                sport = &prog->prog_dummy_sport;
 | 
			
		||||
            sport->sp_flags |= SPORT_FRAGMENT_OK;
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
#endif
 | 
			
		||||
#if HAVE_SENDMMSG
 | 
			
		||||
    case 'g':
 | 
			
		||||
        prog->prog_use_sendmmsg = 1;
 | 
			
		||||
        return 0;
 | 
			
		||||
#endif
 | 
			
		||||
#if HAVE_RECVMMSG
 | 
			
		||||
    case 'j':
 | 
			
		||||
        prog->prog_use_recvmmsg = 1;
 | 
			
		||||
        return 0;
 | 
			
		||||
#endif
 | 
			
		||||
    case 'm':
 | 
			
		||||
        prog->prog_packout_max = atoi(arg);
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'z':
 | 
			
		||||
        prog->prog_max_packet_size = atoi(arg);
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'W':
 | 
			
		||||
        prog->prog_use_stock_pmi = 1;
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'c':
 | 
			
		||||
        if (prog->prog_engine_flags & LSENG_SERVER)
 | 
			
		||||
        {
 | 
			
		||||
            if (!prog->prog_certs)
 | 
			
		||||
                prog->prog_certs = lsquic_hash_create();
 | 
			
		||||
            return load_cert(prog->prog_certs, arg);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return -1;
 | 
			
		||||
    case 'H':
 | 
			
		||||
        if (prog->prog_engine_flags & LSENG_SERVER)
 | 
			
		||||
            return -1;
 | 
			
		||||
        prog->prog_hostname = arg;
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'y':
 | 
			
		||||
        lsquic_log_to_fstream(stderr, atoi(arg));
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'L':
 | 
			
		||||
        return lsquic_set_log_level(arg);
 | 
			
		||||
    case 'l':
 | 
			
		||||
        return lsquic_logger_lopt(arg);
 | 
			
		||||
    case 'o':
 | 
			
		||||
        return set_engine_option(&prog->prog_settings,
 | 
			
		||||
                                            &prog->prog_version_cleared, arg);
 | 
			
		||||
    case 'i':
 | 
			
		||||
        prog->prog_settings.es_clock_granularity = atoi(arg);
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 's':
 | 
			
		||||
        if (0 == (prog->prog_engine_flags & LSENG_SERVER) &&
 | 
			
		||||
                                            !TAILQ_EMPTY(prog->prog_sports))
 | 
			
		||||
            return -1;
 | 
			
		||||
        return prog_add_sport(prog, arg);
 | 
			
		||||
    case 'S':
 | 
			
		||||
        {
 | 
			
		||||
            struct service_port *sport = TAILQ_LAST(prog->prog_sports, sport_head);
 | 
			
		||||
            if (!sport)
 | 
			
		||||
                sport = &prog->prog_dummy_sport;
 | 
			
		||||
            char *const name = strdup(optarg);
 | 
			
		||||
            char *val = strchr(name, '=');
 | 
			
		||||
            if (!val)
 | 
			
		||||
            {
 | 
			
		||||
                free(name);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            *val = '\0';
 | 
			
		||||
            ++val;
 | 
			
		||||
            if (0 == strcasecmp(name, "sndbuf"))
 | 
			
		||||
            {
 | 
			
		||||
                sport->sp_flags |= SPORT_SET_SNDBUF;
 | 
			
		||||
                sport->sp_sndbuf = atoi(val);
 | 
			
		||||
                free(name);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            else if (0 == strcasecmp(name, "rcvbuf"))
 | 
			
		||||
            {
 | 
			
		||||
                sport->sp_flags |= SPORT_SET_RCVBUF;
 | 
			
		||||
                sport->sp_rcvbuf = atoi(val);
 | 
			
		||||
                free(name);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                free(name);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    case 'k':
 | 
			
		||||
        {
 | 
			
		||||
            struct service_port *sport = TAILQ_LAST(prog->prog_sports, sport_head);
 | 
			
		||||
            if (!sport)
 | 
			
		||||
                sport = &prog->prog_dummy_sport;
 | 
			
		||||
            sport->sp_flags |= SPORT_CONNECT;
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    case 'G':
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
        if (0 == stat(optarg, &st))
 | 
			
		||||
        {
 | 
			
		||||
            if (!S_ISDIR(st.st_mode))
 | 
			
		||||
            {
 | 
			
		||||
                LSQ_ERROR("%s is not a directory", optarg);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            s = mkdir(optarg, 0700);
 | 
			
		||||
            if (s != 0)
 | 
			
		||||
            {
 | 
			
		||||
                LSQ_ERROR("cannot create directory %s: %s", optarg,
 | 
			
		||||
                                                        strerror(errno));
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        prog->prog_keylog_dir = optarg;
 | 
			
		||||
        return 0;
 | 
			
		||||
#else
 | 
			
		||||
        LSQ_ERROR("key logging is not supported on Windows");
 | 
			
		||||
        return -1;
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct event_base *
 | 
			
		||||
prog_eb (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    return prog->prog_eb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_connect (struct prog *prog, unsigned char *zero_rtt, size_t zero_rtt_len)
 | 
			
		||||
{
 | 
			
		||||
    struct service_port *sport;
 | 
			
		||||
 | 
			
		||||
    sport = TAILQ_FIRST(prog->prog_sports);
 | 
			
		||||
    if (NULL == lsquic_engine_connect(prog->prog_engine, N_LSQVER,
 | 
			
		||||
                    (struct sockaddr *) &sport->sp_local_addr,
 | 
			
		||||
                    (struct sockaddr *) &sport->sas, sport, NULL,
 | 
			
		||||
                    prog->prog_hostname ? prog->prog_hostname
 | 
			
		||||
                    /* SNI is required for HTTP */
 | 
			
		||||
                  : prog->prog_engine_flags & LSENG_HTTP ? sport->host
 | 
			
		||||
                  : NULL,
 | 
			
		||||
                    prog->prog_max_packet_size, zero_rtt, zero_rtt_len,
 | 
			
		||||
                    sport->sp_token_buf, sport->sp_token_sz))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    prog_process_conns(prog);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
prog_init_client (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    struct service_port *sport;
 | 
			
		||||
 | 
			
		||||
    sport = TAILQ_FIRST(prog->prog_sports);
 | 
			
		||||
    if (0 != sport_init_client(sport, prog->prog_engine, prog->prog_eb))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static SSL_CTX *
 | 
			
		||||
get_ssl_ctx (void *peer_ctx)
 | 
			
		||||
{
 | 
			
		||||
    const struct service_port *const sport = peer_ctx;
 | 
			
		||||
    return sport->sp_prog->prog_ssl_ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
prog_init_server (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    struct service_port *sport;
 | 
			
		||||
    unsigned char ticket_keys[48];
 | 
			
		||||
 | 
			
		||||
    prog->prog_ssl_ctx = SSL_CTX_new(TLS_method());
 | 
			
		||||
    if (prog->prog_ssl_ctx)
 | 
			
		||||
    {
 | 
			
		||||
        SSL_CTX_set_min_proto_version(prog->prog_ssl_ctx, TLS1_3_VERSION);
 | 
			
		||||
        SSL_CTX_set_max_proto_version(prog->prog_ssl_ctx, TLS1_3_VERSION);
 | 
			
		||||
        SSL_CTX_set_default_verify_paths(prog->prog_ssl_ctx);
 | 
			
		||||
 | 
			
		||||
        /* This is obviously test code: the key is just an array of NUL bytes */
 | 
			
		||||
        memset(ticket_keys, 0, sizeof(ticket_keys));
 | 
			
		||||
        if (1 != SSL_CTX_set_tlsext_ticket_keys(prog->prog_ssl_ctx,
 | 
			
		||||
                                            ticket_keys, sizeof(ticket_keys)))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("SSL_CTX_set_tlsext_ticket_keys failed");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_WARN("cannot create SSL context");
 | 
			
		||||
 | 
			
		||||
    TAILQ_FOREACH(sport, prog->prog_sports, next_sport)
 | 
			
		||||
        if (0 != sport_init_server(sport, prog->prog_engine, prog->prog_eb))
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_process_conns (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    int diff;
 | 
			
		||||
    struct timeval timeout;
 | 
			
		||||
 | 
			
		||||
    lsquic_engine_process_conns(prog->prog_engine);
 | 
			
		||||
 | 
			
		||||
    if (lsquic_engine_earliest_adv_tick(prog->prog_engine, &diff))
 | 
			
		||||
    {
 | 
			
		||||
        if (diff < 0
 | 
			
		||||
                || (unsigned) diff < prog->prog_settings.es_clock_granularity)
 | 
			
		||||
        {
 | 
			
		||||
            timeout.tv_sec  = 0;
 | 
			
		||||
            timeout.tv_usec = prog->prog_settings.es_clock_granularity;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            timeout.tv_sec = (unsigned) diff / 1000000;
 | 
			
		||||
            timeout.tv_usec = (unsigned) diff % 1000000;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!prog_is_stopped())
 | 
			
		||||
            event_add(prog->prog_timer, &timeout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prog_timer_handler (int fd, short what, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct prog *const prog = arg;
 | 
			
		||||
    if (!prog_is_stopped())
 | 
			
		||||
        prog_process_conns(prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prog_usr1_handler (int fd, short what, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    LSQ_NOTICE("Got SIGUSR1, stopping engine");
 | 
			
		||||
    prog_stop(arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prog_usr2_handler (int fd, short what, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct prog *const prog = arg;
 | 
			
		||||
 | 
			
		||||
    LSQ_NOTICE("Got SIGUSR2, cool down engine");
 | 
			
		||||
    prog->prog_flags |= PROG_FLAG_COOLDOWN;
 | 
			
		||||
    lsquic_engine_cooldown(prog->prog_engine);
 | 
			
		||||
    prog_process_conns(prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_run (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
    prog->prog_usr1 = evsignal_new(prog->prog_eb, SIGUSR1,
 | 
			
		||||
                                                    prog_usr1_handler, prog);
 | 
			
		||||
    evsignal_add(prog->prog_usr1, NULL);
 | 
			
		||||
    prog->prog_usr2 = evsignal_new(prog->prog_eb, SIGUSR2,
 | 
			
		||||
                                                    prog_usr2_handler, prog);
 | 
			
		||||
    evsignal_add(prog->prog_usr2, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    event_base_loop(prog->prog_eb, 0);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_cleanup (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_engine_destroy(prog->prog_engine);
 | 
			
		||||
    event_base_free(prog->prog_eb);
 | 
			
		||||
    if (!prog->prog_use_stock_pmi)
 | 
			
		||||
        pba_cleanup(&prog->prog_pba);
 | 
			
		||||
    if (prog->prog_ssl_ctx)
 | 
			
		||||
        SSL_CTX_free(prog->prog_ssl_ctx);
 | 
			
		||||
    if (prog->prog_certs)
 | 
			
		||||
        delete_certs(prog->prog_certs);
 | 
			
		||||
    lsquic_global_cleanup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_stop (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    struct service_port *sport;
 | 
			
		||||
 | 
			
		||||
    prog_stopped = 1;
 | 
			
		||||
 | 
			
		||||
    while ((sport = TAILQ_FIRST(prog->prog_sports)))
 | 
			
		||||
    {
 | 
			
		||||
        TAILQ_REMOVE(prog->prog_sports, sport, next_sport);
 | 
			
		||||
        sport_destroy(sport);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (prog->prog_timer)
 | 
			
		||||
    {
 | 
			
		||||
        event_del(prog->prog_timer);
 | 
			
		||||
        event_free(prog->prog_timer);
 | 
			
		||||
        prog->prog_timer = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (prog->prog_usr1)
 | 
			
		||||
    {
 | 
			
		||||
        event_del(prog->prog_usr1);
 | 
			
		||||
        event_free(prog->prog_usr1);
 | 
			
		||||
        prog->prog_usr1 = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (prog->prog_usr2)
 | 
			
		||||
    {
 | 
			
		||||
        event_del(prog->prog_usr2);
 | 
			
		||||
        event_free(prog->prog_usr2);
 | 
			
		||||
        prog->prog_usr2 = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void *
 | 
			
		||||
keylog_open (void *ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    const struct prog *const prog = ctx;
 | 
			
		||||
    const lsquic_cid_t *cid;
 | 
			
		||||
    FILE *fh;
 | 
			
		||||
    int sz;
 | 
			
		||||
    char id_str[MAX_CID_LEN * 2 + 1];
 | 
			
		||||
    char path[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
    cid = lsquic_conn_id(conn);
 | 
			
		||||
    lsquic_hexstr(cid->idbuf, cid->len, id_str, sizeof(id_str));
 | 
			
		||||
    sz = snprintf(path, sizeof(path), "%s/%s.keys", prog->prog_keylog_dir,
 | 
			
		||||
                                                                    id_str);
 | 
			
		||||
    if ((size_t) sz >= sizeof(path))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("%s: file too long", __func__);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    fh = fopen(path, "w");
 | 
			
		||||
    if (!fh)
 | 
			
		||||
        LSQ_WARN("could not open %s for writing: %s", path, strerror(errno));
 | 
			
		||||
    return fh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
keylog_log_line (void *handle, const char *line)
 | 
			
		||||
{
 | 
			
		||||
    size_t len;
 | 
			
		||||
 | 
			
		||||
    len = strlen(line);
 | 
			
		||||
    if (len < sizeof("QUIC_") - 1 || strncmp(line, "QUIC_", 5))
 | 
			
		||||
        fputs("QUIC_", handle);
 | 
			
		||||
    fputs(line, handle);
 | 
			
		||||
    fputs("\n", handle);
 | 
			
		||||
    fflush(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
keylog_close (void *handle)
 | 
			
		||||
{
 | 
			
		||||
    fclose(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const struct lsquic_keylog_if keylog_if =
 | 
			
		||||
{
 | 
			
		||||
    .kli_open       = keylog_open,
 | 
			
		||||
    .kli_log_line   = keylog_log_line,
 | 
			
		||||
    .kli_close      = keylog_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct ssl_ctx_st *
 | 
			
		||||
no_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED, const char *sni)
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_prep (struct prog *prog)
 | 
			
		||||
{
 | 
			
		||||
    int s;
 | 
			
		||||
    char err_buf[100];
 | 
			
		||||
 | 
			
		||||
    if (prog->prog_keylog_dir)
 | 
			
		||||
    {
 | 
			
		||||
        prog->prog_api.ea_keylog_if = &keylog_if;
 | 
			
		||||
        prog->prog_api.ea_keylog_ctx = prog;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != lsquic_engine_check_settings(prog->prog_api.ea_settings,
 | 
			
		||||
                        prog->prog_engine_flags, err_buf, sizeof(err_buf)))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("Error in settings: %s", err_buf);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!prog->prog_use_stock_pmi)
 | 
			
		||||
        pba_init(&prog->prog_pba, prog->prog_packout_max);
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        prog->prog_api.ea_pmi = NULL;
 | 
			
		||||
        prog->prog_api.ea_pmi_ctx = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (TAILQ_EMPTY(prog->prog_sports))
 | 
			
		||||
    {
 | 
			
		||||
        if (prog->prog_hostname)
 | 
			
		||||
            s = prog_add_sport(prog, prog->prog_hostname);
 | 
			
		||||
        else
 | 
			
		||||
            s = prog_add_sport(prog, "0.0.0.0:12345");
 | 
			
		||||
        if (0 != s)
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (prog->prog_certs)
 | 
			
		||||
    {
 | 
			
		||||
    prog->prog_api.ea_lookup_cert = lookup_cert;
 | 
			
		||||
    prog->prog_api.ea_cert_lu_ctx = prog->prog_certs;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (prog->prog_engine_flags & LSENG_SERVER)
 | 
			
		||||
            LSQ_WARN("Not a single service specified.  Use -c option.");
 | 
			
		||||
        prog->prog_api.ea_lookup_cert = no_cert;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    prog->prog_eb = event_base_new();
 | 
			
		||||
    prog->prog_engine = lsquic_engine_new(prog->prog_engine_flags,
 | 
			
		||||
                                                            &prog->prog_api);
 | 
			
		||||
    if (!prog->prog_engine)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    prog->prog_timer = event_new(prog->prog_eb, -1, 0,
 | 
			
		||||
                                        prog_timer_handler, prog);
 | 
			
		||||
 | 
			
		||||
    if (prog->prog_engine_flags & LSENG_SERVER)
 | 
			
		||||
        s = prog_init_server(prog);
 | 
			
		||||
    else
 | 
			
		||||
        s = prog_init_client(prog);
 | 
			
		||||
 | 
			
		||||
    if (s != 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_is_stopped (void)
 | 
			
		||||
{
 | 
			
		||||
    return prog_stopped != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
send_unsent (evutil_socket_t fd, short what, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct prog *const prog = arg;
 | 
			
		||||
    assert(prog->prog_send);
 | 
			
		||||
    event_del(prog->prog_send);
 | 
			
		||||
    event_free(prog->prog_send);
 | 
			
		||||
    prog->prog_send = NULL;
 | 
			
		||||
    LSQ_DEBUG("on_write event fires");
 | 
			
		||||
    lsquic_engine_send_unsent_packets(prog->prog_engine);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_sport_cant_send (struct prog *prog, int fd)
 | 
			
		||||
{
 | 
			
		||||
    assert(!prog->prog_send);
 | 
			
		||||
    LSQ_DEBUG("cannot send: register on_write event");
 | 
			
		||||
    prog->prog_send = event_new(prog->prog_eb, fd, EV_WRITE, send_unsent, prog);
 | 
			
		||||
    event_add(prog->prog_send, NULL);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								bin/prog.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								bin/prog.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,119 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * prog.h -- common setup and options for QUIC program
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef PROG_H
 | 
			
		||||
#define PROG_H 1
 | 
			
		||||
 | 
			
		||||
#include "test_config.h"
 | 
			
		||||
 | 
			
		||||
struct event;
 | 
			
		||||
struct event_base;
 | 
			
		||||
struct lsquic_hash;
 | 
			
		||||
struct sport_head;
 | 
			
		||||
struct ssl_ctx_st;
 | 
			
		||||
 | 
			
		||||
struct prog
 | 
			
		||||
{
 | 
			
		||||
    struct packout_buf_allocator    prog_pba;
 | 
			
		||||
    struct lsquic_engine_settings   prog_settings;
 | 
			
		||||
    struct lsquic_engine_api        prog_api;
 | 
			
		||||
    unsigned                        prog_engine_flags;
 | 
			
		||||
    struct service_port             prog_dummy_sport;   /* Use for options */
 | 
			
		||||
    unsigned                        prog_packout_max;
 | 
			
		||||
    unsigned short                  prog_max_packet_size;
 | 
			
		||||
    int                             prog_version_cleared;
 | 
			
		||||
    unsigned long                   prog_read_count;
 | 
			
		||||
#if HAVE_SENDMMSG
 | 
			
		||||
    int                             prog_use_sendmmsg;
 | 
			
		||||
#endif
 | 
			
		||||
#if HAVE_RECVMMSG
 | 
			
		||||
    int                             prog_use_recvmmsg;
 | 
			
		||||
#endif
 | 
			
		||||
    int                             prog_use_stock_pmi;
 | 
			
		||||
    struct event_base              *prog_eb;
 | 
			
		||||
    struct event                   *prog_timer,
 | 
			
		||||
                                   *prog_send,
 | 
			
		||||
                                   *prog_usr1;
 | 
			
		||||
    struct event                   *prog_usr2;
 | 
			
		||||
    struct ssl_ctx_st              *prog_ssl_ctx;
 | 
			
		||||
    struct lsquic_hash             *prog_certs;
 | 
			
		||||
    struct event                   *prog_event_sni;
 | 
			
		||||
    char                           *prog_susp_sni;
 | 
			
		||||
    struct sport_head              *prog_sports;
 | 
			
		||||
    struct lsquic_engine           *prog_engine;
 | 
			
		||||
    const char                     *prog_hostname;
 | 
			
		||||
    int                             prog_ipver;     /* 0, 4, or 6 */
 | 
			
		||||
    const char                     *prog_keylog_dir;
 | 
			
		||||
    enum {
 | 
			
		||||
        PROG_FLAG_COOLDOWN  = 1 << 0,
 | 
			
		||||
#if LSQUIC_PREFERRED_ADDR
 | 
			
		||||
        PROG_SEARCH_ADDRS   = 1 << 1,
 | 
			
		||||
#endif
 | 
			
		||||
    }                               prog_flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_init (struct prog *, unsigned lsquic_engine_flags, struct sport_head *,
 | 
			
		||||
                    const struct lsquic_stream_if *, void *stream_if_ctx);
 | 
			
		||||
 | 
			
		||||
#if HAVE_SENDMMSG
 | 
			
		||||
#   define SENDMMSG_FLAG "g"
 | 
			
		||||
#else
 | 
			
		||||
#   define SENDMMSG_FLAG ""
 | 
			
		||||
#endif
 | 
			
		||||
#if HAVE_RECVMMSG
 | 
			
		||||
#   define RECVMMSG_FLAG "j"
 | 
			
		||||
#else
 | 
			
		||||
#   define RECVMMSG_FLAG ""
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if LSQUIC_DONTFRAG_SUPPORTED
 | 
			
		||||
#   define IP_DONTFRAG_FLAG "D"
 | 
			
		||||
#else
 | 
			
		||||
#   define IP_DONTFRAG_FLAG ""
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define PROG_OPTS "i:km:c:y:L:l:o:H:s:S:Y:z:G:W" RECVMMSG_FLAG SENDMMSG_FLAG \
 | 
			
		||||
                                                            IP_DONTFRAG_FLAG
 | 
			
		||||
 | 
			
		||||
/* Returns:
 | 
			
		||||
 *  0   Applied
 | 
			
		||||
 *  1   Not applicable
 | 
			
		||||
 * -1   Error
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
prog_set_opt (struct prog *, int opt, const char *arg);
 | 
			
		||||
 | 
			
		||||
struct event_base *
 | 
			
		||||
prog_eb (struct prog *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_run (struct prog *);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_cleanup (struct prog *);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_stop (struct prog *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_prep (struct prog *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_connect (struct prog *, unsigned char *, size_t);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_print_common_options (const struct prog *, FILE *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
prog_is_stopped (void);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_process_conns (struct prog *);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
prog_sport_cant_send (struct prog *, int fd);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										176
									
								
								bin/test_cert.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								bin/test_cert.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,176 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
 | 
			
		||||
#include <openssl/pem.h>
 | 
			
		||||
#include <openssl/x509.h>
 | 
			
		||||
#include <openssl/ssl.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic_types.h"
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_hash.h"
 | 
			
		||||
 | 
			
		||||
#include "test_cert.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
 | 
			
		||||
                    const unsigned char *in, unsigned int inlen, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    const unsigned char alpn[] = "\x5h3-25\x5h3-27";
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,
 | 
			
		||||
                                                            alpn, sizeof(alpn));
 | 
			
		||||
    if (r == OPENSSL_NPN_NEGOTIATED)
 | 
			
		||||
        return SSL_TLSEXT_ERR_OK;
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("no supported protocol can be selected from %.*s",
 | 
			
		||||
                                                    (int) inlen, (char *) in);
 | 
			
		||||
        return SSL_TLSEXT_ERR_ALERT_FATAL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
load_cert (struct lsquic_hash *certs, const char *optarg)
 | 
			
		||||
{
 | 
			
		||||
    int rv = -1;
 | 
			
		||||
    char *sni, *cert_file, *key_file;
 | 
			
		||||
    struct server_cert *cert = NULL;
 | 
			
		||||
    EVP_PKEY *pkey = NULL;
 | 
			
		||||
    FILE *f = NULL;
 | 
			
		||||
 | 
			
		||||
    sni = strdup(optarg);
 | 
			
		||||
    cert_file = strchr(sni, ',');
 | 
			
		||||
    if (!cert_file)
 | 
			
		||||
        goto end;
 | 
			
		||||
    *cert_file = '\0';
 | 
			
		||||
    ++cert_file;
 | 
			
		||||
    key_file = strchr(cert_file, ',');
 | 
			
		||||
    if (!key_file)
 | 
			
		||||
        goto end;
 | 
			
		||||
    *key_file = '\0';
 | 
			
		||||
    ++key_file;
 | 
			
		||||
 | 
			
		||||
    cert = calloc(1, sizeof(*cert));
 | 
			
		||||
    cert->ce_sni = strdup(sni);
 | 
			
		||||
 | 
			
		||||
    cert->ce_ssl_ctx = SSL_CTX_new(TLS_method());
 | 
			
		||||
    if (!cert->ce_ssl_ctx)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("SSL_CTX_new failed");
 | 
			
		||||
        goto end;
 | 
			
		||||
    }
 | 
			
		||||
    SSL_CTX_set_min_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
 | 
			
		||||
    SSL_CTX_set_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
 | 
			
		||||
    SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx);
 | 
			
		||||
    SSL_CTX_set_alpn_select_cb(cert->ce_ssl_ctx, select_alpn, NULL);
 | 
			
		||||
    SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1);    /* XXX */
 | 
			
		||||
    if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file);
 | 
			
		||||
        goto end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strstr(key_file, ".pkcs8"))
 | 
			
		||||
    {
 | 
			
		||||
        f = fopen(key_file, "r");
 | 
			
		||||
        if (!f)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("fopen(%s) failed: %s", cert_file, strerror(errno));
 | 
			
		||||
            goto end;
 | 
			
		||||
        }
 | 
			
		||||
        pkey = d2i_PrivateKey_fp(f, NULL);
 | 
			
		||||
        fclose(f);
 | 
			
		||||
        f = NULL;
 | 
			
		||||
        if (!pkey)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("Reading private key from %s failed", key_file);
 | 
			
		||||
            goto end;
 | 
			
		||||
        }
 | 
			
		||||
        if (!SSL_CTX_use_PrivateKey(cert->ce_ssl_ctx, pkey))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("SSL_CTX_use_PrivateKey failed");
 | 
			
		||||
            goto end;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (1 != SSL_CTX_use_PrivateKey_file(cert->ce_ssl_ctx, key_file,
 | 
			
		||||
                                                            SSL_FILETYPE_PEM))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("SSL_CTX_use_PrivateKey_file failed");
 | 
			
		||||
        goto end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int was = SSL_CTX_set_session_cache_mode(cert->ce_ssl_ctx, 1);
 | 
			
		||||
    LSQ_DEBUG("set SSL session cache mode to 1 (was: %d)", was);
 | 
			
		||||
 | 
			
		||||
    if (lsquic_hash_insert(certs, cert->ce_sni, strlen(cert->ce_sni), cert,
 | 
			
		||||
                                                            &cert->ce_hash_el))
 | 
			
		||||
        rv = 0;
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_WARN("cannot insert cert for %s into hash table", cert->ce_sni);
 | 
			
		||||
 | 
			
		||||
  end:
 | 
			
		||||
    free(sni);
 | 
			
		||||
    if (rv != 0)
 | 
			
		||||
    {   /* Error: free cert and its components */
 | 
			
		||||
        if (cert)
 | 
			
		||||
        {
 | 
			
		||||
            free(cert->ce_sni);
 | 
			
		||||
            free(cert);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ssl_ctx_st *
 | 
			
		||||
lookup_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED,
 | 
			
		||||
             const char *sni)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_hash_elem *el;
 | 
			
		||||
    struct server_cert *server_cert;
 | 
			
		||||
 | 
			
		||||
    if (!cert_lu_ctx)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    if (sni)
 | 
			
		||||
        el = lsquic_hash_find(cert_lu_ctx, sni, strlen(sni));
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("SNI is not set");
 | 
			
		||||
        el = lsquic_hash_first(cert_lu_ctx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (el)
 | 
			
		||||
    {
 | 
			
		||||
        server_cert = lsquic_hashelem_getdata(el);
 | 
			
		||||
        if (server_cert)
 | 
			
		||||
            return server_cert->ce_ssl_ctx;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
delete_certs (struct lsquic_hash *certs)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_hash_elem *el;
 | 
			
		||||
    struct server_cert *cert;
 | 
			
		||||
 | 
			
		||||
    for (el = lsquic_hash_first(certs); el; el = lsquic_hash_next(certs))
 | 
			
		||||
    {
 | 
			
		||||
        cert = lsquic_hashelem_getdata(el);
 | 
			
		||||
        SSL_CTX_free(cert->ce_ssl_ctx);
 | 
			
		||||
        free(cert->ce_sni);
 | 
			
		||||
        free(cert);
 | 
			
		||||
    }
 | 
			
		||||
    lsquic_hash_destroy(certs);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								bin/test_cert.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								bin/test_cert.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
#ifndef TEST_CERT_H
 | 
			
		||||
#define TEST_CERT_H
 | 
			
		||||
 | 
			
		||||
struct lsquic_hash;
 | 
			
		||||
struct ssl_ctx_st;
 | 
			
		||||
struct sockaddr;
 | 
			
		||||
 | 
			
		||||
struct server_cert
 | 
			
		||||
{
 | 
			
		||||
    char                *ce_sni;
 | 
			
		||||
    struct ssl_ctx_st   *ce_ssl_ctx;
 | 
			
		||||
    struct lsquic_hash_elem ce_hash_el;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
load_cert (struct lsquic_hash *, const char *optarg);
 | 
			
		||||
 | 
			
		||||
struct ssl_ctx_st *
 | 
			
		||||
lookup_cert (void *cert_lu_ctx, const struct sockaddr * /*unused */,
 | 
			
		||||
             const char *sni);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
delete_certs (struct lsquic_hash *);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										2207
									
								
								bin/test_common.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2207
									
								
								bin/test_common.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										128
									
								
								bin/test_common.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								bin/test_common.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,128 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * Test client's and server's common components.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef TEST_COMMON_H
 | 
			
		||||
#define TEST_COMMON_H 1
 | 
			
		||||
 | 
			
		||||
#if __linux__
 | 
			
		||||
#   include <net/if.h>  /* For IFNAMSIZ */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct lsquic_engine;
 | 
			
		||||
struct lsquic_engine_settings;
 | 
			
		||||
struct lsquic_out_spec;
 | 
			
		||||
struct event_base;
 | 
			
		||||
struct event;
 | 
			
		||||
struct packets_in;
 | 
			
		||||
struct lsquic_conn;
 | 
			
		||||
struct prog;
 | 
			
		||||
struct reader_ctx;
 | 
			
		||||
 | 
			
		||||
enum sport_flags
 | 
			
		||||
{
 | 
			
		||||
#if LSQUIC_DONTFRAG_SUPPORTED
 | 
			
		||||
    SPORT_FRAGMENT_OK       = (1 << 0),
 | 
			
		||||
#endif
 | 
			
		||||
    SPORT_SET_SNDBUF        = (1 << 1), /* SO_SNDBUF */
 | 
			
		||||
    SPORT_SET_RCVBUF        = (1 << 2), /* SO_RCVBUF */
 | 
			
		||||
    SPORT_SERVER            = (1 << 3),
 | 
			
		||||
    SPORT_CONNECT           = (1 << 4),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct service_port {
 | 
			
		||||
    TAILQ_ENTRY(service_port)  next_sport;
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
    int                        fd;
 | 
			
		||||
#else
 | 
			
		||||
    SOCKET                        fd;
 | 
			
		||||
#endif
 | 
			
		||||
#if __linux__
 | 
			
		||||
    uint32_t                   n_dropped;
 | 
			
		||||
    int                        drop_init;
 | 
			
		||||
    char                       if_name[IFNAMSIZ];
 | 
			
		||||
#endif
 | 
			
		||||
    struct event              *ev;
 | 
			
		||||
    struct lsquic_engine      *engine;
 | 
			
		||||
    void                      *conn_ctx;
 | 
			
		||||
    char                       host[80];
 | 
			
		||||
    struct sockaddr_storage    sas;
 | 
			
		||||
    struct sockaddr_storage    sp_local_addr;
 | 
			
		||||
    struct packets_in         *packs_in;
 | 
			
		||||
    enum sport_flags           sp_flags;
 | 
			
		||||
    int                        sp_sndbuf;   /* If SPORT_SET_SNDBUF is set */
 | 
			
		||||
    int                        sp_rcvbuf;   /* If SPORT_SET_RCVBUF is set */
 | 
			
		||||
    struct prog               *sp_prog;
 | 
			
		||||
    unsigned char             *sp_token_buf;
 | 
			
		||||
    size_t                     sp_token_sz;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TAILQ_HEAD(sport_head, service_port);
 | 
			
		||||
 | 
			
		||||
struct service_port *
 | 
			
		||||
sport_new (const char *optarg, struct prog *);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
sport_destroy (struct service_port *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
sport_init_server (struct service_port *, struct lsquic_engine *,
 | 
			
		||||
                   struct event_base *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
sport_init_client (struct service_port *, struct lsquic_engine *,
 | 
			
		||||
                   struct event_base *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
sport_packets_out (void *ctx, const struct lsquic_out_spec *, unsigned count);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
sport_set_token (struct service_port *, const char *);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
set_engine_option (struct lsquic_engine_settings *,
 | 
			
		||||
                   int *version_cleared, const char *name_value);
 | 
			
		||||
 | 
			
		||||
struct packout_buf;
 | 
			
		||||
 | 
			
		||||
struct packout_buf_allocator
 | 
			
		||||
{
 | 
			
		||||
    unsigned                    n_out,      /* Number of buffers outstanding */
 | 
			
		||||
                                max;        /* Maximum outstanding.  Zero mean no limit */
 | 
			
		||||
    SLIST_HEAD(, packout_buf)   free_packout_bufs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
pba_init (struct packout_buf_allocator *, unsigned max);
 | 
			
		||||
 | 
			
		||||
void *
 | 
			
		||||
pba_allocate (void *packout_buf_allocator, void*, unsigned short, char);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
pba_release (void *packout_buf_allocator, void *, void *obj, char);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
pba_cleanup (struct packout_buf_allocator *);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
print_conn_info (const struct lsquic_conn *conn);
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
test_reader_size (void *void_ctx);
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
test_reader_read (void *void_ctx, void *buf, size_t count);
 | 
			
		||||
 | 
			
		||||
struct reader_ctx *
 | 
			
		||||
create_lsquic_reader_ctx (const char *filename);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
destroy_lsquic_reader_ctx (struct reader_ctx *ctx);
 | 
			
		||||
 | 
			
		||||
#define STRINGIFY(x) #x
 | 
			
		||||
#define TOSTRING(x) STRINGIFY(x)
 | 
			
		||||
#define LITESPEED_ID "lsquic" "/" TOSTRING(LSQUIC_MAJOR_VERSION) "." \
 | 
			
		||||
            TOSTRING(LSQUIC_MINOR_VERSION) "." TOSTRING(LSQUIC_PATCH_VERSION)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										22
									
								
								bin/test_config.h.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								bin/test_config.h.in
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
#ifndef LSQUIC_CONFIG_H
 | 
			
		||||
#define LSQUIC_CONFIG_H
 | 
			
		||||
 | 
			
		||||
#cmakedefine HAVE_SENDMMSG 1
 | 
			
		||||
#cmakedefine HAVE_RECVMMSG 1
 | 
			
		||||
#cmakedefine HAVE_OPEN_MEMSTREAM 1
 | 
			
		||||
#cmakedefine HAVE_IP_DONTFRAG 1
 | 
			
		||||
#cmakedefine HAVE_IP_MTU_DISCOVER 1
 | 
			
		||||
#cmakedefine HAVE_REGEX 1
 | 
			
		||||
 | 
			
		||||
#define LSQUIC_DONTFRAG_SUPPORTED (HAVE_IP_DONTFRAG || HAVE_IP_MTU_DISCOVER)
 | 
			
		||||
 | 
			
		||||
/* TODO: presumably it's the same on FreeBSD, test it.
 | 
			
		||||
 * See https://github.com/quicwg/base-drafts/wiki/ECN-in-QUIC
 | 
			
		||||
 */
 | 
			
		||||
#if __linux__ || defined(__FreeBSD__)
 | 
			
		||||
#define ECN_SUPPORTED 1
 | 
			
		||||
#else
 | 
			
		||||
#define ECN_SUPPORTED 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue