prog.c revision 7483dee0
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   = get_ssl_ctx;
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.  The default is 1370\n"
144"                 bytes for IPv4 socket and 1350 bytes for IPv6 socket\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        return 0;
361#else
362        LSQ_ERROR("key logging is not supported on Windows");
363        return -1;
364#endif
365    default:
366        return 1;
367    }
368}
369
370
371struct event_base *
372prog_eb (struct prog *prog)
373{
374    return prog->prog_eb;
375}
376
377
378int
379prog_connect (struct prog *prog, unsigned char *sess_resume, size_t sess_resume_len)
380{
381    struct service_port *sport;
382
383    sport = TAILQ_FIRST(prog->prog_sports);
384    if (NULL == lsquic_engine_connect(prog->prog_engine, N_LSQVER,
385                    (struct sockaddr *) &sport->sp_local_addr,
386                    (struct sockaddr *) &sport->sas, sport, NULL,
387                    prog->prog_hostname ? prog->prog_hostname
388                    /* SNI is required for HTTP */
389                  : prog->prog_engine_flags & LSENG_HTTP ? sport->host
390                  : NULL,
391                    prog->prog_max_packet_size, sess_resume, sess_resume_len,
392                    sport->sp_token_buf, sport->sp_token_sz))
393        return -1;
394
395    prog_process_conns(prog);
396    return 0;
397}
398
399
400static int
401prog_init_client (struct prog *prog)
402{
403    struct service_port *sport;
404
405    sport = TAILQ_FIRST(prog->prog_sports);
406    if (0 != sport_init_client(sport, prog->prog_engine, prog->prog_eb))
407        return -1;
408
409    return 0;
410}
411
412
413static SSL_CTX *
414get_ssl_ctx (void *peer_ctx)
415{
416    const struct service_port *const sport = peer_ctx;
417    return sport->sp_prog->prog_ssl_ctx;
418}
419
420
421static int
422prog_init_server (struct prog *prog)
423{
424    struct service_port *sport;
425    unsigned char ticket_keys[48];
426
427    prog->prog_ssl_ctx = SSL_CTX_new(TLS_method());
428    if (prog->prog_ssl_ctx)
429    {
430        SSL_CTX_set_min_proto_version(prog->prog_ssl_ctx, TLS1_3_VERSION);
431        SSL_CTX_set_max_proto_version(prog->prog_ssl_ctx, TLS1_3_VERSION);
432        SSL_CTX_set_default_verify_paths(prog->prog_ssl_ctx);
433
434        /* This is obviously test code: the key is just an array of NUL bytes */
435        memset(ticket_keys, 0, sizeof(ticket_keys));
436        if (1 != SSL_CTX_set_tlsext_ticket_keys(prog->prog_ssl_ctx,
437                                            ticket_keys, sizeof(ticket_keys)))
438        {
439            LSQ_ERROR("SSL_CTX_set_tlsext_ticket_keys failed");
440            return -1;
441        }
442    }
443    else
444        LSQ_WARN("cannot create SSL context");
445
446    TAILQ_FOREACH(sport, prog->prog_sports, next_sport)
447        if (0 != sport_init_server(sport, prog->prog_engine, prog->prog_eb))
448            return -1;
449
450    return 0;
451}
452
453
454void
455prog_process_conns (struct prog *prog)
456{
457    int diff;
458    struct timeval timeout;
459
460    lsquic_engine_process_conns(prog->prog_engine);
461
462    if (lsquic_engine_earliest_adv_tick(prog->prog_engine, &diff))
463    {
464        if (diff < 0
465                || (unsigned) diff < prog->prog_settings.es_clock_granularity)
466        {
467            timeout.tv_sec  = 0;
468            timeout.tv_usec = prog->prog_settings.es_clock_granularity;
469        }
470        else
471        {
472            timeout.tv_sec = (unsigned) diff / 1000000;
473            timeout.tv_usec = (unsigned) diff % 1000000;
474        }
475
476        if (!prog_is_stopped())
477            event_add(prog->prog_timer, &timeout);
478    }
479}
480
481
482static void
483prog_timer_handler (int fd, short what, void *arg)
484{
485    struct prog *const prog = arg;
486    if (!prog_is_stopped())
487        prog_process_conns(prog);
488}
489
490
491static void
492prog_usr1_handler (int fd, short what, void *arg)
493{
494    LSQ_NOTICE("Got SIGUSR1, stopping engine");
495    prog_stop(arg);
496}
497
498
499static void
500prog_usr2_handler (int fd, short what, void *arg)
501{
502    struct prog *const prog = arg;
503
504    LSQ_NOTICE("Got SIGUSR2, cool down engine");
505    prog->prog_flags |= PROG_FLAG_COOLDOWN;
506    lsquic_engine_cooldown(prog->prog_engine);
507    prog_process_conns(prog);
508}
509
510
511int
512prog_run (struct prog *prog)
513{
514#ifndef WIN32
515    prog->prog_usr1 = evsignal_new(prog->prog_eb, SIGUSR1,
516                                                    prog_usr1_handler, prog);
517    evsignal_add(prog->prog_usr1, NULL);
518    prog->prog_usr2 = evsignal_new(prog->prog_eb, SIGUSR2,
519                                                    prog_usr2_handler, prog);
520    evsignal_add(prog->prog_usr2, NULL);
521#endif
522
523    event_base_loop(prog->prog_eb, 0);
524
525    return 0;
526}
527
528
529void
530prog_cleanup (struct prog *prog)
531{
532    lsquic_engine_destroy(prog->prog_engine);
533    event_base_free(prog->prog_eb);
534    if (!prog->prog_use_stock_pmi)
535        pba_cleanup(&prog->prog_pba);
536    if (prog->prog_ssl_ctx)
537        SSL_CTX_free(prog->prog_ssl_ctx);
538    if (prog->prog_certs)
539        delete_certs(prog->prog_certs);
540    lsquic_global_cleanup();
541}
542
543
544void
545prog_stop (struct prog *prog)
546{
547    struct service_port *sport;
548
549    prog_stopped = 1;
550
551    while ((sport = TAILQ_FIRST(prog->prog_sports)))
552    {
553        TAILQ_REMOVE(prog->prog_sports, sport, next_sport);
554        sport_destroy(sport);
555    }
556
557    if (prog->prog_timer)
558    {
559        event_del(prog->prog_timer);
560        event_free(prog->prog_timer);
561        prog->prog_timer = NULL;
562    }
563    if (prog->prog_usr1)
564    {
565        event_del(prog->prog_usr1);
566        event_free(prog->prog_usr1);
567        prog->prog_usr1 = NULL;
568    }
569    if (prog->prog_usr2)
570    {
571        event_del(prog->prog_usr2);
572        event_free(prog->prog_usr2);
573        prog->prog_usr2 = NULL;
574    }
575}
576
577
578static void *
579keylog_open (void *ctx, lsquic_conn_t *conn)
580{
581    const struct prog *const prog = ctx;
582    const lsquic_cid_t *cid;
583    FILE *fh;
584    int sz;
585    char id_str[MAX_CID_LEN * 2 + 1];
586    char path[PATH_MAX];
587
588    cid = lsquic_conn_id(conn);
589    lsquic_hexstr(cid->idbuf, cid->len, id_str, sizeof(id_str));
590    sz = snprintf(path, sizeof(path), "%s/%s.keys", prog->prog_keylog_dir,
591                                                                    id_str);
592    if ((size_t) sz >= sizeof(path))
593    {
594        LSQ_WARN("%s: file too long", __func__);
595        return NULL;
596    }
597    fh = fopen(path, "w");
598    if (!fh)
599        LSQ_WARN("could not open %s for writing: %s", path, strerror(errno));
600    return fh;
601}
602
603
604static void
605keylog_log_line (void *handle, const char *line)
606{
607    size_t len;
608
609    len = strlen(line);
610    if (len < sizeof("QUIC_") - 1 || strncmp(line, "QUIC_", 5))
611        fputs("QUIC_", handle);
612    fputs(line, handle);
613    fputs("\n", handle);
614    fflush(handle);
615}
616
617
618static void
619keylog_close (void *handle)
620{
621    fclose(handle);
622}
623
624
625static const struct lsquic_keylog_if keylog_if =
626{
627    .kli_open       = keylog_open,
628    .kli_log_line   = keylog_log_line,
629    .kli_close      = keylog_close,
630};
631
632
633static struct ssl_ctx_st *
634no_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED, const char *sni)
635{
636    return NULL;
637}
638
639
640int
641prog_prep (struct prog *prog)
642{
643    int s;
644    char err_buf[100];
645
646    if (prog->prog_keylog_dir)
647    {
648        prog->prog_api.ea_keylog_if = &keylog_if;
649        prog->prog_api.ea_keylog_ctx = prog;
650    }
651
652    if (0 != lsquic_engine_check_settings(prog->prog_api.ea_settings,
653                        prog->prog_engine_flags, err_buf, sizeof(err_buf)))
654    {
655        LSQ_ERROR("Error in settings: %s", err_buf);
656        return -1;
657    }
658
659    if (!prog->prog_use_stock_pmi)
660        pba_init(&prog->prog_pba, prog->prog_packout_max);
661    else
662    {
663        prog->prog_api.ea_pmi = NULL;
664        prog->prog_api.ea_pmi_ctx = NULL;
665    }
666
667    if (TAILQ_EMPTY(prog->prog_sports))
668    {
669        if (prog->prog_hostname)
670            s = prog_add_sport(prog, prog->prog_hostname);
671        else
672            s = prog_add_sport(prog, "0.0.0.0:12345");
673        if (0 != s)
674            return -1;
675    }
676
677    if (prog->prog_certs)
678    {
679    prog->prog_api.ea_lookup_cert = lookup_cert;
680    prog->prog_api.ea_cert_lu_ctx = prog->prog_certs;
681    }
682    else
683    {
684        if (prog->prog_engine_flags & LSENG_SERVER)
685            LSQ_WARN("Not a single service specified.  Use -c option.");
686        prog->prog_api.ea_lookup_cert = no_cert;
687    }
688
689    prog->prog_eb = event_base_new();
690    prog->prog_engine = lsquic_engine_new(prog->prog_engine_flags,
691                                                            &prog->prog_api);
692    if (!prog->prog_engine)
693        return -1;
694
695    prog->prog_timer = event_new(prog->prog_eb, -1, 0,
696                                        prog_timer_handler, prog);
697
698    if (prog->prog_engine_flags & LSENG_SERVER)
699        s = prog_init_server(prog);
700    else
701        s = prog_init_client(prog);
702
703    if (s != 0)
704        return -1;
705
706    return 0;
707}
708
709
710int
711prog_is_stopped (void)
712{
713    return prog_stopped != 0;
714}
715
716
717static void
718send_unsent (evutil_socket_t fd, short what, void *arg)
719{
720    struct prog *const prog = arg;
721    assert(prog->prog_send);
722    event_del(prog->prog_send);
723    event_free(prog->prog_send);
724    prog->prog_send = NULL;
725    LSQ_DEBUG("on_write event fires");
726    lsquic_engine_send_unsent_packets(prog->prog_engine);
727}
728
729
730void
731prog_sport_cant_send (struct prog *prog, int fd)
732{
733    assert(!prog->prog_send);
734    LSQ_DEBUG("cannot send: register on_write event");
735    prog->prog_send = event_new(prog->prog_eb, fd, EV_WRITE, send_unsent, prog);
736    event_add(prog->prog_send, NULL);
737}
738