prog.c revision 9a690580
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <assert.h>
3#ifndef WIN32
4#include <arpa/inet.h>
5#include <netinet/in.h>
6#include <signal.h>
7#endif
8#include <errno.h>
9#include <limits.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <sys/queue.h>
16#ifndef WIN32
17#include <unistd.h>
18#else
19#include <getopt.h>
20#pragma warning(disable:4028)
21#endif// WIN32
22
23#include <event2/event.h>
24
25#include <lsquic.h>
26
27#include <openssl/ssl.h>
28
29#include "../src/liblsquic/lsquic_hash.h"
30#include "../src/liblsquic/lsquic_int_types.h"
31#include "../src/liblsquic/lsquic_util.h"
32#include "../src/liblsquic/lsquic_logger.h"
33
34#include "test_config.h"
35#include "test_cert.h"
36#include "test_common.h"
37#include "prog.h"
38
39static int prog_stopped;
40
41static SSL_CTX * get_ssl_ctx (void *);
42
43static const struct lsquic_packout_mem_if pmi = {
44    .pmi_allocate = pba_allocate,
45    .pmi_release  = pba_release,
46    .pmi_return   = pba_release,
47};
48
49
50void
51prog_init (struct prog *prog, unsigned flags,
52           struct sport_head *sports,
53           const struct lsquic_stream_if *stream_if, void *stream_if_ctx)
54{
55    /* prog-specific initialization: */
56    memset(prog, 0, sizeof(*prog));
57    prog->prog_engine_flags = flags;
58    prog->prog_sports       = sports;
59    lsquic_engine_init_settings(&prog->prog_settings, flags);
60#if ECN_SUPPORTED
61    prog->prog_settings.es_ecn      = LSQUIC_DF_ECN;
62#else
63    prog->prog_settings.es_ecn      = 0;
64#endif
65
66    prog->prog_api.ea_settings      = &prog->prog_settings;
67    prog->prog_api.ea_stream_if     = stream_if;
68    prog->prog_api.ea_stream_if_ctx = stream_if_ctx;
69    prog->prog_api.ea_packets_out   = sport_packets_out;
70    prog->prog_api.ea_packets_out_ctx
71                                    = prog;
72    prog->prog_api.ea_pmi           = &pmi;
73    prog->prog_api.ea_pmi_ctx       = &prog->prog_pba;
74    prog->prog_api.ea_get_ssl_ctx   = get_ssl_ctx;
75#if LSQUIC_PREFERRED_ADDR
76    if (getenv("LSQUIC_PREFERRED_ADDR4") || getenv("LSQUIC_PREFERRED_ADDR6"))
77        prog->prog_flags |= PROG_SEARCH_ADDRS;
78#endif
79
80    /* Non prog-specific initialization: */
81    lsquic_global_init(flags & LSENG_SERVER ? LSQUIC_GLOBAL_SERVER :
82                                                    LSQUIC_GLOBAL_CLIENT);
83    lsquic_log_to_fstream(stderr, LLTS_HHMMSSMS);
84    lsquic_logger_lopt("=notice");
85}
86
87
88static int
89prog_add_sport (struct prog *prog, const char *arg)
90{
91    struct service_port *sport;
92    sport = sport_new(arg, prog);
93    if (!sport)
94        return -1;
95    /* Default settings: */
96    sport->sp_flags = prog->prog_dummy_sport.sp_flags;
97    sport->sp_sndbuf = prog->prog_dummy_sport.sp_sndbuf;
98    sport->sp_rcvbuf = prog->prog_dummy_sport.sp_rcvbuf;
99    TAILQ_INSERT_TAIL(prog->prog_sports, sport, next_sport);
100    return 0;
101}
102
103
104void
105prog_print_common_options (const struct prog *prog, FILE *out)
106{
107    fprintf(out,
108#if HAVE_REGEX
109"   -s SVCPORT  Service port.  Takes on the form of host:port, host,\n"
110"                 or port.  If host is not an IPv4 or IPv6 address, it is\n"
111"                 resolved.  If host is not set, the value of SNI is\n"
112"                 used (see the -H flag).  If port is not set, the default\n"
113"                 is 443.\n"
114#else
115"   -s SVCPORT  Service port.  Takes on the form of host:port or host.\n"
116"                 If host is not an IPv4 or IPv6 address, it is resolved.\n"
117"                 If port is not set, the default is 443.\n"
118#endif
119"                 Examples:\n"
120"                     127.0.0.1:12345\n"
121"                     ::1:443\n"
122"                     example.com\n"
123"                     example.com:8443\n"
124#if HAVE_REGEX
125"                     8443\n"
126#endif
127"                 If no -s option is given, 0.0.0.0:12345 address\n"
128"                 is used.\n"
129#if LSQUIC_DONTFRAG_SUPPORTED
130"   -D          Do not set `do not fragment' flag on outgoing UDP packets\n"
131#endif
132"   -z BYTES    Maximum size of outgoing UDP packets.  The default is 1370\n"
133"                 bytes for IPv4 socket and 1350 bytes for IPv6 socket\n"
134"   -L LEVEL    Log level for all modules.  Possible values are `debug',\n"
135"                 `info', `notice', `warn', `error', `alert', `emerg',\n"
136"                 and `crit'.\n"
137"   -l LEVELS   Log levels for modules, e.g.\n"
138"                 -l event=info,engine=debug\n"
139"               Can be specified more than once.\n"
140"   -m MAX      Maximum number of outgoing packet buffers that can be\n"
141"                 assigned at any one time.  By default, there is no max.\n"
142"   -y style    Timestamp style used in log messages.  The following styles\n"
143"                 are supported:\n"
144"                   0   No timestamp\n"
145"                   1   Millisecond time (this is the default).\n"
146"                         Example: 11:04:05.196\n"
147"                   2   Full date and millisecond time.\n"
148"                         Example: 2017-03-21 13:43:46.671\n"
149"                   3   Chrome-like timestamp: date/time.microseconds.\n"
150"                         Example: 1223/104613.946956\n"
151"                   4   Microsecond time.\n"
152"                         Example: 11:04:05.196308\n"
153"                   5   Full date and microsecond time.\n"
154"                         Example: 2017-03-21 13:43:46.671345\n"
155"   -S opt=val  Socket options.  Supported options:\n"
156"                   sndbuf=12345    # Sets SO_SNDBUF\n"
157"                   rcvbuf=12345    # Sets SO_RCVBUF\n"
158"   -W          Use stock PMI (malloc & free)\n"
159    );
160
161#if HAVE_SENDMMSG
162    fprintf(out,
163"   -g          Use sendmmsg() to send packets.\n"
164    );
165#endif
166#if HAVE_RECVMMSG
167    fprintf(out,
168"   -j          Use recvmmsg() to receive packets.\n"
169    );
170#endif
171
172    if (prog->prog_engine_flags & LSENG_SERVER)
173        fprintf(out,
174"   -c CERTSPEC Service specification.  The specification is three values\n"
175"                 separated by commas.  The values are:\n"
176"                   * Domain name\n"
177"                   * File containing cert in PEM format\n"
178"                   * File containing private key in DER or PEM format\n"
179"                 Example:\n"
180"                   -c www.example.com,/tmp/cert.pem,/tmp/key.pkcs8\n"
181        );
182    else
183    {
184        if (prog->prog_engine_flags & LSENG_HTTP)
185            fprintf(out,
186"   -H host     Value of `host' HTTP header.  This is also used as SNI\n"
187"                 in Client Hello.  This option is used to override the\n"
188"                 `host' part of the address specified using -s flag.\n"
189            );
190        else
191            fprintf(out,
192"   -H host     Value of SNI in CHLO.\n"
193            );
194    }
195
196#ifndef WIN32
197    fprintf(out,
198"   -G dir      SSL keys will be logged to files in this directory.\n"
199    );
200#endif
201
202
203    fprintf(out,
204"   -k          Connect UDP socket.  Only meant to be used with clients\n"
205"                 to pick up ICMP errors.\n"
206"   -i USECS    Clock granularity in microseconds.  Defaults to %u.\n",
207        LSQUIC_DF_CLOCK_GRANULARITY
208    );
209    fprintf(out,
210"   -h          Print this help screen and exit\n"
211    );
212}
213
214
215int
216prog_set_opt (struct prog *prog, int opt, const char *arg)
217{
218    struct stat st;
219    int s;
220
221    switch (opt)
222    {
223#if LSQUIC_DONTFRAG_SUPPORTED
224    case 'D':
225        {
226            struct service_port *sport = TAILQ_LAST(prog->prog_sports, sport_head);
227            if (!sport)
228                sport = &prog->prog_dummy_sport;
229            sport->sp_flags |= SPORT_FRAGMENT_OK;
230        }
231        return 0;
232#endif
233#if HAVE_SENDMMSG
234    case 'g':
235        prog->prog_use_sendmmsg = 1;
236        return 0;
237#endif
238#if HAVE_RECVMMSG
239    case 'j':
240        prog->prog_use_recvmmsg = 1;
241        return 0;
242#endif
243    case 'm':
244        prog->prog_packout_max = atoi(arg);
245        return 0;
246    case 'z':
247        prog->prog_max_packet_size = atoi(arg);
248        return 0;
249    case 'W':
250        prog->prog_use_stock_pmi = 1;
251        return 0;
252    case 'c':
253        if (prog->prog_engine_flags & LSENG_SERVER)
254        {
255            if (!prog->prog_certs)
256                prog->prog_certs = lsquic_hash_create();
257            return load_cert(prog->prog_certs, arg);
258        }
259        else
260            return -1;
261    case 'H':
262        if (prog->prog_engine_flags & LSENG_SERVER)
263            return -1;
264        prog->prog_hostname = arg;
265        return 0;
266    case 'y':
267        lsquic_log_to_fstream(stderr, atoi(arg));
268        return 0;
269    case 'L':
270        return lsquic_set_log_level(arg);
271    case 'l':
272        return lsquic_logger_lopt(arg);
273    case 'o':
274        return set_engine_option(&prog->prog_settings,
275                                            &prog->prog_version_cleared, arg);
276    case 'i':
277        prog->prog_settings.es_clock_granularity = atoi(arg);
278        return 0;
279    case 's':
280        if (0 == (prog->prog_engine_flags & LSENG_SERVER) &&
281                                            !TAILQ_EMPTY(prog->prog_sports))
282            return -1;
283        return prog_add_sport(prog, arg);
284    case 'S':
285        {
286            struct service_port *sport = TAILQ_LAST(prog->prog_sports, sport_head);
287            if (!sport)
288                sport = &prog->prog_dummy_sport;
289            char *const name = strdup(optarg);
290            char *val = strchr(name, '=');
291            if (!val)
292            {
293                free(name);
294                return -1;
295            }
296            *val = '\0';
297            ++val;
298            if (0 == strcasecmp(name, "sndbuf"))
299            {
300                sport->sp_flags |= SPORT_SET_SNDBUF;
301                sport->sp_sndbuf = atoi(val);
302                free(name);
303                return 0;
304            }
305            else if (0 == strcasecmp(name, "rcvbuf"))
306            {
307                sport->sp_flags |= SPORT_SET_RCVBUF;
308                sport->sp_rcvbuf = atoi(val);
309                free(name);
310                return 0;
311            }
312            else
313            {
314                free(name);
315                return -1;
316            }
317        }
318    case 'k':
319        {
320            struct service_port *sport = TAILQ_LAST(prog->prog_sports, sport_head);
321            if (!sport)
322                sport = &prog->prog_dummy_sport;
323            sport->sp_flags |= SPORT_CONNECT;
324        }
325        return 0;
326    case 'G':
327#ifndef WIN32
328        if (0 == stat(optarg, &st))
329        {
330            if (!S_ISDIR(st.st_mode))
331            {
332                LSQ_ERROR("%s is not a directory", optarg);
333                return -1;
334            }
335        }
336        else
337        {
338            s = mkdir(optarg, 0700);
339            if (s != 0)
340            {
341                LSQ_ERROR("cannot create directory %s: %s", optarg,
342                                                        strerror(errno));
343                return -1;
344            }
345        }
346        prog->prog_keylog_dir = optarg;
347        return 0;
348#else
349        LSQ_ERROR("key logging is not supported on Windows");
350        return -1;
351#endif
352    default:
353        return 1;
354    }
355}
356
357
358struct event_base *
359prog_eb (struct prog *prog)
360{
361    return prog->prog_eb;
362}
363
364
365int
366prog_connect (struct prog *prog, unsigned char *zero_rtt, size_t zero_rtt_len)
367{
368    struct service_port *sport;
369
370    sport = TAILQ_FIRST(prog->prog_sports);
371    if (NULL == lsquic_engine_connect(prog->prog_engine, N_LSQVER,
372                    (struct sockaddr *) &sport->sp_local_addr,
373                    (struct sockaddr *) &sport->sas, sport, NULL,
374                    prog->prog_hostname ? prog->prog_hostname
375                    /* SNI is required for HTTP */
376                  : prog->prog_engine_flags & LSENG_HTTP ? sport->host
377                  : NULL,
378                    prog->prog_max_packet_size, zero_rtt, zero_rtt_len,
379                    sport->sp_token_buf, sport->sp_token_sz))
380        return -1;
381
382    prog_process_conns(prog);
383    return 0;
384}
385
386
387static int
388prog_init_client (struct prog *prog)
389{
390    struct service_port *sport;
391
392    sport = TAILQ_FIRST(prog->prog_sports);
393    if (0 != sport_init_client(sport, prog->prog_engine, prog->prog_eb))
394        return -1;
395
396    return 0;
397}
398
399
400static SSL_CTX *
401get_ssl_ctx (void *peer_ctx)
402{
403    const struct service_port *const sport = peer_ctx;
404    return sport->sp_prog->prog_ssl_ctx;
405}
406
407
408static int
409prog_init_server (struct prog *prog)
410{
411    struct service_port *sport;
412    unsigned char ticket_keys[48];
413
414    prog->prog_ssl_ctx = SSL_CTX_new(TLS_method());
415    if (prog->prog_ssl_ctx)
416    {
417        SSL_CTX_set_min_proto_version(prog->prog_ssl_ctx, TLS1_3_VERSION);
418        SSL_CTX_set_max_proto_version(prog->prog_ssl_ctx, TLS1_3_VERSION);
419        SSL_CTX_set_default_verify_paths(prog->prog_ssl_ctx);
420
421        /* This is obviously test code: the key is just an array of NUL bytes */
422        memset(ticket_keys, 0, sizeof(ticket_keys));
423        if (1 != SSL_CTX_set_tlsext_ticket_keys(prog->prog_ssl_ctx,
424                                            ticket_keys, sizeof(ticket_keys)))
425        {
426            LSQ_ERROR("SSL_CTX_set_tlsext_ticket_keys failed");
427            return -1;
428        }
429    }
430    else
431        LSQ_WARN("cannot create SSL context");
432
433    TAILQ_FOREACH(sport, prog->prog_sports, next_sport)
434        if (0 != sport_init_server(sport, prog->prog_engine, prog->prog_eb))
435            return -1;
436
437    return 0;
438}
439
440
441void
442prog_process_conns (struct prog *prog)
443{
444    int diff;
445    struct timeval timeout;
446
447    lsquic_engine_process_conns(prog->prog_engine);
448
449    if (lsquic_engine_earliest_adv_tick(prog->prog_engine, &diff))
450    {
451        if (diff < 0
452                || (unsigned) diff < prog->prog_settings.es_clock_granularity)
453        {
454            timeout.tv_sec  = 0;
455            timeout.tv_usec = prog->prog_settings.es_clock_granularity;
456        }
457        else
458        {
459            timeout.tv_sec = (unsigned) diff / 1000000;
460            timeout.tv_usec = (unsigned) diff % 1000000;
461        }
462
463        if (!prog_is_stopped())
464            event_add(prog->prog_timer, &timeout);
465    }
466}
467
468
469static void
470prog_timer_handler (int fd, short what, void *arg)
471{
472    struct prog *const prog = arg;
473    if (!prog_is_stopped())
474        prog_process_conns(prog);
475}
476
477
478static void
479prog_usr1_handler (int fd, short what, void *arg)
480{
481    LSQ_NOTICE("Got SIGUSR1, stopping engine");
482    prog_stop(arg);
483}
484
485
486static void
487prog_usr2_handler (int fd, short what, void *arg)
488{
489    struct prog *const prog = arg;
490
491    LSQ_NOTICE("Got SIGUSR2, cool down engine");
492    prog->prog_flags |= PROG_FLAG_COOLDOWN;
493    lsquic_engine_cooldown(prog->prog_engine);
494    prog_process_conns(prog);
495}
496
497
498int
499prog_run (struct prog *prog)
500{
501#ifndef WIN32
502    prog->prog_usr1 = evsignal_new(prog->prog_eb, SIGUSR1,
503                                                    prog_usr1_handler, prog);
504    evsignal_add(prog->prog_usr1, NULL);
505    prog->prog_usr2 = evsignal_new(prog->prog_eb, SIGUSR2,
506                                                    prog_usr2_handler, prog);
507    evsignal_add(prog->prog_usr2, NULL);
508#endif
509
510    event_base_loop(prog->prog_eb, 0);
511
512    return 0;
513}
514
515
516void
517prog_cleanup (struct prog *prog)
518{
519    lsquic_engine_destroy(prog->prog_engine);
520    event_base_free(prog->prog_eb);
521    if (!prog->prog_use_stock_pmi)
522        pba_cleanup(&prog->prog_pba);
523    if (prog->prog_ssl_ctx)
524        SSL_CTX_free(prog->prog_ssl_ctx);
525    if (prog->prog_certs)
526        delete_certs(prog->prog_certs);
527    lsquic_global_cleanup();
528}
529
530
531void
532prog_stop (struct prog *prog)
533{
534    struct service_port *sport;
535
536    prog_stopped = 1;
537
538    while ((sport = TAILQ_FIRST(prog->prog_sports)))
539    {
540        TAILQ_REMOVE(prog->prog_sports, sport, next_sport);
541        sport_destroy(sport);
542    }
543
544    if (prog->prog_timer)
545    {
546        event_del(prog->prog_timer);
547        event_free(prog->prog_timer);
548        prog->prog_timer = NULL;
549    }
550    if (prog->prog_usr1)
551    {
552        event_del(prog->prog_usr1);
553        event_free(prog->prog_usr1);
554        prog->prog_usr1 = NULL;
555    }
556    if (prog->prog_usr2)
557    {
558        event_del(prog->prog_usr2);
559        event_free(prog->prog_usr2);
560        prog->prog_usr2 = NULL;
561    }
562}
563
564
565static void *
566keylog_open (void *ctx, lsquic_conn_t *conn)
567{
568    const struct prog *const prog = ctx;
569    const lsquic_cid_t *cid;
570    FILE *fh;
571    int sz;
572    char id_str[MAX_CID_LEN * 2 + 1];
573    char path[PATH_MAX];
574
575    cid = lsquic_conn_id(conn);
576    lsquic_hexstr(cid->idbuf, cid->len, id_str, sizeof(id_str));
577    sz = snprintf(path, sizeof(path), "%s/%s.keys", prog->prog_keylog_dir,
578                                                                    id_str);
579    if ((size_t) sz >= sizeof(path))
580    {
581        LSQ_WARN("%s: file too long", __func__);
582        return NULL;
583    }
584    fh = fopen(path, "w");
585    if (!fh)
586        LSQ_WARN("could not open %s for writing: %s", path, strerror(errno));
587    return fh;
588}
589
590
591static void
592keylog_log_line (void *handle, const char *line)
593{
594    size_t len;
595
596    len = strlen(line);
597    if (len < sizeof("QUIC_") - 1 || strncmp(line, "QUIC_", 5))
598        fputs("QUIC_", handle);
599    fputs(line, handle);
600    fputs("\n", handle);
601    fflush(handle);
602}
603
604
605static void
606keylog_close (void *handle)
607{
608    fclose(handle);
609}
610
611
612static const struct lsquic_keylog_if keylog_if =
613{
614    .kli_open       = keylog_open,
615    .kli_log_line   = keylog_log_line,
616    .kli_close      = keylog_close,
617};
618
619
620static struct ssl_ctx_st *
621no_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED, const char *sni)
622{
623    return NULL;
624}
625
626
627int
628prog_prep (struct prog *prog)
629{
630    int s;
631    char err_buf[100];
632
633    if (prog->prog_keylog_dir)
634    {
635        prog->prog_api.ea_keylog_if = &keylog_if;
636        prog->prog_api.ea_keylog_ctx = prog;
637    }
638
639    if (0 != lsquic_engine_check_settings(prog->prog_api.ea_settings,
640                        prog->prog_engine_flags, err_buf, sizeof(err_buf)))
641    {
642        LSQ_ERROR("Error in settings: %s", err_buf);
643        return -1;
644    }
645
646    if (!prog->prog_use_stock_pmi)
647        pba_init(&prog->prog_pba, prog->prog_packout_max);
648    else
649    {
650        prog->prog_api.ea_pmi = NULL;
651        prog->prog_api.ea_pmi_ctx = NULL;
652    }
653
654    if (TAILQ_EMPTY(prog->prog_sports))
655    {
656        if (prog->prog_hostname)
657            s = prog_add_sport(prog, prog->prog_hostname);
658        else
659            s = prog_add_sport(prog, "0.0.0.0:12345");
660        if (0 != s)
661            return -1;
662    }
663
664    if (prog->prog_certs)
665    {
666    prog->prog_api.ea_lookup_cert = lookup_cert;
667    prog->prog_api.ea_cert_lu_ctx = prog->prog_certs;
668    }
669    else
670    {
671        if (prog->prog_engine_flags & LSENG_SERVER)
672            LSQ_WARN("Not a single service specified.  Use -c option.");
673        prog->prog_api.ea_lookup_cert = no_cert;
674    }
675
676    prog->prog_eb = event_base_new();
677    prog->prog_engine = lsquic_engine_new(prog->prog_engine_flags,
678                                                            &prog->prog_api);
679    if (!prog->prog_engine)
680        return -1;
681
682    prog->prog_timer = event_new(prog->prog_eb, -1, 0,
683                                        prog_timer_handler, prog);
684
685    if (prog->prog_engine_flags & LSENG_SERVER)
686        s = prog_init_server(prog);
687    else
688        s = prog_init_client(prog);
689
690    if (s != 0)
691        return -1;
692
693    return 0;
694}
695
696
697int
698prog_is_stopped (void)
699{
700    return prog_stopped != 0;
701}
702
703
704static void
705send_unsent (evutil_socket_t fd, short what, void *arg)
706{
707    struct prog *const prog = arg;
708    assert(prog->prog_send);
709    event_del(prog->prog_send);
710    event_free(prog->prog_send);
711    prog->prog_send = NULL;
712    LSQ_DEBUG("on_write event fires");
713    lsquic_engine_send_unsent_packets(prog->prog_engine);
714}
715
716
717void
718prog_sport_cant_send (struct prog *prog, int fd)
719{
720    assert(!prog->prog_send);
721    LSQ_DEBUG("cannot send: register on_write event");
722    prog->prog_send = event_new(prog->prog_eb, fd, EV_WRITE, send_unsent, prog);
723    event_add(prog->prog_send, NULL);
724}
725