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