1/* Copyright (c) 2017 - 2022 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