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