12c593315Sopenharmony_ci/*
22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library
32c593315Sopenharmony_ci *
42c593315Sopenharmony_ci * Copyright (c) 2013 Tatsuhiro Tsujikawa
52c593315Sopenharmony_ci *
62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining
72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the
82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to
112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
122c593315Sopenharmony_ci * the following conditions:
132c593315Sopenharmony_ci *
142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be
152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software.
162c593315Sopenharmony_ci *
172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
242c593315Sopenharmony_ci */
252c593315Sopenharmony_ci#ifdef __sgi
262c593315Sopenharmony_ci#  define errx(exitcode, format, args...)                                      \
272c593315Sopenharmony_ci    {                                                                          \
282c593315Sopenharmony_ci      warnx(format, ##args);                                                   \
292c593315Sopenharmony_ci      exit(exitcode);                                                          \
302c593315Sopenharmony_ci    }
312c593315Sopenharmony_ci#  define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
322c593315Sopenharmony_ci#  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
332c593315Sopenharmony_ci#endif
342c593315Sopenharmony_ci
352c593315Sopenharmony_ci#ifdef HAVE_CONFIG_H
362c593315Sopenharmony_ci#  include <config.h>
372c593315Sopenharmony_ci#endif /* HAVE_CONFIG_H */
382c593315Sopenharmony_ci
392c593315Sopenharmony_ci#include <sys/types.h>
402c593315Sopenharmony_ci#ifdef HAVE_SYS_SOCKET_H
412c593315Sopenharmony_ci#  include <sys/socket.h>
422c593315Sopenharmony_ci#endif /* HAVE_SYS_SOCKET_H */
432c593315Sopenharmony_ci#ifdef HAVE_NETDB_H
442c593315Sopenharmony_ci#  include <netdb.h>
452c593315Sopenharmony_ci#endif /* HAVE_NETDB_H */
462c593315Sopenharmony_ci#include <signal.h>
472c593315Sopenharmony_ci#ifdef HAVE_UNISTD_H
482c593315Sopenharmony_ci#  include <unistd.h>
492c593315Sopenharmony_ci#endif /* HAVE_UNISTD_H */
502c593315Sopenharmony_ci#include <sys/stat.h>
512c593315Sopenharmony_ci#ifdef HAVE_FCNTL_H
522c593315Sopenharmony_ci#  include <fcntl.h>
532c593315Sopenharmony_ci#endif /* HAVE_FCNTL_H */
542c593315Sopenharmony_ci#include <ctype.h>
552c593315Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
562c593315Sopenharmony_ci#  include <netinet/in.h>
572c593315Sopenharmony_ci#endif /* HAVE_NETINET_IN_H */
582c593315Sopenharmony_ci#include <netinet/tcp.h>
592c593315Sopenharmony_ci#ifndef __sgi
602c593315Sopenharmony_ci#  include <err.h>
612c593315Sopenharmony_ci#endif
622c593315Sopenharmony_ci#include <string.h>
632c593315Sopenharmony_ci#include <errno.h>
642c593315Sopenharmony_ci
652c593315Sopenharmony_ci#include <openssl/ssl.h>
662c593315Sopenharmony_ci#include <openssl/err.h>
672c593315Sopenharmony_ci#include <openssl/conf.h>
682c593315Sopenharmony_ci
692c593315Sopenharmony_ci#include <event.h>
702c593315Sopenharmony_ci#include <event2/event.h>
712c593315Sopenharmony_ci#include <event2/bufferevent_ssl.h>
722c593315Sopenharmony_ci#include <event2/listener.h>
732c593315Sopenharmony_ci
742c593315Sopenharmony_ci#include <nghttp2/nghttp2.h>
752c593315Sopenharmony_ci
762c593315Sopenharmony_ci#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
772c593315Sopenharmony_ci
782c593315Sopenharmony_ci#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
792c593315Sopenharmony_ci
802c593315Sopenharmony_ci#define MAKE_NV(NAME, VALUE)                                                   \
812c593315Sopenharmony_ci  {                                                                            \
822c593315Sopenharmony_ci    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,    \
832c593315Sopenharmony_ci        NGHTTP2_NV_FLAG_NONE                                                   \
842c593315Sopenharmony_ci  }
852c593315Sopenharmony_ci
862c593315Sopenharmony_cistruct app_context;
872c593315Sopenharmony_citypedef struct app_context app_context;
882c593315Sopenharmony_ci
892c593315Sopenharmony_citypedef struct http2_stream_data {
902c593315Sopenharmony_ci  struct http2_stream_data *prev, *next;
912c593315Sopenharmony_ci  char *request_path;
922c593315Sopenharmony_ci  int32_t stream_id;
932c593315Sopenharmony_ci  int fd;
942c593315Sopenharmony_ci} http2_stream_data;
952c593315Sopenharmony_ci
962c593315Sopenharmony_citypedef struct http2_session_data {
972c593315Sopenharmony_ci  struct http2_stream_data root;
982c593315Sopenharmony_ci  struct bufferevent *bev;
992c593315Sopenharmony_ci  app_context *app_ctx;
1002c593315Sopenharmony_ci  nghttp2_session *session;
1012c593315Sopenharmony_ci  char *client_addr;
1022c593315Sopenharmony_ci} http2_session_data;
1032c593315Sopenharmony_ci
1042c593315Sopenharmony_cistruct app_context {
1052c593315Sopenharmony_ci  SSL_CTX *ssl_ctx;
1062c593315Sopenharmony_ci  struct event_base *evbase;
1072c593315Sopenharmony_ci};
1082c593315Sopenharmony_ci
1092c593315Sopenharmony_cistatic unsigned char next_proto_list[256];
1102c593315Sopenharmony_cistatic size_t next_proto_list_len;
1112c593315Sopenharmony_ci
1122c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG
1132c593315Sopenharmony_cistatic int next_proto_cb(SSL *ssl, const unsigned char **data,
1142c593315Sopenharmony_ci                         unsigned int *len, void *arg) {
1152c593315Sopenharmony_ci  (void)ssl;
1162c593315Sopenharmony_ci  (void)arg;
1172c593315Sopenharmony_ci
1182c593315Sopenharmony_ci  *data = next_proto_list;
1192c593315Sopenharmony_ci  *len = (unsigned int)next_proto_list_len;
1202c593315Sopenharmony_ci  return SSL_TLSEXT_ERR_OK;
1212c593315Sopenharmony_ci}
1222c593315Sopenharmony_ci#endif /* !OPENSSL_NO_NEXTPROTONEG */
1232c593315Sopenharmony_ci
1242c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L
1252c593315Sopenharmony_cistatic int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
1262c593315Sopenharmony_ci                                unsigned char *outlen, const unsigned char *in,
1272c593315Sopenharmony_ci                                unsigned int inlen, void *arg) {
1282c593315Sopenharmony_ci  int rv;
1292c593315Sopenharmony_ci  (void)ssl;
1302c593315Sopenharmony_ci  (void)arg;
1312c593315Sopenharmony_ci
1322c593315Sopenharmony_ci  rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
1332c593315Sopenharmony_ci
1342c593315Sopenharmony_ci  if (rv != 1) {
1352c593315Sopenharmony_ci    return SSL_TLSEXT_ERR_NOACK;
1362c593315Sopenharmony_ci  }
1372c593315Sopenharmony_ci
1382c593315Sopenharmony_ci  return SSL_TLSEXT_ERR_OK;
1392c593315Sopenharmony_ci}
1402c593315Sopenharmony_ci#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
1412c593315Sopenharmony_ci
1422c593315Sopenharmony_ci/* Create SSL_CTX. */
1432c593315Sopenharmony_cistatic SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
1442c593315Sopenharmony_ci  SSL_CTX *ssl_ctx;
1452c593315Sopenharmony_ci
1462c593315Sopenharmony_ci  ssl_ctx = SSL_CTX_new(TLS_server_method());
1472c593315Sopenharmony_ci  if (!ssl_ctx) {
1482c593315Sopenharmony_ci    errx(1, "Could not create SSL/TLS context: %s",
1492c593315Sopenharmony_ci         ERR_error_string(ERR_get_error(), NULL));
1502c593315Sopenharmony_ci  }
1512c593315Sopenharmony_ci  SSL_CTX_set_options(ssl_ctx,
1522c593315Sopenharmony_ci                      SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
1532c593315Sopenharmony_ci                          SSL_OP_NO_COMPRESSION |
1542c593315Sopenharmony_ci                          SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
1552c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1562c593315Sopenharmony_ci  if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
1572c593315Sopenharmony_ci    errx(1, "SSL_CTX_set1_curves_list failed: %s",
1582c593315Sopenharmony_ci         ERR_error_string(ERR_get_error(), NULL));
1592c593315Sopenharmony_ci  }
1602c593315Sopenharmony_ci#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
1612c593315Sopenharmony_ci  {
1622c593315Sopenharmony_ci    EC_KEY *ecdh;
1632c593315Sopenharmony_ci    ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
1642c593315Sopenharmony_ci    if (!ecdh) {
1652c593315Sopenharmony_ci      errx(1, "EC_KEY_new_by_curv_name failed: %s",
1662c593315Sopenharmony_ci           ERR_error_string(ERR_get_error(), NULL));
1672c593315Sopenharmony_ci    }
1682c593315Sopenharmony_ci    SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
1692c593315Sopenharmony_ci    EC_KEY_free(ecdh);
1702c593315Sopenharmony_ci  }
1712c593315Sopenharmony_ci#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
1722c593315Sopenharmony_ci
1732c593315Sopenharmony_ci  if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
1742c593315Sopenharmony_ci    errx(1, "Could not read private key file %s", key_file);
1752c593315Sopenharmony_ci  }
1762c593315Sopenharmony_ci  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
1772c593315Sopenharmony_ci    errx(1, "Could not read certificate file %s", cert_file);
1782c593315Sopenharmony_ci  }
1792c593315Sopenharmony_ci
1802c593315Sopenharmony_ci  next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
1812c593315Sopenharmony_ci  memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
1822c593315Sopenharmony_ci         NGHTTP2_PROTO_VERSION_ID_LEN);
1832c593315Sopenharmony_ci  next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
1842c593315Sopenharmony_ci
1852c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG
1862c593315Sopenharmony_ci  SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
1872c593315Sopenharmony_ci#endif /* !OPENSSL_NO_NEXTPROTONEG */
1882c593315Sopenharmony_ci
1892c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L
1902c593315Sopenharmony_ci  SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
1912c593315Sopenharmony_ci#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
1922c593315Sopenharmony_ci
1932c593315Sopenharmony_ci  return ssl_ctx;
1942c593315Sopenharmony_ci}
1952c593315Sopenharmony_ci
1962c593315Sopenharmony_ci/* Create SSL object */
1972c593315Sopenharmony_cistatic SSL *create_ssl(SSL_CTX *ssl_ctx) {
1982c593315Sopenharmony_ci  SSL *ssl;
1992c593315Sopenharmony_ci  ssl = SSL_new(ssl_ctx);
2002c593315Sopenharmony_ci  if (!ssl) {
2012c593315Sopenharmony_ci    errx(1, "Could not create SSL/TLS session object: %s",
2022c593315Sopenharmony_ci         ERR_error_string(ERR_get_error(), NULL));
2032c593315Sopenharmony_ci  }
2042c593315Sopenharmony_ci  return ssl;
2052c593315Sopenharmony_ci}
2062c593315Sopenharmony_ci
2072c593315Sopenharmony_cistatic void add_stream(http2_session_data *session_data,
2082c593315Sopenharmony_ci                       http2_stream_data *stream_data) {
2092c593315Sopenharmony_ci  stream_data->next = session_data->root.next;
2102c593315Sopenharmony_ci  session_data->root.next = stream_data;
2112c593315Sopenharmony_ci  stream_data->prev = &session_data->root;
2122c593315Sopenharmony_ci  if (stream_data->next) {
2132c593315Sopenharmony_ci    stream_data->next->prev = stream_data;
2142c593315Sopenharmony_ci  }
2152c593315Sopenharmony_ci}
2162c593315Sopenharmony_ci
2172c593315Sopenharmony_cistatic void remove_stream(http2_session_data *session_data,
2182c593315Sopenharmony_ci                          http2_stream_data *stream_data) {
2192c593315Sopenharmony_ci  (void)session_data;
2202c593315Sopenharmony_ci
2212c593315Sopenharmony_ci  stream_data->prev->next = stream_data->next;
2222c593315Sopenharmony_ci  if (stream_data->next) {
2232c593315Sopenharmony_ci    stream_data->next->prev = stream_data->prev;
2242c593315Sopenharmony_ci  }
2252c593315Sopenharmony_ci}
2262c593315Sopenharmony_ci
2272c593315Sopenharmony_cistatic http2_stream_data *
2282c593315Sopenharmony_cicreate_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
2292c593315Sopenharmony_ci  http2_stream_data *stream_data;
2302c593315Sopenharmony_ci  stream_data = malloc(sizeof(http2_stream_data));
2312c593315Sopenharmony_ci  memset(stream_data, 0, sizeof(http2_stream_data));
2322c593315Sopenharmony_ci  stream_data->stream_id = stream_id;
2332c593315Sopenharmony_ci  stream_data->fd = -1;
2342c593315Sopenharmony_ci
2352c593315Sopenharmony_ci  add_stream(session_data, stream_data);
2362c593315Sopenharmony_ci  return stream_data;
2372c593315Sopenharmony_ci}
2382c593315Sopenharmony_ci
2392c593315Sopenharmony_cistatic void delete_http2_stream_data(http2_stream_data *stream_data) {
2402c593315Sopenharmony_ci  if (stream_data->fd != -1) {
2412c593315Sopenharmony_ci    close(stream_data->fd);
2422c593315Sopenharmony_ci  }
2432c593315Sopenharmony_ci  free(stream_data->request_path);
2442c593315Sopenharmony_ci  free(stream_data);
2452c593315Sopenharmony_ci}
2462c593315Sopenharmony_ci
2472c593315Sopenharmony_cistatic http2_session_data *create_http2_session_data(app_context *app_ctx,
2482c593315Sopenharmony_ci                                                     int fd,
2492c593315Sopenharmony_ci                                                     struct sockaddr *addr,
2502c593315Sopenharmony_ci                                                     int addrlen) {
2512c593315Sopenharmony_ci  int rv;
2522c593315Sopenharmony_ci  http2_session_data *session_data;
2532c593315Sopenharmony_ci  SSL *ssl;
2542c593315Sopenharmony_ci  char host[NI_MAXHOST];
2552c593315Sopenharmony_ci  int val = 1;
2562c593315Sopenharmony_ci
2572c593315Sopenharmony_ci  ssl = create_ssl(app_ctx->ssl_ctx);
2582c593315Sopenharmony_ci  session_data = malloc(sizeof(http2_session_data));
2592c593315Sopenharmony_ci  memset(session_data, 0, sizeof(http2_session_data));
2602c593315Sopenharmony_ci  session_data->app_ctx = app_ctx;
2612c593315Sopenharmony_ci  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
2622c593315Sopenharmony_ci  session_data->bev = bufferevent_openssl_socket_new(
2632c593315Sopenharmony_ci      app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
2642c593315Sopenharmony_ci      BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
2652c593315Sopenharmony_ci  bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
2662c593315Sopenharmony_ci  rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
2672c593315Sopenharmony_ci                   NI_NUMERICHOST);
2682c593315Sopenharmony_ci  if (rv != 0) {
2692c593315Sopenharmony_ci    session_data->client_addr = strdup("(unknown)");
2702c593315Sopenharmony_ci  } else {
2712c593315Sopenharmony_ci    session_data->client_addr = strdup(host);
2722c593315Sopenharmony_ci  }
2732c593315Sopenharmony_ci
2742c593315Sopenharmony_ci  return session_data;
2752c593315Sopenharmony_ci}
2762c593315Sopenharmony_ci
2772c593315Sopenharmony_cistatic void delete_http2_session_data(http2_session_data *session_data) {
2782c593315Sopenharmony_ci  http2_stream_data *stream_data;
2792c593315Sopenharmony_ci  SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
2802c593315Sopenharmony_ci  fprintf(stderr, "%s disconnected\n", session_data->client_addr);
2812c593315Sopenharmony_ci  if (ssl) {
2822c593315Sopenharmony_ci    SSL_shutdown(ssl);
2832c593315Sopenharmony_ci  }
2842c593315Sopenharmony_ci  bufferevent_free(session_data->bev);
2852c593315Sopenharmony_ci  nghttp2_session_del(session_data->session);
2862c593315Sopenharmony_ci  for (stream_data = session_data->root.next; stream_data;) {
2872c593315Sopenharmony_ci    http2_stream_data *next = stream_data->next;
2882c593315Sopenharmony_ci    delete_http2_stream_data(stream_data);
2892c593315Sopenharmony_ci    stream_data = next;
2902c593315Sopenharmony_ci  }
2912c593315Sopenharmony_ci  free(session_data->client_addr);
2922c593315Sopenharmony_ci  free(session_data);
2932c593315Sopenharmony_ci}
2942c593315Sopenharmony_ci
2952c593315Sopenharmony_ci/* Serialize the frame and send (or buffer) the data to
2962c593315Sopenharmony_ci   bufferevent. */
2972c593315Sopenharmony_cistatic int session_send(http2_session_data *session_data) {
2982c593315Sopenharmony_ci  int rv;
2992c593315Sopenharmony_ci  rv = nghttp2_session_send(session_data->session);
3002c593315Sopenharmony_ci  if (rv != 0) {
3012c593315Sopenharmony_ci    warnx("Fatal error: %s", nghttp2_strerror(rv));
3022c593315Sopenharmony_ci    return -1;
3032c593315Sopenharmony_ci  }
3042c593315Sopenharmony_ci  return 0;
3052c593315Sopenharmony_ci}
3062c593315Sopenharmony_ci
3072c593315Sopenharmony_ci/* Read the data in the bufferevent and feed them into nghttp2 library
3082c593315Sopenharmony_ci   function. Invocation of nghttp2_session_mem_recv() may make
3092c593315Sopenharmony_ci   additional pending frames, so call session_send() at the end of the
3102c593315Sopenharmony_ci   function. */
3112c593315Sopenharmony_cistatic int session_recv(http2_session_data *session_data) {
3122c593315Sopenharmony_ci  ssize_t readlen;
3132c593315Sopenharmony_ci  struct evbuffer *input = bufferevent_get_input(session_data->bev);
3142c593315Sopenharmony_ci  size_t datalen = evbuffer_get_length(input);
3152c593315Sopenharmony_ci  unsigned char *data = evbuffer_pullup(input, -1);
3162c593315Sopenharmony_ci
3172c593315Sopenharmony_ci  readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
3182c593315Sopenharmony_ci  if (readlen < 0) {
3192c593315Sopenharmony_ci    warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
3202c593315Sopenharmony_ci    return -1;
3212c593315Sopenharmony_ci  }
3222c593315Sopenharmony_ci  if (evbuffer_drain(input, (size_t)readlen) != 0) {
3232c593315Sopenharmony_ci    warnx("Fatal error: evbuffer_drain failed");
3242c593315Sopenharmony_ci    return -1;
3252c593315Sopenharmony_ci  }
3262c593315Sopenharmony_ci  if (session_send(session_data) != 0) {
3272c593315Sopenharmony_ci    return -1;
3282c593315Sopenharmony_ci  }
3292c593315Sopenharmony_ci  return 0;
3302c593315Sopenharmony_ci}
3312c593315Sopenharmony_ci
3322c593315Sopenharmony_cistatic ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
3332c593315Sopenharmony_ci                             size_t length, int flags, void *user_data) {
3342c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)user_data;
3352c593315Sopenharmony_ci  struct bufferevent *bev = session_data->bev;
3362c593315Sopenharmony_ci  (void)session;
3372c593315Sopenharmony_ci  (void)flags;
3382c593315Sopenharmony_ci
3392c593315Sopenharmony_ci  /* Avoid excessive buffering in server side. */
3402c593315Sopenharmony_ci  if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
3412c593315Sopenharmony_ci      OUTPUT_WOULDBLOCK_THRESHOLD) {
3422c593315Sopenharmony_ci    return NGHTTP2_ERR_WOULDBLOCK;
3432c593315Sopenharmony_ci  }
3442c593315Sopenharmony_ci  bufferevent_write(bev, data, length);
3452c593315Sopenharmony_ci  return (ssize_t)length;
3462c593315Sopenharmony_ci}
3472c593315Sopenharmony_ci
3482c593315Sopenharmony_ci/* Returns nonzero if the string |s| ends with the substring |sub| */
3492c593315Sopenharmony_cistatic int ends_with(const char *s, const char *sub) {
3502c593315Sopenharmony_ci  size_t slen = strlen(s);
3512c593315Sopenharmony_ci  size_t sublen = strlen(sub);
3522c593315Sopenharmony_ci  if (slen < sublen) {
3532c593315Sopenharmony_ci    return 0;
3542c593315Sopenharmony_ci  }
3552c593315Sopenharmony_ci  return memcmp(s + slen - sublen, sub, sublen) == 0;
3562c593315Sopenharmony_ci}
3572c593315Sopenharmony_ci
3582c593315Sopenharmony_ci/* Returns int value of hex string character |c| */
3592c593315Sopenharmony_cistatic uint8_t hex_to_uint(uint8_t c) {
3602c593315Sopenharmony_ci  if ('0' <= c && c <= '9') {
3612c593315Sopenharmony_ci    return (uint8_t)(c - '0');
3622c593315Sopenharmony_ci  }
3632c593315Sopenharmony_ci  if ('A' <= c && c <= 'F') {
3642c593315Sopenharmony_ci    return (uint8_t)(c - 'A' + 10);
3652c593315Sopenharmony_ci  }
3662c593315Sopenharmony_ci  if ('a' <= c && c <= 'f') {
3672c593315Sopenharmony_ci    return (uint8_t)(c - 'a' + 10);
3682c593315Sopenharmony_ci  }
3692c593315Sopenharmony_ci  return 0;
3702c593315Sopenharmony_ci}
3712c593315Sopenharmony_ci
3722c593315Sopenharmony_ci/* Decodes percent-encoded byte string |value| with length |valuelen|
3732c593315Sopenharmony_ci   and returns the decoded byte string in allocated buffer. The return
3742c593315Sopenharmony_ci   value is NULL terminated. The caller must free the returned
3752c593315Sopenharmony_ci   string. */
3762c593315Sopenharmony_cistatic char *percent_decode(const uint8_t *value, size_t valuelen) {
3772c593315Sopenharmony_ci  char *res;
3782c593315Sopenharmony_ci
3792c593315Sopenharmony_ci  res = malloc(valuelen + 1);
3802c593315Sopenharmony_ci  if (valuelen > 3) {
3812c593315Sopenharmony_ci    size_t i, j;
3822c593315Sopenharmony_ci    for (i = 0, j = 0; i < valuelen - 2;) {
3832c593315Sopenharmony_ci      if (value[i] != '%' || !isxdigit(value[i + 1]) ||
3842c593315Sopenharmony_ci          !isxdigit(value[i + 2])) {
3852c593315Sopenharmony_ci        res[j++] = (char)value[i++];
3862c593315Sopenharmony_ci        continue;
3872c593315Sopenharmony_ci      }
3882c593315Sopenharmony_ci      res[j++] =
3892c593315Sopenharmony_ci          (char)((hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]));
3902c593315Sopenharmony_ci      i += 3;
3912c593315Sopenharmony_ci    }
3922c593315Sopenharmony_ci    memcpy(&res[j], &value[i], 2);
3932c593315Sopenharmony_ci    res[j + 2] = '\0';
3942c593315Sopenharmony_ci  } else {
3952c593315Sopenharmony_ci    memcpy(res, value, valuelen);
3962c593315Sopenharmony_ci    res[valuelen] = '\0';
3972c593315Sopenharmony_ci  }
3982c593315Sopenharmony_ci  return res;
3992c593315Sopenharmony_ci}
4002c593315Sopenharmony_ci
4012c593315Sopenharmony_cistatic ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
4022c593315Sopenharmony_ci                                  uint8_t *buf, size_t length,
4032c593315Sopenharmony_ci                                  uint32_t *data_flags,
4042c593315Sopenharmony_ci                                  nghttp2_data_source *source,
4052c593315Sopenharmony_ci                                  void *user_data) {
4062c593315Sopenharmony_ci  int fd = source->fd;
4072c593315Sopenharmony_ci  ssize_t r;
4082c593315Sopenharmony_ci  (void)session;
4092c593315Sopenharmony_ci  (void)stream_id;
4102c593315Sopenharmony_ci  (void)user_data;
4112c593315Sopenharmony_ci
4122c593315Sopenharmony_ci  while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
4132c593315Sopenharmony_ci    ;
4142c593315Sopenharmony_ci  if (r == -1) {
4152c593315Sopenharmony_ci    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
4162c593315Sopenharmony_ci  }
4172c593315Sopenharmony_ci  if (r == 0) {
4182c593315Sopenharmony_ci    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
4192c593315Sopenharmony_ci  }
4202c593315Sopenharmony_ci  return r;
4212c593315Sopenharmony_ci}
4222c593315Sopenharmony_ci
4232c593315Sopenharmony_cistatic int send_response(nghttp2_session *session, int32_t stream_id,
4242c593315Sopenharmony_ci                         nghttp2_nv *nva, size_t nvlen, int fd) {
4252c593315Sopenharmony_ci  int rv;
4262c593315Sopenharmony_ci  nghttp2_data_provider data_prd;
4272c593315Sopenharmony_ci  data_prd.source.fd = fd;
4282c593315Sopenharmony_ci  data_prd.read_callback = file_read_callback;
4292c593315Sopenharmony_ci
4302c593315Sopenharmony_ci  rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
4312c593315Sopenharmony_ci  if (rv != 0) {
4322c593315Sopenharmony_ci    warnx("Fatal error: %s", nghttp2_strerror(rv));
4332c593315Sopenharmony_ci    return -1;
4342c593315Sopenharmony_ci  }
4352c593315Sopenharmony_ci  return 0;
4362c593315Sopenharmony_ci}
4372c593315Sopenharmony_ci
4382c593315Sopenharmony_cistatic const char ERROR_HTML[] = "<html><head><title>404</title></head>"
4392c593315Sopenharmony_ci                                 "<body><h1>404 Not Found</h1></body></html>";
4402c593315Sopenharmony_ci
4412c593315Sopenharmony_cistatic int error_reply(nghttp2_session *session,
4422c593315Sopenharmony_ci                       http2_stream_data *stream_data) {
4432c593315Sopenharmony_ci  int rv;
4442c593315Sopenharmony_ci  ssize_t writelen;
4452c593315Sopenharmony_ci  int pipefd[2];
4462c593315Sopenharmony_ci  nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
4472c593315Sopenharmony_ci
4482c593315Sopenharmony_ci  rv = pipe(pipefd);
4492c593315Sopenharmony_ci  if (rv != 0) {
4502c593315Sopenharmony_ci    warn("Could not create pipe");
4512c593315Sopenharmony_ci    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
4522c593315Sopenharmony_ci                                   stream_data->stream_id,
4532c593315Sopenharmony_ci                                   NGHTTP2_INTERNAL_ERROR);
4542c593315Sopenharmony_ci    if (rv != 0) {
4552c593315Sopenharmony_ci      warnx("Fatal error: %s", nghttp2_strerror(rv));
4562c593315Sopenharmony_ci      return -1;
4572c593315Sopenharmony_ci    }
4582c593315Sopenharmony_ci    return 0;
4592c593315Sopenharmony_ci  }
4602c593315Sopenharmony_ci
4612c593315Sopenharmony_ci  writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
4622c593315Sopenharmony_ci  close(pipefd[1]);
4632c593315Sopenharmony_ci
4642c593315Sopenharmony_ci  if (writelen != sizeof(ERROR_HTML) - 1) {
4652c593315Sopenharmony_ci    close(pipefd[0]);
4662c593315Sopenharmony_ci    return -1;
4672c593315Sopenharmony_ci  }
4682c593315Sopenharmony_ci
4692c593315Sopenharmony_ci  stream_data->fd = pipefd[0];
4702c593315Sopenharmony_ci
4712c593315Sopenharmony_ci  if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
4722c593315Sopenharmony_ci                    pipefd[0]) != 0) {
4732c593315Sopenharmony_ci    close(pipefd[0]);
4742c593315Sopenharmony_ci    return -1;
4752c593315Sopenharmony_ci  }
4762c593315Sopenharmony_ci  return 0;
4772c593315Sopenharmony_ci}
4782c593315Sopenharmony_ci
4792c593315Sopenharmony_ci/* nghttp2_on_header_callback: Called when nghttp2 library emits
4802c593315Sopenharmony_ci   single header name/value pair. */
4812c593315Sopenharmony_cistatic int on_header_callback(nghttp2_session *session,
4822c593315Sopenharmony_ci                              const nghttp2_frame *frame, const uint8_t *name,
4832c593315Sopenharmony_ci                              size_t namelen, const uint8_t *value,
4842c593315Sopenharmony_ci                              size_t valuelen, uint8_t flags, void *user_data) {
4852c593315Sopenharmony_ci  http2_stream_data *stream_data;
4862c593315Sopenharmony_ci  const char PATH[] = ":path";
4872c593315Sopenharmony_ci  (void)flags;
4882c593315Sopenharmony_ci  (void)user_data;
4892c593315Sopenharmony_ci
4902c593315Sopenharmony_ci  switch (frame->hd.type) {
4912c593315Sopenharmony_ci  case NGHTTP2_HEADERS:
4922c593315Sopenharmony_ci    if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
4932c593315Sopenharmony_ci      break;
4942c593315Sopenharmony_ci    }
4952c593315Sopenharmony_ci    stream_data =
4962c593315Sopenharmony_ci        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
4972c593315Sopenharmony_ci    if (!stream_data || stream_data->request_path) {
4982c593315Sopenharmony_ci      break;
4992c593315Sopenharmony_ci    }
5002c593315Sopenharmony_ci    if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
5012c593315Sopenharmony_ci      size_t j;
5022c593315Sopenharmony_ci      for (j = 0; j < valuelen && value[j] != '?'; ++j)
5032c593315Sopenharmony_ci        ;
5042c593315Sopenharmony_ci      stream_data->request_path = percent_decode(value, j);
5052c593315Sopenharmony_ci    }
5062c593315Sopenharmony_ci    break;
5072c593315Sopenharmony_ci  }
5082c593315Sopenharmony_ci  return 0;
5092c593315Sopenharmony_ci}
5102c593315Sopenharmony_ci
5112c593315Sopenharmony_cistatic int on_begin_headers_callback(nghttp2_session *session,
5122c593315Sopenharmony_ci                                     const nghttp2_frame *frame,
5132c593315Sopenharmony_ci                                     void *user_data) {
5142c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)user_data;
5152c593315Sopenharmony_ci  http2_stream_data *stream_data;
5162c593315Sopenharmony_ci
5172c593315Sopenharmony_ci  if (frame->hd.type != NGHTTP2_HEADERS ||
5182c593315Sopenharmony_ci      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
5192c593315Sopenharmony_ci    return 0;
5202c593315Sopenharmony_ci  }
5212c593315Sopenharmony_ci  stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
5222c593315Sopenharmony_ci  nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
5232c593315Sopenharmony_ci                                       stream_data);
5242c593315Sopenharmony_ci  return 0;
5252c593315Sopenharmony_ci}
5262c593315Sopenharmony_ci
5272c593315Sopenharmony_ci/* Minimum check for directory traversal. Returns nonzero if it is
5282c593315Sopenharmony_ci   safe. */
5292c593315Sopenharmony_cistatic int check_path(const char *path) {
5302c593315Sopenharmony_ci  /* We don't like '\' in url. */
5312c593315Sopenharmony_ci  return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
5322c593315Sopenharmony_ci         strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
5332c593315Sopenharmony_ci         !ends_with(path, "/..") && !ends_with(path, "/.");
5342c593315Sopenharmony_ci}
5352c593315Sopenharmony_ci
5362c593315Sopenharmony_cistatic int on_request_recv(nghttp2_session *session,
5372c593315Sopenharmony_ci                           http2_session_data *session_data,
5382c593315Sopenharmony_ci                           http2_stream_data *stream_data) {
5392c593315Sopenharmony_ci  int fd;
5402c593315Sopenharmony_ci  nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
5412c593315Sopenharmony_ci  char *rel_path;
5422c593315Sopenharmony_ci
5432c593315Sopenharmony_ci  if (!stream_data->request_path) {
5442c593315Sopenharmony_ci    if (error_reply(session, stream_data) != 0) {
5452c593315Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
5462c593315Sopenharmony_ci    }
5472c593315Sopenharmony_ci    return 0;
5482c593315Sopenharmony_ci  }
5492c593315Sopenharmony_ci  fprintf(stderr, "%s GET %s\n", session_data->client_addr,
5502c593315Sopenharmony_ci          stream_data->request_path);
5512c593315Sopenharmony_ci  if (!check_path(stream_data->request_path)) {
5522c593315Sopenharmony_ci    if (error_reply(session, stream_data) != 0) {
5532c593315Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
5542c593315Sopenharmony_ci    }
5552c593315Sopenharmony_ci    return 0;
5562c593315Sopenharmony_ci  }
5572c593315Sopenharmony_ci  for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
5582c593315Sopenharmony_ci    ;
5592c593315Sopenharmony_ci  fd = open(rel_path, O_RDONLY);
5602c593315Sopenharmony_ci  if (fd == -1) {
5612c593315Sopenharmony_ci    if (error_reply(session, stream_data) != 0) {
5622c593315Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
5632c593315Sopenharmony_ci    }
5642c593315Sopenharmony_ci    return 0;
5652c593315Sopenharmony_ci  }
5662c593315Sopenharmony_ci  stream_data->fd = fd;
5672c593315Sopenharmony_ci
5682c593315Sopenharmony_ci  if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
5692c593315Sopenharmony_ci      0) {
5702c593315Sopenharmony_ci    close(fd);
5712c593315Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
5722c593315Sopenharmony_ci  }
5732c593315Sopenharmony_ci  return 0;
5742c593315Sopenharmony_ci}
5752c593315Sopenharmony_ci
5762c593315Sopenharmony_cistatic int on_frame_recv_callback(nghttp2_session *session,
5772c593315Sopenharmony_ci                                  const nghttp2_frame *frame, void *user_data) {
5782c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)user_data;
5792c593315Sopenharmony_ci  http2_stream_data *stream_data;
5802c593315Sopenharmony_ci  switch (frame->hd.type) {
5812c593315Sopenharmony_ci  case NGHTTP2_DATA:
5822c593315Sopenharmony_ci  case NGHTTP2_HEADERS:
5832c593315Sopenharmony_ci    /* Check that the client request has finished */
5842c593315Sopenharmony_ci    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
5852c593315Sopenharmony_ci      stream_data =
5862c593315Sopenharmony_ci          nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
5872c593315Sopenharmony_ci      /* For DATA and HEADERS frame, this callback may be called after
5882c593315Sopenharmony_ci         on_stream_close_callback. Check that stream still alive. */
5892c593315Sopenharmony_ci      if (!stream_data) {
5902c593315Sopenharmony_ci        return 0;
5912c593315Sopenharmony_ci      }
5922c593315Sopenharmony_ci      return on_request_recv(session, session_data, stream_data);
5932c593315Sopenharmony_ci    }
5942c593315Sopenharmony_ci    break;
5952c593315Sopenharmony_ci  default:
5962c593315Sopenharmony_ci    break;
5972c593315Sopenharmony_ci  }
5982c593315Sopenharmony_ci  return 0;
5992c593315Sopenharmony_ci}
6002c593315Sopenharmony_ci
6012c593315Sopenharmony_cistatic int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
6022c593315Sopenharmony_ci                                    uint32_t error_code, void *user_data) {
6032c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)user_data;
6042c593315Sopenharmony_ci  http2_stream_data *stream_data;
6052c593315Sopenharmony_ci  (void)error_code;
6062c593315Sopenharmony_ci
6072c593315Sopenharmony_ci  stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
6082c593315Sopenharmony_ci  if (!stream_data) {
6092c593315Sopenharmony_ci    return 0;
6102c593315Sopenharmony_ci  }
6112c593315Sopenharmony_ci  remove_stream(session_data, stream_data);
6122c593315Sopenharmony_ci  delete_http2_stream_data(stream_data);
6132c593315Sopenharmony_ci  return 0;
6142c593315Sopenharmony_ci}
6152c593315Sopenharmony_ci
6162c593315Sopenharmony_cistatic void initialize_nghttp2_session(http2_session_data *session_data) {
6172c593315Sopenharmony_ci  nghttp2_session_callbacks *callbacks;
6182c593315Sopenharmony_ci
6192c593315Sopenharmony_ci  nghttp2_session_callbacks_new(&callbacks);
6202c593315Sopenharmony_ci
6212c593315Sopenharmony_ci  nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
6222c593315Sopenharmony_ci
6232c593315Sopenharmony_ci  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
6242c593315Sopenharmony_ci                                                       on_frame_recv_callback);
6252c593315Sopenharmony_ci
6262c593315Sopenharmony_ci  nghttp2_session_callbacks_set_on_stream_close_callback(
6272c593315Sopenharmony_ci      callbacks, on_stream_close_callback);
6282c593315Sopenharmony_ci
6292c593315Sopenharmony_ci  nghttp2_session_callbacks_set_on_header_callback(callbacks,
6302c593315Sopenharmony_ci                                                   on_header_callback);
6312c593315Sopenharmony_ci
6322c593315Sopenharmony_ci  nghttp2_session_callbacks_set_on_begin_headers_callback(
6332c593315Sopenharmony_ci      callbacks, on_begin_headers_callback);
6342c593315Sopenharmony_ci
6352c593315Sopenharmony_ci  nghttp2_session_server_new(&session_data->session, callbacks, session_data);
6362c593315Sopenharmony_ci
6372c593315Sopenharmony_ci  nghttp2_session_callbacks_del(callbacks);
6382c593315Sopenharmony_ci}
6392c593315Sopenharmony_ci
6402c593315Sopenharmony_ci/* Send HTTP/2 client connection header, which includes 24 bytes
6412c593315Sopenharmony_ci   magic octets and SETTINGS frame */
6422c593315Sopenharmony_cistatic int send_server_connection_header(http2_session_data *session_data) {
6432c593315Sopenharmony_ci  nghttp2_settings_entry iv[1] = {
6442c593315Sopenharmony_ci      {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
6452c593315Sopenharmony_ci  int rv;
6462c593315Sopenharmony_ci
6472c593315Sopenharmony_ci  rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
6482c593315Sopenharmony_ci                               ARRLEN(iv));
6492c593315Sopenharmony_ci  if (rv != 0) {
6502c593315Sopenharmony_ci    warnx("Fatal error: %s", nghttp2_strerror(rv));
6512c593315Sopenharmony_ci    return -1;
6522c593315Sopenharmony_ci  }
6532c593315Sopenharmony_ci  return 0;
6542c593315Sopenharmony_ci}
6552c593315Sopenharmony_ci
6562c593315Sopenharmony_ci/* readcb for bufferevent after client connection header was
6572c593315Sopenharmony_ci   checked. */
6582c593315Sopenharmony_cistatic void readcb(struct bufferevent *bev, void *ptr) {
6592c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)ptr;
6602c593315Sopenharmony_ci  (void)bev;
6612c593315Sopenharmony_ci
6622c593315Sopenharmony_ci  if (session_recv(session_data) != 0) {
6632c593315Sopenharmony_ci    delete_http2_session_data(session_data);
6642c593315Sopenharmony_ci    return;
6652c593315Sopenharmony_ci  }
6662c593315Sopenharmony_ci}
6672c593315Sopenharmony_ci
6682c593315Sopenharmony_ci/* writecb for bufferevent. To greaceful shutdown after sending or
6692c593315Sopenharmony_ci   receiving GOAWAY, we check the some conditions on the nghttp2
6702c593315Sopenharmony_ci   library and output buffer of bufferevent. If it indicates we have
6712c593315Sopenharmony_ci   no business to this session, tear down the connection. If the
6722c593315Sopenharmony_ci   connection is not going to shutdown, we call session_send() to
6732c593315Sopenharmony_ci   process pending data in the output buffer. This is necessary
6742c593315Sopenharmony_ci   because we have a threshold on the buffer size to avoid too much
6752c593315Sopenharmony_ci   buffering. See send_callback(). */
6762c593315Sopenharmony_cistatic void writecb(struct bufferevent *bev, void *ptr) {
6772c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)ptr;
6782c593315Sopenharmony_ci  if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
6792c593315Sopenharmony_ci    return;
6802c593315Sopenharmony_ci  }
6812c593315Sopenharmony_ci  if (nghttp2_session_want_read(session_data->session) == 0 &&
6822c593315Sopenharmony_ci      nghttp2_session_want_write(session_data->session) == 0) {
6832c593315Sopenharmony_ci    delete_http2_session_data(session_data);
6842c593315Sopenharmony_ci    return;
6852c593315Sopenharmony_ci  }
6862c593315Sopenharmony_ci  if (session_send(session_data) != 0) {
6872c593315Sopenharmony_ci    delete_http2_session_data(session_data);
6882c593315Sopenharmony_ci    return;
6892c593315Sopenharmony_ci  }
6902c593315Sopenharmony_ci}
6912c593315Sopenharmony_ci
6922c593315Sopenharmony_ci/* eventcb for bufferevent */
6932c593315Sopenharmony_cistatic void eventcb(struct bufferevent *bev, short events, void *ptr) {
6942c593315Sopenharmony_ci  http2_session_data *session_data = (http2_session_data *)ptr;
6952c593315Sopenharmony_ci  if (events & BEV_EVENT_CONNECTED) {
6962c593315Sopenharmony_ci    const unsigned char *alpn = NULL;
6972c593315Sopenharmony_ci    unsigned int alpnlen = 0;
6982c593315Sopenharmony_ci    SSL *ssl;
6992c593315Sopenharmony_ci    (void)bev;
7002c593315Sopenharmony_ci
7012c593315Sopenharmony_ci    fprintf(stderr, "%s connected\n", session_data->client_addr);
7022c593315Sopenharmony_ci
7032c593315Sopenharmony_ci    ssl = bufferevent_openssl_get_ssl(session_data->bev);
7042c593315Sopenharmony_ci
7052c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG
7062c593315Sopenharmony_ci    SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
7072c593315Sopenharmony_ci#endif /* !OPENSSL_NO_NEXTPROTONEG */
7082c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L
7092c593315Sopenharmony_ci    if (alpn == NULL) {
7102c593315Sopenharmony_ci      SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
7112c593315Sopenharmony_ci    }
7122c593315Sopenharmony_ci#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
7132c593315Sopenharmony_ci
7142c593315Sopenharmony_ci    if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
7152c593315Sopenharmony_ci      fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
7162c593315Sopenharmony_ci      delete_http2_session_data(session_data);
7172c593315Sopenharmony_ci      return;
7182c593315Sopenharmony_ci    }
7192c593315Sopenharmony_ci
7202c593315Sopenharmony_ci    initialize_nghttp2_session(session_data);
7212c593315Sopenharmony_ci
7222c593315Sopenharmony_ci    if (send_server_connection_header(session_data) != 0 ||
7232c593315Sopenharmony_ci        session_send(session_data) != 0) {
7242c593315Sopenharmony_ci      delete_http2_session_data(session_data);
7252c593315Sopenharmony_ci      return;
7262c593315Sopenharmony_ci    }
7272c593315Sopenharmony_ci
7282c593315Sopenharmony_ci    return;
7292c593315Sopenharmony_ci  }
7302c593315Sopenharmony_ci  if (events & BEV_EVENT_EOF) {
7312c593315Sopenharmony_ci    fprintf(stderr, "%s EOF\n", session_data->client_addr);
7322c593315Sopenharmony_ci  } else if (events & BEV_EVENT_ERROR) {
7332c593315Sopenharmony_ci    fprintf(stderr, "%s network error\n", session_data->client_addr);
7342c593315Sopenharmony_ci  } else if (events & BEV_EVENT_TIMEOUT) {
7352c593315Sopenharmony_ci    fprintf(stderr, "%s timeout\n", session_data->client_addr);
7362c593315Sopenharmony_ci  }
7372c593315Sopenharmony_ci  delete_http2_session_data(session_data);
7382c593315Sopenharmony_ci}
7392c593315Sopenharmony_ci
7402c593315Sopenharmony_ci/* callback for evconnlistener */
7412c593315Sopenharmony_cistatic void acceptcb(struct evconnlistener *listener, int fd,
7422c593315Sopenharmony_ci                     struct sockaddr *addr, int addrlen, void *arg) {
7432c593315Sopenharmony_ci  app_context *app_ctx = (app_context *)arg;
7442c593315Sopenharmony_ci  http2_session_data *session_data;
7452c593315Sopenharmony_ci  (void)listener;
7462c593315Sopenharmony_ci
7472c593315Sopenharmony_ci  session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
7482c593315Sopenharmony_ci
7492c593315Sopenharmony_ci  bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
7502c593315Sopenharmony_ci}
7512c593315Sopenharmony_ci
7522c593315Sopenharmony_cistatic void start_listen(struct event_base *evbase, const char *service,
7532c593315Sopenharmony_ci                         app_context *app_ctx) {
7542c593315Sopenharmony_ci  int rv;
7552c593315Sopenharmony_ci  struct addrinfo hints;
7562c593315Sopenharmony_ci  struct addrinfo *res, *rp;
7572c593315Sopenharmony_ci
7582c593315Sopenharmony_ci  memset(&hints, 0, sizeof(hints));
7592c593315Sopenharmony_ci  hints.ai_family = AF_UNSPEC;
7602c593315Sopenharmony_ci  hints.ai_socktype = SOCK_STREAM;
7612c593315Sopenharmony_ci  hints.ai_flags = AI_PASSIVE;
7622c593315Sopenharmony_ci#ifdef AI_ADDRCONFIG
7632c593315Sopenharmony_ci  hints.ai_flags |= AI_ADDRCONFIG;
7642c593315Sopenharmony_ci#endif /* AI_ADDRCONFIG */
7652c593315Sopenharmony_ci
7662c593315Sopenharmony_ci  rv = getaddrinfo(NULL, service, &hints, &res);
7672c593315Sopenharmony_ci  if (rv != 0) {
7682c593315Sopenharmony_ci    errx(1, "Could not resolve server address");
7692c593315Sopenharmony_ci  }
7702c593315Sopenharmony_ci  for (rp = res; rp; rp = rp->ai_next) {
7712c593315Sopenharmony_ci    struct evconnlistener *listener;
7722c593315Sopenharmony_ci    listener = evconnlistener_new_bind(
7732c593315Sopenharmony_ci        evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
7742c593315Sopenharmony_ci        16, rp->ai_addr, (int)rp->ai_addrlen);
7752c593315Sopenharmony_ci    if (listener) {
7762c593315Sopenharmony_ci      freeaddrinfo(res);
7772c593315Sopenharmony_ci
7782c593315Sopenharmony_ci      return;
7792c593315Sopenharmony_ci    }
7802c593315Sopenharmony_ci  }
7812c593315Sopenharmony_ci  errx(1, "Could not start listener");
7822c593315Sopenharmony_ci}
7832c593315Sopenharmony_ci
7842c593315Sopenharmony_cistatic void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
7852c593315Sopenharmony_ci                                   struct event_base *evbase) {
7862c593315Sopenharmony_ci  memset(app_ctx, 0, sizeof(app_context));
7872c593315Sopenharmony_ci  app_ctx->ssl_ctx = ssl_ctx;
7882c593315Sopenharmony_ci  app_ctx->evbase = evbase;
7892c593315Sopenharmony_ci}
7902c593315Sopenharmony_ci
7912c593315Sopenharmony_cistatic void run(const char *service, const char *key_file,
7922c593315Sopenharmony_ci                const char *cert_file) {
7932c593315Sopenharmony_ci  SSL_CTX *ssl_ctx;
7942c593315Sopenharmony_ci  app_context app_ctx;
7952c593315Sopenharmony_ci  struct event_base *evbase;
7962c593315Sopenharmony_ci
7972c593315Sopenharmony_ci  ssl_ctx = create_ssl_ctx(key_file, cert_file);
7982c593315Sopenharmony_ci  evbase = event_base_new();
7992c593315Sopenharmony_ci  initialize_app_context(&app_ctx, ssl_ctx, evbase);
8002c593315Sopenharmony_ci  start_listen(evbase, service, &app_ctx);
8012c593315Sopenharmony_ci
8022c593315Sopenharmony_ci  event_base_loop(evbase, 0);
8032c593315Sopenharmony_ci
8042c593315Sopenharmony_ci  event_base_free(evbase);
8052c593315Sopenharmony_ci  SSL_CTX_free(ssl_ctx);
8062c593315Sopenharmony_ci}
8072c593315Sopenharmony_ci
8082c593315Sopenharmony_ciint main(int argc, char **argv) {
8092c593315Sopenharmony_ci  struct sigaction act;
8102c593315Sopenharmony_ci
8112c593315Sopenharmony_ci  if (argc < 4) {
8122c593315Sopenharmony_ci    fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
8132c593315Sopenharmony_ci    exit(EXIT_FAILURE);
8142c593315Sopenharmony_ci  }
8152c593315Sopenharmony_ci
8162c593315Sopenharmony_ci  memset(&act, 0, sizeof(struct sigaction));
8172c593315Sopenharmony_ci  act.sa_handler = SIG_IGN;
8182c593315Sopenharmony_ci  sigaction(SIGPIPE, &act, NULL);
8192c593315Sopenharmony_ci
8202c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
8212c593315Sopenharmony_ci  /* No explicit initialization is required. */
8222c593315Sopenharmony_ci#elif defined(OPENSSL_IS_BORINGSSL)
8232c593315Sopenharmony_ci  CRYPTO_library_init();
8242c593315Sopenharmony_ci#else  /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) &&                          \
8252c593315Sopenharmony_ci          !defined(OPENSSL_IS_BORINGSSL) */
8262c593315Sopenharmony_ci  OPENSSL_config(NULL);
8272c593315Sopenharmony_ci  SSL_load_error_strings();
8282c593315Sopenharmony_ci  SSL_library_init();
8292c593315Sopenharmony_ci  OpenSSL_add_all_algorithms();
8302c593315Sopenharmony_ci#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) &&                          \
8312c593315Sopenharmony_ci          !defined(OPENSSL_IS_BORINGSSL) */
8322c593315Sopenharmony_ci
8332c593315Sopenharmony_ci  run(argv[1], argv[2], argv[3]);
8342c593315Sopenharmony_ci  return 0;
8352c593315Sopenharmony_ci}
836