1 /* 2 * testcode/dohclient.c - debug program. Perform multiple DNS queries using DoH. 3 * 4 * Copyright (c) 2020, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /** 37 * \file 38 * 39 * Simple DNS-over-HTTPS client. For testing and debugging purposes. 40 * No authentication of TLS cert. 41 */ 42 43 #include "config.h" 44 #ifdef HAVE_GETOPT_H 45 #include <getopt.h> 46 #endif 47 #include "sldns/wire2str.h" 48 #include "sldns/sbuffer.h" 49 #include "sldns/str2wire.h" 50 #include "sldns/parseutil.h" 51 #include "util/data/msgencode.h" 52 #include "util/data/msgreply.h" 53 #include "util/data/msgparse.h" 54 #include "util/net_help.h" 55 #include <openssl/ssl.h> 56 #include <openssl/err.h> 57 #ifdef HAVE_NGHTTP2 58 #include <nghttp2/nghttp2.h> 59 60 struct http2_session { 61 nghttp2_session* session; 62 SSL* ssl; 63 int fd; 64 int query_count; 65 /* Use POST :method if 1 */ 66 int post; 67 int block_select; 68 const char* authority; 69 const char* endpoint; 70 const char* content_type; 71 }; 72 73 struct http2_stream { 74 int32_t stream_id; 75 int res_status; 76 struct sldns_buffer* buf; 77 char* path; 78 }; 79 80 static void usage(char* argv[]) 81 { 82 printf("usage: %s [options] name type class ...\n", argv[0]); 83 printf(" sends the name-type-class queries over " 84 "DNS-over-HTTPS.\n"); 85 printf("-s server IP address to send the queries to, " 86 "default: 127.0.0.1\n"); 87 printf("-p Port to connect to, default: %d\n", 88 UNBOUND_DNS_OVER_HTTPS_PORT); 89 printf("-P Use POST method instead of default GET\n"); 90 printf("-e HTTP endpoint, default: /dns-query\n"); 91 printf("-c Content-type in request, default: " 92 "application/dns-message\n"); 93 printf("-n no-tls, TLS is disabled\n"); 94 printf("-h This help text\n"); 95 exit(1); 96 } 97 98 /** open TCP socket to svr */ 99 static int 100 open_svr(const char* svr, int port) 101 { 102 struct sockaddr_storage addr; 103 socklen_t addrlen; 104 int fd = -1; 105 int r; 106 if(!ipstrtoaddr(svr, port, &addr, &addrlen)) { 107 printf("fatal: bad server specs '%s'\n", svr); 108 exit(1); 109 } 110 111 fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET, 112 SOCK_STREAM, 0); 113 if(fd == -1) { 114 perror("socket() error"); 115 exit(1); 116 } 117 r = connect(fd, (struct sockaddr*)&addr, addrlen); 118 if(r < 0 && r != EINPROGRESS) { 119 perror("connect() error"); 120 exit(1); 121 } 122 return fd; 123 } 124 125 static ssize_t http2_submit_request_read_cb( 126 nghttp2_session* ATTR_UNUSED(session), 127 int32_t ATTR_UNUSED(stream_id), uint8_t* buf, size_t length, 128 uint32_t* data_flags, nghttp2_data_source* source, 129 void* ATTR_UNUSED(cb_arg)) 130 { 131 if(length > sldns_buffer_remaining(source->ptr)) 132 length = sldns_buffer_remaining(source->ptr); 133 134 memcpy(buf, sldns_buffer_current(source->ptr), length); 135 sldns_buffer_skip(source->ptr, length); 136 137 if(sldns_buffer_remaining(source->ptr) == 0) { 138 *data_flags |= NGHTTP2_DATA_FLAG_EOF; 139 } 140 141 return length; 142 } 143 144 static void 145 submit_query(struct http2_session* h2_session, struct sldns_buffer* buf) 146 { 147 int32_t stream_id; 148 struct http2_stream* h2_stream; 149 nghttp2_nv headers[5]; 150 char* qb64; 151 size_t qb64_size; 152 size_t qb64_expected_size; 153 size_t i; 154 nghttp2_data_provider data_prd; 155 156 h2_stream = calloc(1, sizeof(*h2_stream)); 157 if(!h2_stream) 158 fatal_exit("could not malloc http2 stream"); 159 h2_stream->buf = buf; 160 161 if(h2_session->post) { 162 data_prd.source.ptr = buf; 163 data_prd.read_callback = http2_submit_request_read_cb; 164 h2_stream->path = (char*)h2_session->endpoint; 165 } else { 166 qb64_expected_size = sldns_b64_ntop_calculate_size( 167 sldns_buffer_remaining(buf)); 168 qb64 = malloc(qb64_expected_size); 169 if(!qb64) fatal_exit("out of memory"); 170 qb64_size = sldns_b64url_ntop(sldns_buffer_begin(buf), 171 sldns_buffer_remaining(buf), qb64, qb64_expected_size); 172 h2_stream->path = malloc(strlen( 173 h2_session->endpoint)+strlen("?dns=")+qb64_size+1); 174 if(!h2_stream->path) fatal_exit("out of memory"); 175 snprintf(h2_stream->path, strlen(h2_session->endpoint)+ 176 strlen("?dns=")+qb64_size+1, "%s?dns=%s", 177 h2_session->endpoint, qb64); 178 free(qb64); 179 } 180 181 headers[0].name = (uint8_t*)":method"; 182 if(h2_session->post) 183 headers[0].value = (uint8_t*)"POST"; 184 else 185 headers[0].value = (uint8_t*)"GET"; 186 headers[1].name = (uint8_t*)":path"; 187 headers[1].value = (uint8_t*)h2_stream->path; 188 headers[2].name = (uint8_t*)":scheme"; 189 if(h2_session->ssl) 190 headers[2].value = (uint8_t*)"https"; 191 else 192 headers[2].value = (uint8_t*)"http"; 193 headers[3].name = (uint8_t*)":authority"; 194 headers[3].value = (uint8_t*)h2_session->authority; 195 headers[4].name = (uint8_t*)"content-type"; 196 headers[4].value = (uint8_t*)h2_session->content_type; 197 198 printf("Request headers\n"); 199 for(i=0; i<sizeof(headers)/sizeof(headers[0]); i++) { 200 headers[i].namelen = strlen((char*)headers[i].name); 201 headers[i].valuelen = strlen((char*)headers[i].value); 202 headers[i].flags = NGHTTP2_NV_FLAG_NONE; 203 printf("%s: %s\n", headers[i].name, headers[i].value); 204 } 205 206 stream_id = nghttp2_submit_request(h2_session->session, NULL, headers, 207 sizeof(headers)/sizeof(headers[0]), 208 (h2_session->post) ? &data_prd : NULL, h2_stream); 209 if(stream_id < 0) { 210 printf("Failed to submit nghttp2 request"); 211 exit(1); 212 } 213 h2_session->query_count++; 214 h2_stream->stream_id = stream_id; 215 } 216 217 static sldns_buffer* 218 make_query(char* qname, char* qtype, char* qclass) 219 { 220 struct query_info qinfo; 221 struct edns_data edns; 222 sldns_buffer* buf = sldns_buffer_new(65553); 223 if(!buf) fatal_exit("out of memory"); 224 qinfo.qname = sldns_str2wire_dname(qname, &qinfo.qname_len); 225 if(!qinfo.qname) { 226 printf("cannot parse query name: '%s'\n", qname); 227 exit(1); 228 } 229 qinfo.qtype = sldns_get_rr_type_by_name(qtype); 230 if(qinfo.qtype == 0 && strcmp(qtype, "TYPE0") != 0) { 231 printf("cannot parse query type: '%s'\n", qtype); 232 exit(1); 233 } 234 qinfo.qclass = sldns_get_rr_class_by_name(qclass); 235 if(qinfo.qclass == 0 && strcmp(qclass, "CLASS0") != 0) { 236 printf("cannot parse query class: '%s'\n", qclass); 237 exit(1); 238 } 239 qinfo.local_alias = NULL; 240 241 qinfo_query_encode(buf, &qinfo); /* flips buffer */ 242 free(qinfo.qname); 243 sldns_buffer_write_u16_at(buf, 0, 0x0000); 244 sldns_buffer_write_u16_at(buf, 2, BIT_RD); 245 memset(&edns, 0, sizeof(edns)); 246 edns.edns_present = 1; 247 edns.bits = EDNS_DO; 248 edns.udp_size = 4096; 249 if(sldns_buffer_capacity(buf) >= 250 sldns_buffer_limit(buf)+calc_edns_field_size(&edns)) 251 attach_edns_record(buf, &edns); 252 return buf; 253 } 254 255 static ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), 256 uint8_t* buf, size_t len, int ATTR_UNUSED(flags), void* cb_arg) 257 { 258 struct http2_session* h2_session = (struct http2_session*)cb_arg; 259 int r; 260 ssize_t ret; 261 struct timeval tv, *waittv; 262 fd_set rfd; 263 ERR_clear_error(); 264 265 memset(&tv, 0, sizeof(tv)); 266 267 if(h2_session->block_select && h2_session->query_count <= 0) { 268 return NGHTTP2_ERR_WOULDBLOCK; 269 } 270 if(h2_session->block_select) 271 waittv = NULL; 272 else 273 waittv = &tv; 274 memset(&rfd, 0, sizeof(rfd)); 275 FD_ZERO(&rfd); 276 FD_SET(h2_session->fd, &rfd); 277 r = select(h2_session->fd+1, &rfd, NULL, NULL, waittv); 278 if(r <= 0) { 279 return NGHTTP2_ERR_WOULDBLOCK; 280 } 281 282 if(h2_session->ssl) { 283 r = SSL_read(h2_session->ssl, buf, len); 284 if(r <= 0) { 285 int want = SSL_get_error(h2_session->ssl, r); 286 if(want == SSL_ERROR_ZERO_RETURN) { 287 return NGHTTP2_ERR_EOF; 288 } 289 log_crypto_err("could not SSL_read"); 290 return NGHTTP2_ERR_EOF; 291 } 292 return r; 293 } 294 295 ret = read(h2_session->fd, buf, len); 296 if(ret == 0) { 297 return NGHTTP2_ERR_EOF; 298 } else if(ret < 0) { 299 log_err("could not http2 read: %s", strerror(errno)); 300 return NGHTTP2_ERR_EOF; 301 } 302 return ret; 303 } 304 305 static ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), 306 const uint8_t* buf, size_t len, int ATTR_UNUSED(flags), void* cb_arg) 307 { 308 struct http2_session* h2_session = (struct http2_session*)cb_arg; 309 ssize_t ret; 310 311 if(h2_session->ssl) { 312 int r; 313 ERR_clear_error(); 314 r = SSL_write(h2_session->ssl, buf, len); 315 if(r <= 0) { 316 int want = SSL_get_error(h2_session->ssl, r); 317 if(want == SSL_ERROR_ZERO_RETURN) { 318 return NGHTTP2_ERR_CALLBACK_FAILURE; 319 } 320 log_crypto_err("could not SSL_write"); 321 return NGHTTP2_ERR_CALLBACK_FAILURE; 322 } 323 return r; 324 } 325 326 ret = write(h2_session->fd, buf, len); 327 if(ret == 0) { 328 return NGHTTP2_ERR_CALLBACK_FAILURE; 329 } else if(ret < 0) { 330 log_err("could not http2 write: %s", strerror(errno)); 331 return NGHTTP2_ERR_CALLBACK_FAILURE; 332 } 333 return ret; 334 } 335 336 static int http2_stream_close_cb(nghttp2_session* ATTR_UNUSED(session), 337 int32_t ATTR_UNUSED(stream_id), 338 nghttp2_error_code ATTR_UNUSED(error_code), void *cb_arg) 339 { 340 struct http2_session* h2_session = (struct http2_session*)cb_arg; 341 struct http2_stream* h2_stream; 342 if(!(h2_stream = nghttp2_session_get_stream_user_data( 343 h2_session->session, stream_id))) { 344 return 0; 345 } 346 h2_session->query_count--; 347 sldns_buffer_free(h2_stream->buf); 348 if(!h2_session->post) 349 free(h2_stream->path); 350 free(h2_stream); 351 h2_stream = NULL; 352 return 0; 353 } 354 355 static int http2_data_chunk_recv_cb(nghttp2_session* ATTR_UNUSED(session), 356 uint8_t ATTR_UNUSED(flags), int32_t stream_id, const uint8_t* data, 357 size_t len, void* cb_arg) 358 { 359 struct http2_session* h2_session = (struct http2_session*)cb_arg; 360 struct http2_stream* h2_stream; 361 362 if(!(h2_stream = nghttp2_session_get_stream_user_data( 363 h2_session->session, stream_id))) { 364 return 0; 365 } 366 367 if(sldns_buffer_remaining(h2_stream->buf) < len) { 368 log_err("received data chunk does not fit into buffer"); 369 return NGHTTP2_ERR_CALLBACK_FAILURE; 370 } 371 372 sldns_buffer_write(h2_stream->buf, data, len); 373 374 return 0; 375 } 376 377 static int http2_frame_recv_cb(nghttp2_session *session, 378 const nghttp2_frame *frame, void* ATTR_UNUSED(cb_arg)) 379 { 380 struct http2_stream* h2_stream; 381 382 if(!(h2_stream = nghttp2_session_get_stream_user_data( 383 session, frame->hd.stream_id))) 384 return 0; 385 if(frame->hd.type == NGHTTP2_HEADERS && 386 frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { 387 sldns_buffer_clear(h2_stream->buf); 388 } 389 if(((frame->hd.type != NGHTTP2_DATA && 390 frame->hd.type != NGHTTP2_HEADERS) || 391 frame->hd.flags & NGHTTP2_FLAG_END_STREAM) && 392 h2_stream->res_status == 200) { 393 char* pktstr; 394 sldns_buffer_flip(h2_stream->buf); 395 pktstr = sldns_wire2str_pkt( 396 sldns_buffer_begin(h2_stream->buf), 397 sldns_buffer_limit(h2_stream->buf)); 398 printf("%s\n", pktstr); 399 free(pktstr); 400 return 0; 401 } 402 return 0; 403 } 404 static int http2_header_cb(nghttp2_session* ATTR_UNUSED(session), 405 const nghttp2_frame* frame, const uint8_t* name, size_t namelen, 406 const uint8_t* value, size_t ATTR_UNUSED(valuelen), 407 uint8_t ATTR_UNUSED(flags), void* cb_arg) 408 { 409 struct http2_stream* h2_stream; 410 struct http2_session* h2_session = (struct http2_session*)cb_arg; 411 printf("%s %s\n", name, value); 412 if(namelen == 7 && memcmp(":status", name, namelen) == 0) { 413 if(!(h2_stream = nghttp2_session_get_stream_user_data( 414 h2_session->session, frame->hd.stream_id))) { 415 return 0; 416 } 417 h2_stream->res_status = atoi((char*)value); 418 } 419 return 0; 420 } 421 422 static struct http2_session* 423 http2_session_create() 424 { 425 struct http2_session* h2_session = calloc(1, 426 sizeof(struct http2_session)); 427 nghttp2_session_callbacks* callbacks; 428 if(!h2_session) 429 fatal_exit("out of memory"); 430 431 if(nghttp2_session_callbacks_new(&callbacks) == NGHTTP2_ERR_NOMEM) { 432 log_err("failed to initialize nghttp2 callback"); 433 free(h2_session); 434 return NULL; 435 } 436 nghttp2_session_callbacks_set_recv_callback(callbacks, http2_recv_cb); 437 nghttp2_session_callbacks_set_send_callback(callbacks, http2_send_cb); 438 nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, 439 http2_stream_close_cb); 440 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, 441 http2_data_chunk_recv_cb); 442 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, 443 http2_frame_recv_cb); 444 nghttp2_session_callbacks_set_on_header_callback(callbacks, 445 http2_header_cb); 446 nghttp2_session_client_new(&h2_session->session, callbacks, h2_session); 447 nghttp2_session_callbacks_del(callbacks); 448 return h2_session; 449 } 450 451 static void 452 http2_session_delete(struct http2_session* h2_session) 453 { 454 nghttp2_session_del(h2_session->session); 455 free(h2_session); 456 } 457 458 static void 459 http2_submit_setting(struct http2_session* h2_session) 460 { 461 int ret; 462 nghttp2_settings_entry settings[1] = { 463 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 464 100}}; 465 466 ret = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE, 467 settings, 1); 468 if(ret) { 469 printf("http2: submit_settings failed, " 470 "error: %s\n", nghttp2_strerror(ret)); 471 exit(1); 472 } 473 } 474 475 static void 476 http2_write(struct http2_session* h2_session) 477 { 478 if(nghttp2_session_want_write(h2_session->session)) { 479 if(nghttp2_session_send(h2_session->session)) { 480 printf("nghttp2 session send failed\n"); 481 exit(1); 482 } 483 } 484 } 485 486 static void 487 http2_read(struct http2_session* h2_session) 488 { 489 if(nghttp2_session_want_read(h2_session->session)) { 490 if(nghttp2_session_recv(h2_session->session)) { 491 printf("nghttp2 session mem_recv failed\n"); 492 exit(1); 493 } 494 } 495 } 496 497 static void 498 run(struct http2_session* h2_session, int port, int no_tls, int count, char** q) 499 { 500 int i; 501 SSL_CTX* ctx = NULL; 502 SSL* ssl = NULL; 503 int fd; 504 struct sldns_buffer* buf = NULL; 505 506 fd = open_svr(h2_session->authority, port); 507 h2_session->fd = fd; 508 509 if(!no_tls) { 510 ctx = connect_sslctx_create(NULL, NULL, NULL, 0); 511 if(!ctx) fatal_exit("cannot create ssl ctx"); 512 #ifdef HAVE_SSL_CTX_SET_ALPN_PROTOS 513 SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)"\x02h2", 3); 514 #endif 515 ssl = outgoing_ssl_fd(ctx, fd); 516 if(!ssl) { 517 printf("cannot create ssl\n"); 518 exit(1); 519 } 520 h2_session->ssl = ssl; 521 while(1) { 522 int r; 523 ERR_clear_error(); 524 if( (r=SSL_do_handshake(ssl)) == 1) 525 break; 526 r = SSL_get_error(ssl, r); 527 if(r != SSL_ERROR_WANT_READ && 528 r != SSL_ERROR_WANT_WRITE) { 529 log_crypto_err("could not ssl_handshake"); 530 exit(1); 531 } 532 } 533 } 534 535 http2_submit_setting(h2_session); 536 http2_write(h2_session); 537 http2_read(h2_session); /* Read setting from remote peer */ 538 539 h2_session->block_select = 1; 540 541 /* handle query */ 542 for(i=0; i<count; i+=3) { 543 buf = make_query(q[i], q[i+1], q[i+2]); 544 submit_query(h2_session, buf); 545 } 546 http2_write(h2_session); 547 while(h2_session->query_count) { 548 http2_read(h2_session); 549 http2_write(h2_session); 550 } 551 552 /* shutdown */ 553 http2_session_delete(h2_session); 554 if(ssl) { 555 SSL_shutdown(ssl); 556 SSL_free(ssl); 557 } 558 if(ctx) { 559 SSL_CTX_free(ctx); 560 } 561 sock_close(fd); 562 } 563 564 /** getopt global, in case header files fail to declare it. */ 565 extern int optind; 566 /** getopt global, in case header files fail to declare it. */ 567 extern char* optarg; 568 int main(int argc, char** argv) 569 { 570 int c; 571 int port = UNBOUND_DNS_OVER_HTTPS_PORT, no_tls = 0; 572 struct http2_session* h2_session; 573 574 #ifdef USE_WINSOCK 575 WSADATA wsa_data; 576 if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) { 577 printf("WSAStartup failed\n"); 578 return 1; 579 } 580 #endif 581 checklock_start(); 582 log_init(0, 0, 0); 583 log_ident_set("dohclient"); 584 585 h2_session = http2_session_create(); 586 if(!h2_session) fatal_exit("out of memory"); 587 if(argc == 1) { 588 usage(argv); 589 } 590 591 h2_session->authority = "127.0.0.1"; 592 h2_session->post = 0; 593 h2_session->endpoint = "/dns-query"; 594 h2_session->content_type = "application/dns-message"; 595 596 while((c=getopt(argc, argv, "c:e:hns:p:P")) != -1) { 597 switch(c) { 598 case 'c': 599 h2_session->content_type = optarg; 600 break; 601 case 'e': 602 h2_session->endpoint = optarg; 603 break; 604 case 'n': 605 no_tls = 1; 606 break; 607 case 'p': 608 if(atoi(optarg)==0 && strcmp(optarg,"0")!=0) { 609 printf("error parsing port, " 610 "number expected: %s\n", optarg); 611 return 1; 612 } 613 port = atoi(optarg); 614 break; 615 case 'P': 616 h2_session->post = 1; 617 break; 618 case 's': 619 h2_session->authority = optarg; 620 break; 621 case 'h': 622 case '?': 623 default: 624 usage(argv); 625 } 626 } 627 argc -= optind; 628 argv += optind; 629 if(argc%3!=0) { 630 printf("Invalid input. Specify qname, qtype, and qclass.\n"); 631 return 1; 632 } 633 634 if(!no_tls) { 635 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 636 ERR_load_SSL_strings(); 637 #endif 638 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) 639 # ifndef S_SPLINT_S 640 OpenSSL_add_all_algorithms(); 641 # endif 642 #else 643 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS 644 | OPENSSL_INIT_ADD_ALL_DIGESTS 645 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 646 #endif 647 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 648 (void)SSL_library_init(); 649 #else 650 (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); 651 #endif 652 } 653 run(h2_session, port, no_tls, argc, argv); 654 655 checklock_stop(); 656 #ifdef USE_WINSOCK 657 WSACleanup(); 658 #endif 659 return 0; 660 } 661 #else 662 int main(int ATTR_UNUSED(argc), char** ATTR_UNUSED(argv)) 663 { 664 printf("Compiled without nghttp2, cannot run test.\n"); 665 return 1; 666 } 667 #endif /* HAVE_NGHTTP2 */ 668