duck_client.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * duck_client.c -- The siduck client.  See
4 *      https://tools.ietf.org/html/draft-pardue-quic-siduck-00
5 */
6
7#include <assert.h>
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/queue.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15
16#ifndef WIN32
17#include <fcntl.h>
18#include <unistd.h>
19#define Read read
20#else
21#include "vc_compat.h"
22#include "getopt.h"
23#include <io.h>
24#define Read _read
25#define STDIN_FILENO 0
26#endif
27
28#include <event2/event.h>
29
30#include "lsquic.h"
31#include "test_common.h"
32#include "prog.h"
33
34#include "../src/liblsquic/lsquic_logger.h"
35
36/* Expected request and response of the siduck protocol */
37#define REQUEST "quack"
38#define RESPONSE "quack-ack"
39
40static lsquic_conn_ctx_t *
41duck_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
42{
43    LSQ_NOTICE("created a new connection");
44    return stream_if_ctx;
45}
46
47
48static void
49duck_client_on_hsk_done (lsquic_conn_t *conn, enum lsquic_hsk_status s)
50{
51    if (s == LSQ_HSK_OK || s == LSQ_HSK_RESUMED_OK)
52    {
53        if (lsquic_conn_want_datagram_write(conn, 1) < 0)
54            LSQ_ERROR("want_datagram_write failed");
55    }
56}
57
58
59static void
60duck_client_on_conn_closed (lsquic_conn_t *conn)
61{
62    lsquic_conn_ctx_t *ctx = lsquic_conn_get_ctx(conn);
63    LSQ_NOTICE("Connection closed, stop client");
64    prog_stop((struct prog *) ctx);
65}
66
67
68static ssize_t
69duck_client_on_dg_write (lsquic_conn_t *conn, void *buf, size_t sz)
70{
71    /* We only write one request */
72#ifndef NDEBUG
73    int s =
74#endif
75    lsquic_conn_want_datagram_write(conn, 0);
76    assert(s == 1); /* Old value was "yes, we want to write a datagram" */
77
78    if (sz >= sizeof(REQUEST) - 1)
79    {
80        LSQ_INFO("wrote `%s' in request", REQUEST);
81        memcpy(buf, REQUEST, sizeof(REQUEST) - 1);
82        return sizeof(REQUEST) - 1;
83    }
84    else
85        return -1;
86}
87
88
89static void
90duck_client_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
91{
92    if (bufsz == sizeof(RESPONSE) - 1
93            && 0 == memcmp(buf, RESPONSE, sizeof(RESPONSE) - 1))
94    {
95        LSQ_DEBUG("received the expected `%s' response", RESPONSE);
96        lsquic_conn_close(conn);
97    }
98    else
99    {
100        LSQ_NOTICE("unexpected request received, abort connection");
101        lsquic_conn_abort(conn);
102    }
103}
104
105
106const struct lsquic_stream_if duck_client_stream_if = {
107    .on_new_conn            = duck_client_on_new_conn,
108    .on_hsk_done            = duck_client_on_hsk_done,
109    .on_conn_closed         = duck_client_on_conn_closed,
110    .on_dg_write            = duck_client_on_dg_write,
111    .on_datagram            = duck_client_on_datagram,
112};
113
114
115static void
116usage (const char *prog)
117{
118    const char *const slash = strrchr(prog, '/');
119    if (slash)
120        prog = slash + 1;
121    LSQ_NOTICE(
122"Usage: %s [opts]\n"
123"\n"
124"Options:\n"
125            , prog);
126}
127
128
129int
130main (int argc, char **argv)
131{
132    int opt, s;
133    struct sport_head sports;
134    struct prog prog;
135
136    TAILQ_INIT(&sports);
137    prog_init(&prog, 0, &sports, &duck_client_stream_if, &prog);
138    prog.prog_settings.es_datagrams = 1;
139    prog.prog_settings.es_init_max_data = 0;
140    prog.prog_settings.es_init_max_streams_bidi = 0;
141    prog.prog_settings.es_init_max_streams_uni = 0;
142    prog.prog_settings.es_max_streams_in = 0;
143    prog.prog_api.ea_alpn = "siduck-00";
144
145    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
146    {
147        switch (opt) {
148        case 'h':
149            usage(argv[0]);
150            prog_print_common_options(&prog, stdout);
151            exit(0);
152        default:
153            if (0 != prog_set_opt(&prog, opt, optarg))
154                exit(1);
155        }
156    }
157
158#ifndef WIN32
159    int flags = fcntl(STDIN_FILENO, F_GETFL);
160    flags |= O_NONBLOCK;
161    if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
162    {
163        perror("fcntl");
164        exit(1);
165    }
166#else
167    {
168        u_long on = 1;
169        ioctlsocket(STDIN_FILENO, FIONBIO, &on);
170    }
171#endif
172
173    if (0 != prog_prep(&prog))
174    {
175        LSQ_ERROR("could not prep");
176        exit(EXIT_FAILURE);
177    }
178    if (0 != prog_connect(&prog, NULL, 0))
179    {
180        LSQ_ERROR("could not connect");
181        exit(EXIT_FAILURE);
182    }
183
184    LSQ_DEBUG("entering event loop");
185
186    s = prog_run(&prog);
187    prog_cleanup(&prog);
188
189    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
190}
191