prog.c revision 4429f8ea
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 (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