duck_client.c revision b1a7c3f9
1/* Copyright (c) 2017 - 2020 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    int s;
72
73    /* We only write one request */
74    s = lsquic_conn_want_datagram_write(conn, 0);
75    assert(s == 1); /* Old value was "yes, we want to write a datagram" */
76
77    if (sz >= sizeof(REQUEST) - 1)
78    {
79        LSQ_INFO("wrote `%s' in request", REQUEST);
80        memcpy(buf, REQUEST, sizeof(REQUEST) - 1);
81        return sizeof(REQUEST) - 1;
82    }
83    else
84        return -1;
85}
86
87
88static void
89duck_client_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
90{
91    if (bufsz == sizeof(RESPONSE) - 1
92            && 0 == memcmp(buf, RESPONSE, sizeof(RESPONSE) - 1))
93    {
94        LSQ_DEBUG("received the expected `%s' response", RESPONSE);
95        lsquic_conn_close(conn);
96    }
97    else
98    {
99        LSQ_NOTICE("unexpected request received, abort connection");
100        lsquic_conn_abort(conn);
101    }
102}
103
104
105const struct lsquic_stream_if duck_client_stream_if = {
106    .on_new_conn            = duck_client_on_new_conn,
107    .on_hsk_done            = duck_client_on_hsk_done,
108    .on_conn_closed         = duck_client_on_conn_closed,
109    .on_dg_write            = duck_client_on_dg_write,
110    .on_datagram            = duck_client_on_datagram,
111};
112
113
114static void
115usage (const char *prog)
116{
117    const char *const slash = strrchr(prog, '/');
118    if (slash)
119        prog = slash + 1;
120    LSQ_NOTICE(
121"Usage: %s [opts]\n"
122"\n"
123"Options:\n"
124            , prog);
125}
126
127
128int
129main (int argc, char **argv)
130{
131    int opt, s;
132    struct sport_head sports;
133    struct prog prog;
134
135    TAILQ_INIT(&sports);
136    prog_init(&prog, 0, &sports, &duck_client_stream_if, &prog);
137    prog.prog_settings.es_datagrams = 1;
138    prog.prog_settings.es_init_max_data = 0;
139    prog.prog_settings.es_init_max_streams_bidi = 0;
140    prog.prog_settings.es_init_max_streams_uni = 0;
141    prog.prog_settings.es_max_streams_in = 0;
142    prog.prog_api.ea_alpn = "siduck-00";
143
144    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
145    {
146        switch (opt) {
147        case 'h':
148            usage(argv[0]);
149            prog_print_common_options(&prog, stdout);
150            exit(0);
151        default:
152            if (0 != prog_set_opt(&prog, opt, optarg))
153                exit(1);
154        }
155    }
156
157#ifndef WIN32
158    int flags = fcntl(STDIN_FILENO, F_GETFL);
159    flags |= O_NONBLOCK;
160    if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
161    {
162        perror("fcntl");
163        exit(1);
164    }
165#else
166    {
167        u_long on = 1;
168        ioctlsocket(STDIN_FILENO, FIONBIO, &on);
169    }
170#endif
171
172    if (0 != prog_prep(&prog))
173    {
174        LSQ_ERROR("could not prep");
175        exit(EXIT_FAILURE);
176    }
177    if (0 != prog_connect(&prog, NULL, 0))
178    {
179        LSQ_ERROR("could not connect");
180        exit(EXIT_FAILURE);
181    }
182
183    LSQ_DEBUG("entering event loop");
184
185    s = prog_run(&prog);
186    prog_cleanup(&prog);
187
188    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
189}
190