1/* 2 * nghttp2 - HTTP/2 C Library 3 * 4 * Copyright (c) 2013 Tatsuhiro Tsujikawa 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sublicense, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be 15 * included in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25#ifdef __sgi 26# include <string.h> 27# define errx(exitcode, format, args...) \ 28 { \ 29 warnx(format, ##args); \ 30 exit(exitcode); \ 31 } 32# define warnx(format, args...) fprintf(stderr, format "\n", ##args) 33char *strndup(const char *s, size_t size); 34#endif 35 36#ifdef HAVE_CONFIG_H 37# include <config.h> 38#endif /* HAVE_CONFIG_H */ 39 40#include <sys/types.h> 41#ifdef HAVE_UNISTD_H 42# include <unistd.h> 43#endif /* HAVE_UNISTD_H */ 44#ifdef HAVE_SYS_SOCKET_H 45# include <sys/socket.h> 46#endif /* HAVE_SYS_SOCKET_H */ 47#ifdef HAVE_NETINET_IN_H 48# include <netinet/in.h> 49#endif /* HAVE_NETINET_IN_H */ 50#include <netinet/tcp.h> 51#ifndef __sgi 52# include <err.h> 53#endif 54#include <signal.h> 55#include <string.h> 56 57#include <openssl/ssl.h> 58#include <openssl/err.h> 59#include <openssl/conf.h> 60 61#include <event.h> 62#include <event2/event.h> 63#include <event2/bufferevent_ssl.h> 64#include <event2/dns.h> 65 66#include <nghttp2/nghttp2.h> 67 68#include "url-parser/url_parser.h" 69 70#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) 71 72typedef struct { 73 /* The NULL-terminated URI string to retrieve. */ 74 const char *uri; 75 /* Parsed result of the |uri| */ 76 struct http_parser_url *u; 77 /* The authority portion of the |uri|, not NULL-terminated */ 78 char *authority; 79 /* The path portion of the |uri|, including query, not 80 NULL-terminated */ 81 char *path; 82 /* The length of the |authority| */ 83 size_t authoritylen; 84 /* The length of the |path| */ 85 size_t pathlen; 86 /* The stream ID of this stream */ 87 int32_t stream_id; 88} http2_stream_data; 89 90typedef struct { 91 nghttp2_session *session; 92 struct evdns_base *dnsbase; 93 struct bufferevent *bev; 94 http2_stream_data *stream_data; 95} http2_session_data; 96 97static http2_stream_data *create_http2_stream_data(const char *uri, 98 struct http_parser_url *u) { 99 /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */ 100 size_t extra = 7; 101 http2_stream_data *stream_data = malloc(sizeof(http2_stream_data)); 102 103 stream_data->uri = uri; 104 stream_data->u = u; 105 stream_data->stream_id = -1; 106 107 stream_data->authoritylen = u->field_data[UF_HOST].len; 108 stream_data->authority = malloc(stream_data->authoritylen + extra); 109 memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off], 110 u->field_data[UF_HOST].len); 111 if (u->field_set & (1 << UF_PORT)) { 112 stream_data->authoritylen += 113 (size_t)snprintf(stream_data->authority + u->field_data[UF_HOST].len, 114 extra, ":%u", u->port); 115 } 116 117 /* If we don't have path in URI, we use "/" as path. */ 118 stream_data->pathlen = 1; 119 if (u->field_set & (1 << UF_PATH)) { 120 stream_data->pathlen = u->field_data[UF_PATH].len; 121 } 122 if (u->field_set & (1 << UF_QUERY)) { 123 /* +1 for '?' character */ 124 stream_data->pathlen += (size_t)(u->field_data[UF_QUERY].len + 1); 125 } 126 127 stream_data->path = malloc(stream_data->pathlen); 128 if (u->field_set & (1 << UF_PATH)) { 129 memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off], 130 u->field_data[UF_PATH].len); 131 } else { 132 stream_data->path[0] = '/'; 133 } 134 if (u->field_set & (1 << UF_QUERY)) { 135 stream_data->path[stream_data->pathlen - u->field_data[UF_QUERY].len - 1] = 136 '?'; 137 memcpy(stream_data->path + stream_data->pathlen - 138 u->field_data[UF_QUERY].len, 139 &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len); 140 } 141 142 return stream_data; 143} 144 145static void delete_http2_stream_data(http2_stream_data *stream_data) { 146 free(stream_data->path); 147 free(stream_data->authority); 148 free(stream_data); 149} 150 151/* Initializes |session_data| */ 152static http2_session_data * 153create_http2_session_data(struct event_base *evbase) { 154 http2_session_data *session_data = malloc(sizeof(http2_session_data)); 155 156 memset(session_data, 0, sizeof(http2_session_data)); 157 session_data->dnsbase = evdns_base_new(evbase, 1); 158 return session_data; 159} 160 161static void delete_http2_session_data(http2_session_data *session_data) { 162 SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); 163 164 if (ssl) { 165 SSL_shutdown(ssl); 166 } 167 bufferevent_free(session_data->bev); 168 session_data->bev = NULL; 169 evdns_base_free(session_data->dnsbase, 1); 170 session_data->dnsbase = NULL; 171 nghttp2_session_del(session_data->session); 172 session_data->session = NULL; 173 if (session_data->stream_data) { 174 delete_http2_stream_data(session_data->stream_data); 175 session_data->stream_data = NULL; 176 } 177 free(session_data); 178} 179 180static void print_header(FILE *f, const uint8_t *name, size_t namelen, 181 const uint8_t *value, size_t valuelen) { 182 fwrite(name, 1, namelen, f); 183 fprintf(f, ": "); 184 fwrite(value, 1, valuelen, f); 185 fprintf(f, "\n"); 186} 187 188/* Print HTTP headers to |f|. Please note that this function does not 189 take into account that header name and value are sequence of 190 octets, therefore they may contain non-printable characters. */ 191static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) { 192 size_t i; 193 for (i = 0; i < nvlen; ++i) { 194 print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen); 195 } 196 fprintf(f, "\n"); 197} 198 199/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes, 200 to the network. Because we are using libevent bufferevent, we just 201 write those bytes into bufferevent buffer. */ 202static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, 203 size_t length, int flags, void *user_data) { 204 http2_session_data *session_data = (http2_session_data *)user_data; 205 struct bufferevent *bev = session_data->bev; 206 (void)session; 207 (void)flags; 208 209 bufferevent_write(bev, data, length); 210 return (ssize_t)length; 211} 212 213/* nghttp2_on_header_callback: Called when nghttp2 library emits 214 single header name/value pair. */ 215static int on_header_callback(nghttp2_session *session, 216 const nghttp2_frame *frame, const uint8_t *name, 217 size_t namelen, const uint8_t *value, 218 size_t valuelen, uint8_t flags, void *user_data) { 219 http2_session_data *session_data = (http2_session_data *)user_data; 220 (void)session; 221 (void)flags; 222 223 switch (frame->hd.type) { 224 case NGHTTP2_HEADERS: 225 if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && 226 session_data->stream_data->stream_id == frame->hd.stream_id) { 227 /* Print response headers for the initiated request. */ 228 print_header(stderr, name, namelen, value, valuelen); 229 break; 230 } 231 } 232 return 0; 233} 234 235/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets 236 started to receive header block. */ 237static int on_begin_headers_callback(nghttp2_session *session, 238 const nghttp2_frame *frame, 239 void *user_data) { 240 http2_session_data *session_data = (http2_session_data *)user_data; 241 (void)session; 242 243 switch (frame->hd.type) { 244 case NGHTTP2_HEADERS: 245 if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && 246 session_data->stream_data->stream_id == frame->hd.stream_id) { 247 fprintf(stderr, "Response headers for stream ID=%d:\n", 248 frame->hd.stream_id); 249 } 250 break; 251 } 252 return 0; 253} 254 255/* nghttp2_on_frame_recv_callback: Called when nghttp2 library 256 received a complete frame from the remote peer. */ 257static int on_frame_recv_callback(nghttp2_session *session, 258 const nghttp2_frame *frame, void *user_data) { 259 http2_session_data *session_data = (http2_session_data *)user_data; 260 (void)session; 261 262 switch (frame->hd.type) { 263 case NGHTTP2_HEADERS: 264 if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && 265 session_data->stream_data->stream_id == frame->hd.stream_id) { 266 fprintf(stderr, "All headers received\n"); 267 } 268 break; 269 } 270 return 0; 271} 272 273/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is 274 received from the remote peer. In this implementation, if the frame 275 is meant to the stream we initiated, print the received data in 276 stdout, so that the user can redirect its output to the file 277 easily. */ 278static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, 279 int32_t stream_id, const uint8_t *data, 280 size_t len, void *user_data) { 281 http2_session_data *session_data = (http2_session_data *)user_data; 282 (void)session; 283 (void)flags; 284 285 if (session_data->stream_data->stream_id == stream_id) { 286 fwrite(data, 1, len, stdout); 287 } 288 return 0; 289} 290 291/* nghttp2_on_stream_close_callback: Called when a stream is about to 292 closed. This example program only deals with 1 HTTP request (1 293 stream), if it is closed, we send GOAWAY and tear down the 294 session */ 295static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, 296 uint32_t error_code, void *user_data) { 297 http2_session_data *session_data = (http2_session_data *)user_data; 298 int rv; 299 300 if (session_data->stream_data->stream_id == stream_id) { 301 fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id, 302 error_code); 303 rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); 304 if (rv != 0) { 305 return NGHTTP2_ERR_CALLBACK_FAILURE; 306 } 307 } 308 return 0; 309} 310 311#ifndef OPENSSL_NO_NEXTPROTONEG 312/* NPN TLS extension client callback. We check that server advertised 313 the HTTP/2 protocol the nghttp2 library supports. If not, exit 314 the program. */ 315static int select_next_proto_cb(SSL *ssl, unsigned char **out, 316 unsigned char *outlen, const unsigned char *in, 317 unsigned int inlen, void *arg) { 318 (void)ssl; 319 (void)arg; 320 321 if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { 322 errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); 323 } 324 return SSL_TLSEXT_ERR_OK; 325} 326#endif /* !OPENSSL_NO_NEXTPROTONEG */ 327 328/* Create SSL_CTX. */ 329static SSL_CTX *create_ssl_ctx(void) { 330 SSL_CTX *ssl_ctx; 331 ssl_ctx = SSL_CTX_new(TLS_client_method()); 332 if (!ssl_ctx) { 333 errx(1, "Could not create SSL/TLS context: %s", 334 ERR_error_string(ERR_get_error(), NULL)); 335 } 336 SSL_CTX_set_options(ssl_ctx, 337 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | 338 SSL_OP_NO_COMPRESSION | 339 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); 340#ifndef OPENSSL_NO_NEXTPROTONEG 341 SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); 342#endif /* !OPENSSL_NO_NEXTPROTONEG */ 343 344#if OPENSSL_VERSION_NUMBER >= 0x10002000L 345 SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); 346#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ 347 348 return ssl_ctx; 349} 350 351/* Create SSL object */ 352static SSL *create_ssl(SSL_CTX *ssl_ctx) { 353 SSL *ssl; 354 ssl = SSL_new(ssl_ctx); 355 if (!ssl) { 356 errx(1, "Could not create SSL/TLS session object: %s", 357 ERR_error_string(ERR_get_error(), NULL)); 358 } 359 return ssl; 360} 361 362static void initialize_nghttp2_session(http2_session_data *session_data) { 363 nghttp2_session_callbacks *callbacks; 364 365 nghttp2_session_callbacks_new(&callbacks); 366 367 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); 368 369 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, 370 on_frame_recv_callback); 371 372 nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 373 callbacks, on_data_chunk_recv_callback); 374 375 nghttp2_session_callbacks_set_on_stream_close_callback( 376 callbacks, on_stream_close_callback); 377 378 nghttp2_session_callbacks_set_on_header_callback(callbacks, 379 on_header_callback); 380 381 nghttp2_session_callbacks_set_on_begin_headers_callback( 382 callbacks, on_begin_headers_callback); 383 384 nghttp2_session_client_new(&session_data->session, callbacks, session_data); 385 386 nghttp2_session_callbacks_del(callbacks); 387} 388 389static void send_client_connection_header(http2_session_data *session_data) { 390 nghttp2_settings_entry iv[1] = { 391 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; 392 int rv; 393 394 /* client 24 bytes magic string will be sent by nghttp2 library */ 395 rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, 396 ARRLEN(iv)); 397 if (rv != 0) { 398 errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); 399 } 400} 401 402#define MAKE_NV(NAME, VALUE, VALUELEN) \ 403 { \ 404 (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \ 405 NGHTTP2_NV_FLAG_NONE \ 406 } 407 408#define MAKE_NV2(NAME, VALUE) \ 409 { \ 410 (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ 411 NGHTTP2_NV_FLAG_NONE \ 412 } 413 414/* Send HTTP request to the remote peer */ 415static void submit_request(http2_session_data *session_data) { 416 int32_t stream_id; 417 http2_stream_data *stream_data = session_data->stream_data; 418 const char *uri = stream_data->uri; 419 const struct http_parser_url *u = stream_data->u; 420 nghttp2_nv hdrs[] = { 421 MAKE_NV2(":method", "GET"), 422 MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], 423 u->field_data[UF_SCHEMA].len), 424 MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), 425 MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; 426 fprintf(stderr, "Request headers:\n"); 427 print_headers(stderr, hdrs, ARRLEN(hdrs)); 428 stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, 429 ARRLEN(hdrs), NULL, stream_data); 430 if (stream_id < 0) { 431 errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); 432 } 433 434 stream_data->stream_id = stream_id; 435} 436 437/* Serialize the frame and send (or buffer) the data to 438 bufferevent. */ 439static int session_send(http2_session_data *session_data) { 440 int rv; 441 442 rv = nghttp2_session_send(session_data->session); 443 if (rv != 0) { 444 warnx("Fatal error: %s", nghttp2_strerror(rv)); 445 return -1; 446 } 447 return 0; 448} 449 450/* readcb for bufferevent. Here we get the data from the input buffer 451 of bufferevent and feed them to nghttp2 library. This may invoke 452 nghttp2 callbacks. It may also queues the frame in nghttp2 session 453 context. To send them, we call session_send() in the end. */ 454static void readcb(struct bufferevent *bev, void *ptr) { 455 http2_session_data *session_data = (http2_session_data *)ptr; 456 ssize_t readlen; 457 struct evbuffer *input = bufferevent_get_input(bev); 458 size_t datalen = evbuffer_get_length(input); 459 unsigned char *data = evbuffer_pullup(input, -1); 460 461 readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); 462 if (readlen < 0) { 463 warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); 464 delete_http2_session_data(session_data); 465 return; 466 } 467 if (evbuffer_drain(input, (size_t)readlen) != 0) { 468 warnx("Fatal error: evbuffer_drain failed"); 469 delete_http2_session_data(session_data); 470 return; 471 } 472 if (session_send(session_data) != 0) { 473 delete_http2_session_data(session_data); 474 return; 475 } 476} 477 478/* writecb for bufferevent. To greaceful shutdown after sending or 479 receiving GOAWAY, we check the some conditions on the nghttp2 480 library and output buffer of bufferevent. If it indicates we have 481 no business to this session, tear down the connection. */ 482static void writecb(struct bufferevent *bev, void *ptr) { 483 http2_session_data *session_data = (http2_session_data *)ptr; 484 (void)bev; 485 486 if (nghttp2_session_want_read(session_data->session) == 0 && 487 nghttp2_session_want_write(session_data->session) == 0 && 488 evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { 489 delete_http2_session_data(session_data); 490 } 491} 492 493/* eventcb for bufferevent. For the purpose of simplicity and 494 readability of the example program, we omitted the certificate and 495 peer verification. After SSL/TLS handshake is over, initialize 496 nghttp2 library session, and send client connection header. Then 497 send HTTP request. */ 498static void eventcb(struct bufferevent *bev, short events, void *ptr) { 499 http2_session_data *session_data = (http2_session_data *)ptr; 500 if (events & BEV_EVENT_CONNECTED) { 501 int fd = bufferevent_getfd(bev); 502 int val = 1; 503 const unsigned char *alpn = NULL; 504 unsigned int alpnlen = 0; 505 SSL *ssl; 506 507 fprintf(stderr, "Connected\n"); 508 509 ssl = bufferevent_openssl_get_ssl(session_data->bev); 510 511#ifndef OPENSSL_NO_NEXTPROTONEG 512 SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen); 513#endif /* !OPENSSL_NO_NEXTPROTONEG */ 514#if OPENSSL_VERSION_NUMBER >= 0x10002000L 515 if (alpn == NULL) { 516 SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); 517 } 518#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ 519 520 if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { 521 fprintf(stderr, "h2 is not negotiated\n"); 522 delete_http2_session_data(session_data); 523 return; 524 } 525 526 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); 527 initialize_nghttp2_session(session_data); 528 send_client_connection_header(session_data); 529 submit_request(session_data); 530 if (session_send(session_data) != 0) { 531 delete_http2_session_data(session_data); 532 } 533 return; 534 } 535 if (events & BEV_EVENT_EOF) { 536 warnx("Disconnected from the remote host"); 537 } else if (events & BEV_EVENT_ERROR) { 538 warnx("Network error"); 539 } else if (events & BEV_EVENT_TIMEOUT) { 540 warnx("Timeout"); 541 } 542 delete_http2_session_data(session_data); 543} 544 545/* Start connecting to the remote peer |host:port| */ 546static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, 547 const char *host, uint16_t port, 548 http2_session_data *session_data) { 549 int rv; 550 struct bufferevent *bev; 551 SSL *ssl; 552 553 ssl = create_ssl(ssl_ctx); 554 bev = bufferevent_openssl_socket_new( 555 evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, 556 BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); 557 bufferevent_enable(bev, EV_READ | EV_WRITE); 558 bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); 559 rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, 560 AF_UNSPEC, host, port); 561 562 if (rv != 0) { 563 errx(1, "Could not connect to the remote host %s", host); 564 } 565 session_data->bev = bev; 566} 567 568/* Get resource denoted by the |uri|. The debug and error messages are 569 printed in stderr, while the response body is printed in stdout. */ 570static void run(const char *uri) { 571 struct http_parser_url u; 572 char *host; 573 uint16_t port; 574 int rv; 575 SSL_CTX *ssl_ctx; 576 struct event_base *evbase; 577 http2_session_data *session_data; 578 579 /* Parse the |uri| and stores its components in |u| */ 580 rv = http_parser_parse_url(uri, strlen(uri), 0, &u); 581 if (rv != 0) { 582 errx(1, "Could not parse URI %s", uri); 583 } 584 host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); 585 if (!(u.field_set & (1 << UF_PORT))) { 586 port = 443; 587 } else { 588 port = u.port; 589 } 590 591 ssl_ctx = create_ssl_ctx(); 592 593 evbase = event_base_new(); 594 595 session_data = create_http2_session_data(evbase); 596 session_data->stream_data = create_http2_stream_data(uri, &u); 597 598 initiate_connection(evbase, ssl_ctx, host, port, session_data); 599 free(host); 600 host = NULL; 601 602 event_base_loop(evbase, 0); 603 604 event_base_free(evbase); 605 SSL_CTX_free(ssl_ctx); 606} 607 608int main(int argc, char **argv) { 609 struct sigaction act; 610 611 if (argc < 2) { 612 fprintf(stderr, "Usage: libevent-client HTTPS_URI\n"); 613 exit(EXIT_FAILURE); 614 } 615 616 memset(&act, 0, sizeof(struct sigaction)); 617 act.sa_handler = SIG_IGN; 618 sigaction(SIGPIPE, &act, NULL); 619 620#if OPENSSL_VERSION_NUMBER >= 0x1010000fL 621 /* No explicit initialization is required. */ 622#elif defined(OPENSSL_IS_BORINGSSL) 623 CRYPTO_library_init(); 624#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \ 625 !defined(OPENSSL_IS_BORINGSSL) */ 626 OPENSSL_config(NULL); 627 SSL_load_error_strings(); 628 SSL_library_init(); 629 OpenSSL_add_all_algorithms(); 630#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \ 631 !defined(OPENSSL_IS_BORINGSSL) */ 632 633 run(argv[1]); 634 return 0; 635} 636