1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * A duck quacks!  The server for the siduck protocol:
4 *      https://tools.ietf.org/html/draft-pardue-quic-siduck-00
5 */
6
7#include <assert.h>
8#include <signal.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/queue.h>
13#include <time.h>
14#ifndef WIN32
15#include <unistd.h>
16#include <netinet/in.h>
17#else
18#include "vc_compat.h"
19#include "getopt.h"
20#endif
21
22#include "lsquic.h"
23#include "../src/liblsquic/lsquic_hash.h"
24#include "test_common.h"
25#include "test_cert.h"
26#include "prog.h"
27
28#include "../src/liblsquic/lsquic_logger.h"
29
30
31static lsquic_conn_ctx_t *
32duck_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
33{
34    LSQ_NOTICE("New siduck connection established!");
35    return NULL;
36}
37
38
39static void
40duck_server_on_conn_closed (lsquic_conn_t *conn)
41{
42    LSQ_NOTICE("siduck connection closed");
43}
44
45
46/* Expected request and response of the siduck protocol */
47#define REQUEST "quack"
48#define RESPONSE "quack-ack"
49
50
51static ssize_t
52duck_on_dg_write (lsquic_conn_t *conn, void *buf, size_t sz)
53{
54    int s;
55
56    /* We only write one response */
57    s = lsquic_conn_want_datagram_write(conn, 0);
58    assert(s == 1);     /* Old value was "yes" */
59
60    if (sz >= sizeof(RESPONSE) - 1)
61    {
62        LSQ_INFO("wrote `%s' in response", RESPONSE);
63        memcpy(buf, RESPONSE, sizeof(RESPONSE) - 1);
64        lsquic_conn_close(conn);    /* Close connection right away */
65        return sizeof(RESPONSE) - 1;
66    }
67    else
68        return -1;
69}
70
71
72static void
73duck_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
74{
75    int s;
76
77    if (bufsz == sizeof(REQUEST) - 1
78            && 0 == memcmp(buf, REQUEST, sizeof(REQUEST) - 1))
79    {
80        LSQ_DEBUG("received the expected `%s' request", REQUEST);
81        s = lsquic_conn_want_datagram_write(conn, 1);
82        assert(s == 0);
83    }
84    else
85    {
86        LSQ_NOTICE("unexpected request received, abort connection");
87        lsquic_conn_abort(conn);
88    }
89}
90
91
92const struct lsquic_stream_if duck_server_stream_if = {
93    .on_new_conn            = duck_server_on_new_conn,
94    .on_conn_closed         = duck_server_on_conn_closed,
95    .on_dg_write            = duck_on_dg_write,
96    .on_datagram            = duck_on_datagram,
97};
98
99
100static void
101usage (const char *prog)
102{
103    const char *const slash = strrchr(prog, '/');
104    if (slash)
105        prog = slash + 1;
106    printf(
107"Usage: %s [opts]\n"
108"\n"
109"Options:\n"
110                , prog);
111}
112
113
114int
115main (int argc, char **argv)
116{
117    int opt, s;
118    struct prog prog;
119    struct sport_head sports;
120
121    TAILQ_INIT(&sports);
122    prog_init(&prog, LSENG_SERVER, &sports, &duck_server_stream_if, NULL);
123    prog.prog_settings.es_datagrams = 1;
124    prog.prog_settings.es_init_max_data = 0;
125    prog.prog_settings.es_init_max_streams_bidi = 0;
126    prog.prog_settings.es_init_max_streams_uni = 0;
127    prog.prog_settings.es_max_streams_in = 0;
128
129    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
130    {
131        switch (opt) {
132        case 'h':
133            usage(argv[0]);
134            prog_print_common_options(&prog, stdout);
135            exit(0);
136        default:
137            if (0 != prog_set_opt(&prog, opt, optarg))
138                exit(1);
139        }
140    }
141
142    if (0 != add_alpn("siduck-00"))
143    {
144        LSQ_ERROR("could not add ALPN");
145        exit(EXIT_FAILURE);
146    }
147
148    if (0 != prog_prep(&prog))
149    {
150        LSQ_ERROR("could not prep");
151        exit(EXIT_FAILURE);
152    }
153
154    LSQ_DEBUG("entering event loop");
155
156    s = prog_run(&prog);
157    prog_cleanup(&prog);
158
159    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
160}
161