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