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