1 /* $OpenBSD: dtlstest.c,v 1.8 2021/05/03 23:44:05 inoguchi Exp $ */ 2 /* 3 * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <netinet/in.h> 19 #include <sys/socket.h> 20 21 #include <err.h> 22 #include <limits.h> 23 #include <poll.h> 24 #include <unistd.h> 25 26 #include <openssl/bio.h> 27 #include <openssl/err.h> 28 #include <openssl/ssl.h> 29 30 const char *server_ca_file; 31 const char *server_cert_file; 32 const char *server_key_file; 33 34 char dtls_cookie[32]; 35 36 int debug = 0; 37 38 static void 39 hexdump(const unsigned char *buf, size_t len) 40 { 41 size_t i; 42 43 for (i = 1; i <= len; i++) 44 fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n"); 45 46 if (len % 8) 47 fprintf(stderr, "\n"); 48 } 49 50 #define BIO_C_DROP_PACKET 1000 51 #define BIO_C_DROP_RANDOM 1001 52 53 struct bio_packet_monkey_ctx { 54 unsigned int drop_rand; 55 unsigned int drop_mask; 56 }; 57 58 static int 59 bio_packet_monkey_new(BIO *bio) 60 { 61 struct bio_packet_monkey_ctx *ctx; 62 63 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) 64 return 0; 65 66 bio->flags = 0; 67 bio->init = 1; 68 bio->num = 0; 69 bio->ptr = ctx; 70 71 return 1; 72 } 73 74 static int 75 bio_packet_monkey_free(BIO *bio) 76 { 77 struct bio_packet_monkey_ctx *ctx; 78 79 if (bio == NULL) 80 return 1; 81 82 ctx = bio->ptr; 83 free(ctx); 84 85 return 1; 86 } 87 88 static long 89 bio_packet_monkey_ctrl(BIO *bio, int cmd, long num, void *ptr) 90 { 91 struct bio_packet_monkey_ctx *ctx; 92 93 ctx = bio->ptr; 94 95 switch (cmd) { 96 case BIO_C_DROP_PACKET: 97 if (num < 1 || num > 31) 98 return 0; 99 ctx->drop_mask |= 1 << ((unsigned int)num - 1); 100 return 1; 101 102 case BIO_C_DROP_RANDOM: 103 if (num < 0 || (size_t)num > UINT_MAX) 104 return 0; 105 ctx->drop_rand = (unsigned int)num; 106 return 1; 107 } 108 109 if (bio->next_bio == NULL) 110 return 0; 111 112 return BIO_ctrl(bio->next_bio, cmd, num, ptr); 113 } 114 115 static int 116 bio_packet_monkey_read(BIO *bio, char *out, int out_len) 117 { 118 struct bio_packet_monkey_ctx *ctx = bio->ptr; 119 int ret; 120 121 if (ctx == NULL || bio->next_bio == NULL) 122 return 0; 123 124 ret = BIO_read(bio->next_bio, out, out_len); 125 126 BIO_clear_retry_flags(bio); 127 if (ret <= 0 && BIO_should_retry(bio->next_bio)) 128 BIO_set_retry_read(bio); 129 130 return ret; 131 } 132 133 static int 134 bio_packet_monkey_write(BIO *bio, const char *in, int in_len) 135 { 136 struct bio_packet_monkey_ctx *ctx = bio->ptr; 137 int drop = 0; 138 int ret; 139 140 if (ctx == NULL || bio->next_bio == NULL) 141 return 0; 142 143 if (ctx->drop_rand > 0) { 144 drop = arc4random_uniform(ctx->drop_rand) == 0; 145 } else if (ctx->drop_mask > 0) { 146 drop = ctx->drop_mask & 1; 147 ctx->drop_mask >>= 1; 148 } 149 if (debug) { 150 fprintf(stderr, "DEBUG: %s packet...\n", 151 drop ? "dropping" : "writing"); 152 if (debug > 1) 153 hexdump(in, in_len); 154 } 155 if (drop) 156 return in_len; 157 158 ret = BIO_write(bio->next_bio, in, in_len); 159 160 BIO_clear_retry_flags(bio); 161 if (ret <= 0 && BIO_should_retry(bio->next_bio)) 162 BIO_set_retry_write(bio); 163 164 return ret; 165 } 166 167 static int 168 bio_packet_monkey_puts(BIO *bio, const char *str) 169 { 170 return bio_packet_monkey_write(bio, str, strlen(str)); 171 } 172 173 static const BIO_METHOD bio_packet_monkey = { 174 .type = BIO_TYPE_BUFFER, 175 .name = "packet monkey", 176 .bread = bio_packet_monkey_read, 177 .bwrite = bio_packet_monkey_write, 178 .bputs = bio_packet_monkey_puts, 179 .ctrl = bio_packet_monkey_ctrl, 180 .create = bio_packet_monkey_new, 181 .destroy = bio_packet_monkey_free 182 }; 183 184 static const BIO_METHOD * 185 BIO_f_packet_monkey(void) 186 { 187 return &bio_packet_monkey; 188 } 189 190 static BIO * 191 BIO_new_packet_monkey(void) 192 { 193 return BIO_new(BIO_f_packet_monkey()); 194 } 195 196 static int 197 BIO_packet_monkey_drop(BIO *bio, int num) 198 { 199 return BIO_ctrl(bio, BIO_C_DROP_PACKET, num, NULL); 200 } 201 202 #if 0 203 static int 204 BIO_packet_monkey_drop_random(BIO *bio, int num) 205 { 206 return BIO_ctrl(bio, BIO_C_DROP_RANDOM, num, NULL); 207 } 208 #endif 209 210 static int 211 datagram_pair(int *client_sock, int *server_sock, 212 struct sockaddr_in *server_sin) 213 { 214 struct sockaddr_in sin; 215 socklen_t sock_len; 216 int cs = -1, ss = -1; 217 218 memset(&sin, 0, sizeof(sin)); 219 sin.sin_family = AF_INET; 220 sin.sin_port = 0; 221 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 222 223 if ((ss = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 224 err(1, "server socket"); 225 if (bind(ss, (struct sockaddr *)&sin, sizeof(sin)) == -1) 226 err(1, "server bind"); 227 sock_len = sizeof(sin); 228 if (getsockname(ss, (struct sockaddr *)&sin, &sock_len) == -1) 229 err(1, "server getsockname"); 230 231 if ((cs = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 232 err(1, "client socket"); 233 if (connect(cs, (struct sockaddr *)&sin, sizeof(sin)) == -1) 234 err(1, "client connect"); 235 236 *client_sock = cs; 237 *server_sock = ss; 238 memcpy(server_sin, &sin, sizeof(sin)); 239 240 return 1; 241 } 242 243 static int 244 poll_timeout(SSL *client, SSL *server) 245 { 246 int client_timeout = 0, server_timeout = 0; 247 struct timeval timeout; 248 249 if (DTLSv1_get_timeout(client, &timeout)) 250 client_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; 251 252 if (DTLSv1_get_timeout(server, &timeout)) 253 server_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; 254 255 if (client_timeout <= 0) 256 return server_timeout; 257 if (client_timeout > 0 && server_timeout <= 0) 258 return client_timeout; 259 if (client_timeout < server_timeout) 260 return client_timeout; 261 262 return server_timeout; 263 } 264 265 static int 266 dtls_cookie_generate(SSL *ssl, unsigned char *cookie, 267 unsigned int *cookie_len) 268 { 269 arc4random_buf(dtls_cookie, sizeof(dtls_cookie)); 270 memcpy(cookie, dtls_cookie, sizeof(dtls_cookie)); 271 *cookie_len = sizeof(dtls_cookie); 272 273 return 1; 274 } 275 276 static int 277 dtls_cookie_verify(SSL *ssl, const unsigned char *cookie, 278 unsigned int cookie_len) 279 { 280 return cookie_len == sizeof(dtls_cookie) && 281 memcmp(cookie, dtls_cookie, sizeof(dtls_cookie)) == 0; 282 } 283 284 static void 285 dtls_info_callback(const SSL *ssl, int type, int val) 286 { 287 /* 288 * Squeals ahead... remove the bbio from the info callback, so we can 289 * drop specific messages. Ideally this would be an option for the SSL. 290 */ 291 if (ssl->wbio == ssl->bbio) 292 ((SSL *)ssl)->wbio = BIO_pop(ssl->wbio); 293 } 294 295 static SSL * 296 dtls_client(int sock, struct sockaddr_in *server_sin, long mtu) 297 { 298 SSL_CTX *ssl_ctx = NULL; 299 SSL *ssl = NULL; 300 BIO *bio = NULL; 301 302 if ((bio = BIO_new_dgram(sock, BIO_NOCLOSE)) == NULL) 303 errx(1, "client bio"); 304 if (!BIO_socket_nbio(sock, 1)) 305 errx(1, "client nbio"); 306 if (!BIO_ctrl_set_connected(bio, 1, server_sin)) 307 errx(1, "client set connected"); 308 309 if ((ssl_ctx = SSL_CTX_new(DTLS_method())) == NULL) 310 errx(1, "client context"); 311 SSL_CTX_set_read_ahead(ssl_ctx, 1); 312 313 if ((ssl = SSL_new(ssl_ctx)) == NULL) 314 errx(1, "client ssl"); 315 316 SSL_set_bio(ssl, bio, bio); 317 bio = NULL; 318 319 if (mtu > 0) { 320 SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU); 321 SSL_set_mtu(ssl, mtu); 322 } 323 324 SSL_CTX_free(ssl_ctx); 325 BIO_free(bio); 326 327 return ssl; 328 } 329 330 static SSL * 331 dtls_server(int sock, long options, long mtu) 332 { 333 SSL_CTX *ssl_ctx = NULL; 334 SSL *ssl = NULL; 335 BIO *bio = NULL; 336 337 if ((bio = BIO_new_dgram(sock, BIO_NOCLOSE)) == NULL) 338 errx(1, "server bio"); 339 if (!BIO_socket_nbio(sock, 1)) 340 errx(1, "server nbio"); 341 342 if ((ssl_ctx = SSL_CTX_new(DTLS_method())) == NULL) 343 errx(1, "server context"); 344 345 SSL_CTX_set_cookie_generate_cb(ssl_ctx, dtls_cookie_generate); 346 SSL_CTX_set_cookie_verify_cb(ssl_ctx, dtls_cookie_verify); 347 SSL_CTX_set_dh_auto(ssl_ctx, 2); 348 SSL_CTX_set_options(ssl_ctx, options); 349 SSL_CTX_set_read_ahead(ssl_ctx, 1); 350 351 if (SSL_CTX_use_certificate_file(ssl_ctx, server_cert_file, 352 SSL_FILETYPE_PEM) != 1) { 353 fprintf(stderr, "FAIL: Failed to load server certificate"); 354 goto failure; 355 } 356 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, server_key_file, 357 SSL_FILETYPE_PEM) != 1) { 358 fprintf(stderr, "FAIL: Failed to load server private key"); 359 goto failure; 360 } 361 362 if ((ssl = SSL_new(ssl_ctx)) == NULL) 363 errx(1, "server ssl"); 364 365 SSL_set_bio(ssl, bio, bio); 366 bio = NULL; 367 368 if (mtu > 0) { 369 SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU); 370 SSL_set_mtu(ssl, mtu); 371 } 372 373 failure: 374 SSL_CTX_free(ssl_ctx); 375 BIO_free(bio); 376 377 return ssl; 378 } 379 380 static int 381 ssl_error(SSL *ssl, const char *name, const char *desc, int ssl_ret, 382 short *events) 383 { 384 int ssl_err; 385 386 ssl_err = SSL_get_error(ssl, ssl_ret); 387 388 if (ssl_err == SSL_ERROR_WANT_READ) { 389 *events = POLLIN; 390 } else if (ssl_err == SSL_ERROR_WANT_WRITE) { 391 *events = POLLOUT; 392 } else if (ssl_err == SSL_ERROR_SYSCALL && errno == 0) { 393 /* Yup, this is apparently a thing... */ 394 } else { 395 fprintf(stderr, "FAIL: %s %s failed - ssl err = %d, errno = %d\n", 396 name, desc, ssl_err, errno); 397 ERR_print_errors_fp(stderr); 398 return 0; 399 } 400 401 return 1; 402 } 403 404 static int 405 do_connect(SSL *ssl, const char *name, int *done, short *events) 406 { 407 int ssl_ret; 408 409 if ((ssl_ret = SSL_connect(ssl)) == 1) { 410 fprintf(stderr, "INFO: %s connect done\n", name); 411 *done = 1; 412 return 1; 413 } 414 415 return ssl_error(ssl, name, "connect", ssl_ret, events); 416 } 417 418 static int 419 do_accept(SSL *ssl, const char *name, int *done, short *events) 420 { 421 int ssl_ret; 422 423 if ((ssl_ret = SSL_accept(ssl)) == 1) { 424 fprintf(stderr, "INFO: %s accept done\n", name); 425 *done = 1; 426 return 1; 427 } 428 429 return ssl_error(ssl, name, "accept", ssl_ret, events); 430 } 431 432 static int 433 do_read(SSL *ssl, const char *name, int *done, short *events) 434 { 435 uint8_t buf[512]; 436 int ssl_ret; 437 438 if ((ssl_ret = SSL_read(ssl, buf, sizeof(buf))) > 0) { 439 fprintf(stderr, "INFO: %s read done\n", name); 440 if (debug > 1) 441 hexdump(buf, ssl_ret); 442 *done = 1; 443 return 1; 444 } 445 446 return ssl_error(ssl, name, "read", ssl_ret, events); 447 } 448 449 static int 450 do_write(SSL *ssl, const char *name, int *done, short *events) 451 { 452 const uint8_t buf[] = "Hello, World!\n"; 453 int ssl_ret; 454 455 if ((ssl_ret = SSL_write(ssl, buf, sizeof(buf))) > 0) { 456 fprintf(stderr, "INFO: %s write done\n", name); 457 *done = 1; 458 return 1; 459 } 460 461 return ssl_error(ssl, name, "write", ssl_ret, events); 462 } 463 464 static int 465 do_shutdown(SSL *ssl, const char *name, int *done, short *events) 466 { 467 int ssl_ret; 468 469 ssl_ret = SSL_shutdown(ssl); 470 if (ssl_ret == 1) { 471 fprintf(stderr, "INFO: %s shutdown done\n", name); 472 *done = 1; 473 return 1; 474 } 475 return ssl_error(ssl, name, "shutdown", ssl_ret, events); 476 } 477 478 typedef int (*ssl_func)(SSL *ssl, const char *name, int *done, short *events); 479 480 static int 481 do_client_server_loop(SSL *client, ssl_func client_func, SSL *server, 482 ssl_func server_func, struct pollfd pfd[2]) 483 { 484 int client_done = 0, server_done = 0; 485 int i = 0; 486 487 pfd[0].revents = POLLIN; 488 pfd[1].revents = POLLIN; 489 490 do { 491 if (!client_done) { 492 if (debug) 493 fprintf(stderr, "DEBUG: client loop\n"); 494 if (DTLSv1_handle_timeout(client) > 0) 495 fprintf(stderr, "INFO: client timeout\n"); 496 if (!client_func(client, "client", &client_done, 497 &pfd[0].events)) 498 return 0; 499 if (client_done) 500 pfd[0].events = 0; 501 } 502 if (!server_done) { 503 if (debug) 504 fprintf(stderr, "DEBUG: server loop\n"); 505 if (DTLSv1_handle_timeout(server) > 0) 506 fprintf(stderr, "INFO: server timeout\n"); 507 if (!server_func(server, "server", &server_done, 508 &pfd[1].events)) 509 return 0; 510 if (server_done) 511 pfd[1].events = 0; 512 } 513 if (poll(pfd, 2, poll_timeout(client, server)) == -1) 514 err(1, "poll"); 515 516 } while (i++ < 100 && (!client_done || !server_done)); 517 518 if (!client_done || !server_done) 519 fprintf(stderr, "FAIL: gave up\n"); 520 521 return client_done && server_done; 522 } 523 524 #define MAX_PACKET_DROPS 32 525 526 struct dtls_test { 527 const unsigned char *desc; 528 long mtu; 529 long ssl_options; 530 int client_bbio_off; 531 int server_bbio_off; 532 uint8_t client_drops[MAX_PACKET_DROPS]; 533 uint8_t server_drops[MAX_PACKET_DROPS]; 534 }; 535 536 static const struct dtls_test dtls_tests[] = { 537 { 538 .desc = "DTLS without cookies", 539 .ssl_options = 0, 540 }, 541 { 542 .desc = "DTLS with cookies", 543 .ssl_options = SSL_OP_COOKIE_EXCHANGE, 544 }, 545 { 546 .desc = "DTLS with low MTU", 547 .mtu = 256, 548 .ssl_options = 0, 549 }, 550 { 551 .desc = "DTLS with low MTU and cookies", 552 .mtu = 256, 553 .ssl_options = SSL_OP_COOKIE_EXCHANGE, 554 }, 555 { 556 .desc = "DTLS with dropped server response", 557 .ssl_options = 0, 558 .server_drops = { 1 }, 559 }, 560 { 561 .desc = "DTLS with two dropped server responses", 562 .ssl_options = 0, 563 .server_drops = { 1, 2 }, 564 }, 565 { 566 .desc = "DTLS with dropped ServerHello", 567 .ssl_options = SSL_OP_NO_TICKET, 568 .server_bbio_off = 1, 569 .server_drops = { 1 }, 570 }, 571 { 572 .desc = "DTLS with dropped server Certificate", 573 .ssl_options = SSL_OP_NO_TICKET, 574 .server_bbio_off = 1, 575 .server_drops = { 2 }, 576 }, 577 { 578 .desc = "DTLS with dropped ServerKeyExchange", 579 .ssl_options = SSL_OP_NO_TICKET, 580 .server_bbio_off = 1, 581 .server_drops = { 3 }, 582 }, 583 { 584 .desc = "DTLS with dropped ServerHelloDone", 585 .ssl_options = SSL_OP_NO_TICKET, 586 .server_bbio_off = 1, 587 .server_drops = { 4 }, 588 }, 589 #if 0 590 /* 591 * These two result in the server accept completing and the 592 * client looping on a timeout. Presumably the server should not 593 * complete until the client Finished is received... 594 */ 595 { 596 .desc = "DTLS with dropped server CCS", 597 .ssl_options = 0, 598 .server_bbio_off = 1, 599 .server_drops = { 5 }, 600 }, 601 { 602 .desc = "DTLS with dropped server Finished", 603 .ssl_options = 0, 604 .server_bbio_off = 1, 605 .server_drops = { 6 }, 606 }, 607 #endif 608 { 609 .desc = "DTLS with dropped ClientKeyExchange", 610 .ssl_options = 0, 611 .client_bbio_off = 1, 612 .client_drops = { 2 }, 613 }, 614 { 615 .desc = "DTLS with dropped client CCS", 616 .ssl_options = 0, 617 .client_bbio_off = 1, 618 .client_drops = { 3 }, 619 }, 620 { 621 .desc = "DTLS with dropped client Finished", 622 .ssl_options = 0, 623 .client_bbio_off = 1, 624 .client_drops = { 4 }, 625 }, 626 }; 627 628 #define N_DTLS_TESTS (sizeof(dtls_tests) / sizeof(*dtls_tests)) 629 630 static void 631 dtlstest_packet_monkey(SSL *ssl, const uint8_t drops[]) 632 { 633 BIO *bio_monkey; 634 BIO *bio; 635 int i; 636 637 if ((bio_monkey = BIO_new_packet_monkey()) == NULL) 638 errx(1, "packet monkey"); 639 640 for (i = 0; i < MAX_PACKET_DROPS; i++) { 641 if (drops[i] == 0) 642 break; 643 if (!BIO_packet_monkey_drop(bio_monkey, drops[i])) 644 errx(1, "drop failure"); 645 } 646 647 if ((bio = SSL_get_wbio(ssl)) == NULL) 648 errx(1, "SSL has NULL bio"); 649 650 BIO_up_ref(bio); 651 bio = BIO_push(bio_monkey, bio); 652 653 SSL_set_bio(ssl, bio, bio); 654 } 655 656 static int 657 dtlstest(const struct dtls_test *dt) 658 { 659 SSL *client = NULL, *server = NULL; 660 struct sockaddr_in server_sin; 661 struct pollfd pfd[2]; 662 int client_sock = -1; 663 int server_sock = -1; 664 int failed = 1; 665 666 fprintf(stderr, "\n== Testing %s... ==\n", dt->desc); 667 668 if (!datagram_pair(&client_sock, &server_sock, &server_sin)) 669 goto failure; 670 671 if ((client = dtls_client(client_sock, &server_sin, dt->mtu)) == NULL) 672 goto failure; 673 if ((server = dtls_server(server_sock, dt->ssl_options, dt->mtu)) == NULL) 674 goto failure; 675 676 if (dt->client_bbio_off) 677 SSL_set_info_callback(client, dtls_info_callback); 678 if (dt->server_bbio_off) 679 SSL_set_info_callback(server, dtls_info_callback); 680 681 dtlstest_packet_monkey(client, dt->client_drops); 682 dtlstest_packet_monkey(server, dt->server_drops); 683 684 pfd[0].fd = client_sock; 685 pfd[0].events = POLLOUT; 686 pfd[1].fd = server_sock; 687 pfd[1].events = POLLIN; 688 689 if (!do_client_server_loop(client, do_connect, server, do_accept, pfd)) { 690 fprintf(stderr, "FAIL: client and server handshake failed\n"); 691 goto failure; 692 } 693 694 pfd[0].events = POLLIN; 695 pfd[1].events = POLLOUT; 696 697 if (!do_client_server_loop(client, do_read, server, do_write, pfd)) { 698 fprintf(stderr, "FAIL: client read and server write I/O failed\n"); 699 goto failure; 700 } 701 702 pfd[0].events = POLLOUT; 703 pfd[1].events = POLLIN; 704 705 if (!do_client_server_loop(client, do_write, server, do_read, pfd)) { 706 fprintf(stderr, "FAIL: client write and server read I/O failed\n"); 707 goto failure; 708 } 709 710 pfd[0].events = POLLOUT; 711 pfd[1].events = POLLOUT; 712 713 if (!do_client_server_loop(client, do_shutdown, server, do_shutdown, pfd)) { 714 fprintf(stderr, "FAIL: client and server shutdown failed\n"); 715 goto failure; 716 } 717 718 fprintf(stderr, "INFO: Done!\n"); 719 720 failed = 0; 721 722 failure: 723 if (client_sock != -1) 724 close(client_sock); 725 if (server_sock != -1) 726 close(server_sock); 727 728 SSL_free(client); 729 SSL_free(server); 730 731 return failed; 732 } 733 734 int 735 main(int argc, char **argv) 736 { 737 int failed = 0; 738 size_t i; 739 740 if (argc != 4) { 741 fprintf(stderr, "usage: %s keyfile certfile cafile\n", 742 argv[0]); 743 exit(1); 744 } 745 746 server_key_file = argv[1]; 747 server_cert_file = argv[2]; 748 server_ca_file = argv[3]; 749 750 for (i = 0; i < N_DTLS_TESTS; i++) 751 failed |= dtlstest(&dtls_tests[i]); 752 753 return failed; 754 } 755