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