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