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