2021-01-06 14:00:05 +00:00
|
|
|
/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */
|
2019-10-15 21:02:21 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
2020-10-28 12:10:17 +00:00
|
|
|
#include "../src/liblsquic/lsquic_hash.h"
|
|
|
|
#include "test_cert.h"
|
2019-10-15 21:02:21 +00:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 12:10:17 +00:00
|
|
|
add_alpn("md5");
|
2019-10-15 21:02:21 +00:00
|
|
|
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);
|
|
|
|
}
|