1/*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2013 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25#ifdef __sgi
26#  define errx(exitcode, format, args...)                                      \
27    {                                                                          \
28      warnx(format, ##args);                                                   \
29      exit(exitcode);                                                          \
30    }
31#  define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
32#  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
33#endif
34
35#ifdef HAVE_CONFIG_H
36#  include <config.h>
37#endif /* HAVE_CONFIG_H */
38
39#include <sys/types.h>
40#ifdef HAVE_SYS_SOCKET_H
41#  include <sys/socket.h>
42#endif /* HAVE_SYS_SOCKET_H */
43#ifdef HAVE_NETDB_H
44#  include <netdb.h>
45#endif /* HAVE_NETDB_H */
46#include <signal.h>
47#ifdef HAVE_UNISTD_H
48#  include <unistd.h>
49#endif /* HAVE_UNISTD_H */
50#include <sys/stat.h>
51#ifdef HAVE_FCNTL_H
52#  include <fcntl.h>
53#endif /* HAVE_FCNTL_H */
54#include <ctype.h>
55#ifdef HAVE_NETINET_IN_H
56#  include <netinet/in.h>
57#endif /* HAVE_NETINET_IN_H */
58#include <netinet/tcp.h>
59#ifndef __sgi
60#  include <err.h>
61#endif
62#include <string.h>
63#include <errno.h>
64
65#include <openssl/ssl.h>
66#include <openssl/err.h>
67#include <openssl/conf.h>
68
69#include <event.h>
70#include <event2/event.h>
71#include <event2/bufferevent_ssl.h>
72#include <event2/listener.h>
73
74#include <nghttp2/nghttp2.h>
75
76#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
77
78#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
79
80#define MAKE_NV(NAME, VALUE)                                                   \
81  {                                                                            \
82    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,    \
83        NGHTTP2_NV_FLAG_NONE                                                   \
84  }
85
86struct app_context;
87typedef struct app_context app_context;
88
89typedef struct http2_stream_data {
90  struct http2_stream_data *prev, *next;
91  char *request_path;
92  int32_t stream_id;
93  int fd;
94} http2_stream_data;
95
96typedef struct http2_session_data {
97  struct http2_stream_data root;
98  struct bufferevent *bev;
99  app_context *app_ctx;
100  nghttp2_session *session;
101  char *client_addr;
102} http2_session_data;
103
104struct app_context {
105  SSL_CTX *ssl_ctx;
106  struct event_base *evbase;
107};
108
109static unsigned char next_proto_list[256];
110static size_t next_proto_list_len;
111
112#ifndef OPENSSL_NO_NEXTPROTONEG
113static int next_proto_cb(SSL *ssl, const unsigned char **data,
114                         unsigned int *len, void *arg) {
115  (void)ssl;
116  (void)arg;
117
118  *data = next_proto_list;
119  *len = (unsigned int)next_proto_list_len;
120  return SSL_TLSEXT_ERR_OK;
121}
122#endif /* !OPENSSL_NO_NEXTPROTONEG */
123
124#if OPENSSL_VERSION_NUMBER >= 0x10002000L
125static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
126                                unsigned char *outlen, const unsigned char *in,
127                                unsigned int inlen, void *arg) {
128  int rv;
129  (void)ssl;
130  (void)arg;
131
132  rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
133
134  if (rv != 1) {
135    return SSL_TLSEXT_ERR_NOACK;
136  }
137
138  return SSL_TLSEXT_ERR_OK;
139}
140#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
141
142/* Create SSL_CTX. */
143static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
144  SSL_CTX *ssl_ctx;
145
146  ssl_ctx = SSL_CTX_new(TLS_server_method());
147  if (!ssl_ctx) {
148    errx(1, "Could not create SSL/TLS context: %s",
149         ERR_error_string(ERR_get_error(), NULL));
150  }
151  SSL_CTX_set_options(ssl_ctx,
152                      SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
153                          SSL_OP_NO_COMPRESSION |
154                          SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
155#if OPENSSL_VERSION_NUMBER >= 0x30000000L
156  if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
157    errx(1, "SSL_CTX_set1_curves_list failed: %s",
158         ERR_error_string(ERR_get_error(), NULL));
159  }
160#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
161  {
162    EC_KEY *ecdh;
163    ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
164    if (!ecdh) {
165      errx(1, "EC_KEY_new_by_curv_name failed: %s",
166           ERR_error_string(ERR_get_error(), NULL));
167    }
168    SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
169    EC_KEY_free(ecdh);
170  }
171#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
172
173  if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
174    errx(1, "Could not read private key file %s", key_file);
175  }
176  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
177    errx(1, "Could not read certificate file %s", cert_file);
178  }
179
180  next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
181  memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
182         NGHTTP2_PROTO_VERSION_ID_LEN);
183  next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
184
185#ifndef OPENSSL_NO_NEXTPROTONEG
186  SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
187#endif /* !OPENSSL_NO_NEXTPROTONEG */
188
189#if OPENSSL_VERSION_NUMBER >= 0x10002000L
190  SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
191#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
192
193  return ssl_ctx;
194}
195
196/* Create SSL object */
197static SSL *create_ssl(SSL_CTX *ssl_ctx) {
198  SSL *ssl;
199  ssl = SSL_new(ssl_ctx);
200  if (!ssl) {
201    errx(1, "Could not create SSL/TLS session object: %s",
202         ERR_error_string(ERR_get_error(), NULL));
203  }
204  return ssl;
205}
206
207static void add_stream(http2_session_data *session_data,
208                       http2_stream_data *stream_data) {
209  stream_data->next = session_data->root.next;
210  session_data->root.next = stream_data;
211  stream_data->prev = &session_data->root;
212  if (stream_data->next) {
213    stream_data->next->prev = stream_data;
214  }
215}
216
217static void remove_stream(http2_session_data *session_data,
218                          http2_stream_data *stream_data) {
219  (void)session_data;
220
221  stream_data->prev->next = stream_data->next;
222  if (stream_data->next) {
223    stream_data->next->prev = stream_data->prev;
224  }
225}
226
227static http2_stream_data *
228create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
229  http2_stream_data *stream_data;
230  stream_data = malloc(sizeof(http2_stream_data));
231  memset(stream_data, 0, sizeof(http2_stream_data));
232  stream_data->stream_id = stream_id;
233  stream_data->fd = -1;
234
235  add_stream(session_data, stream_data);
236  return stream_data;
237}
238
239static void delete_http2_stream_data(http2_stream_data *stream_data) {
240  if (stream_data->fd != -1) {
241    close(stream_data->fd);
242  }
243  free(stream_data->request_path);
244  free(stream_data);
245}
246
247static http2_session_data *create_http2_session_data(app_context *app_ctx,
248                                                     int fd,
249                                                     struct sockaddr *addr,
250                                                     int addrlen) {
251  int rv;
252  http2_session_data *session_data;
253  SSL *ssl;
254  char host[NI_MAXHOST];
255  int val = 1;
256
257  ssl = create_ssl(app_ctx->ssl_ctx);
258  session_data = malloc(sizeof(http2_session_data));
259  memset(session_data, 0, sizeof(http2_session_data));
260  session_data->app_ctx = app_ctx;
261  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
262  session_data->bev = bufferevent_openssl_socket_new(
263      app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
264      BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
265  bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
266  rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
267                   NI_NUMERICHOST);
268  if (rv != 0) {
269    session_data->client_addr = strdup("(unknown)");
270  } else {
271    session_data->client_addr = strdup(host);
272  }
273
274  return session_data;
275}
276
277static void delete_http2_session_data(http2_session_data *session_data) {
278  http2_stream_data *stream_data;
279  SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
280  fprintf(stderr, "%s disconnected\n", session_data->client_addr);
281  if (ssl) {
282    SSL_shutdown(ssl);
283  }
284  bufferevent_free(session_data->bev);
285  nghttp2_session_del(session_data->session);
286  for (stream_data = session_data->root.next; stream_data;) {
287    http2_stream_data *next = stream_data->next;
288    delete_http2_stream_data(stream_data);
289    stream_data = next;
290  }
291  free(session_data->client_addr);
292  free(session_data);
293}
294
295/* Serialize the frame and send (or buffer) the data to
296   bufferevent. */
297static int session_send(http2_session_data *session_data) {
298  int rv;
299  rv = nghttp2_session_send(session_data->session);
300  if (rv != 0) {
301    warnx("Fatal error: %s", nghttp2_strerror(rv));
302    return -1;
303  }
304  return 0;
305}
306
307/* Read the data in the bufferevent and feed them into nghttp2 library
308   function. Invocation of nghttp2_session_mem_recv() may make
309   additional pending frames, so call session_send() at the end of the
310   function. */
311static int session_recv(http2_session_data *session_data) {
312  ssize_t readlen;
313  struct evbuffer *input = bufferevent_get_input(session_data->bev);
314  size_t datalen = evbuffer_get_length(input);
315  unsigned char *data = evbuffer_pullup(input, -1);
316
317  readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
318  if (readlen < 0) {
319    warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
320    return -1;
321  }
322  if (evbuffer_drain(input, (size_t)readlen) != 0) {
323    warnx("Fatal error: evbuffer_drain failed");
324    return -1;
325  }
326  if (session_send(session_data) != 0) {
327    return -1;
328  }
329  return 0;
330}
331
332static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
333                             size_t length, int flags, void *user_data) {
334  http2_session_data *session_data = (http2_session_data *)user_data;
335  struct bufferevent *bev = session_data->bev;
336  (void)session;
337  (void)flags;
338
339  /* Avoid excessive buffering in server side. */
340  if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
341      OUTPUT_WOULDBLOCK_THRESHOLD) {
342    return NGHTTP2_ERR_WOULDBLOCK;
343  }
344  bufferevent_write(bev, data, length);
345  return (ssize_t)length;
346}
347
348/* Returns nonzero if the string |s| ends with the substring |sub| */
349static int ends_with(const char *s, const char *sub) {
350  size_t slen = strlen(s);
351  size_t sublen = strlen(sub);
352  if (slen < sublen) {
353    return 0;
354  }
355  return memcmp(s + slen - sublen, sub, sublen) == 0;
356}
357
358/* Returns int value of hex string character |c| */
359static uint8_t hex_to_uint(uint8_t c) {
360  if ('0' <= c && c <= '9') {
361    return (uint8_t)(c - '0');
362  }
363  if ('A' <= c && c <= 'F') {
364    return (uint8_t)(c - 'A' + 10);
365  }
366  if ('a' <= c && c <= 'f') {
367    return (uint8_t)(c - 'a' + 10);
368  }
369  return 0;
370}
371
372/* Decodes percent-encoded byte string |value| with length |valuelen|
373   and returns the decoded byte string in allocated buffer. The return
374   value is NULL terminated. The caller must free the returned
375   string. */
376static char *percent_decode(const uint8_t *value, size_t valuelen) {
377  char *res;
378
379  res = malloc(valuelen + 1);
380  if (valuelen > 3) {
381    size_t i, j;
382    for (i = 0, j = 0; i < valuelen - 2;) {
383      if (value[i] != '%' || !isxdigit(value[i + 1]) ||
384          !isxdigit(value[i + 2])) {
385        res[j++] = (char)value[i++];
386        continue;
387      }
388      res[j++] =
389          (char)((hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]));
390      i += 3;
391    }
392    memcpy(&res[j], &value[i], 2);
393    res[j + 2] = '\0';
394  } else {
395    memcpy(res, value, valuelen);
396    res[valuelen] = '\0';
397  }
398  return res;
399}
400
401static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
402                                  uint8_t *buf, size_t length,
403                                  uint32_t *data_flags,
404                                  nghttp2_data_source *source,
405                                  void *user_data) {
406  int fd = source->fd;
407  ssize_t r;
408  (void)session;
409  (void)stream_id;
410  (void)user_data;
411
412  while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
413    ;
414  if (r == -1) {
415    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
416  }
417  if (r == 0) {
418    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
419  }
420  return r;
421}
422
423static int send_response(nghttp2_session *session, int32_t stream_id,
424                         nghttp2_nv *nva, size_t nvlen, int fd) {
425  int rv;
426  nghttp2_data_provider data_prd;
427  data_prd.source.fd = fd;
428  data_prd.read_callback = file_read_callback;
429
430  rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
431  if (rv != 0) {
432    warnx("Fatal error: %s", nghttp2_strerror(rv));
433    return -1;
434  }
435  return 0;
436}
437
438static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
439                                 "<body><h1>404 Not Found</h1></body></html>";
440
441static int error_reply(nghttp2_session *session,
442                       http2_stream_data *stream_data) {
443  int rv;
444  ssize_t writelen;
445  int pipefd[2];
446  nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
447
448  rv = pipe(pipefd);
449  if (rv != 0) {
450    warn("Could not create pipe");
451    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
452                                   stream_data->stream_id,
453                                   NGHTTP2_INTERNAL_ERROR);
454    if (rv != 0) {
455      warnx("Fatal error: %s", nghttp2_strerror(rv));
456      return -1;
457    }
458    return 0;
459  }
460
461  writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
462  close(pipefd[1]);
463
464  if (writelen != sizeof(ERROR_HTML) - 1) {
465    close(pipefd[0]);
466    return -1;
467  }
468
469  stream_data->fd = pipefd[0];
470
471  if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
472                    pipefd[0]) != 0) {
473    close(pipefd[0]);
474    return -1;
475  }
476  return 0;
477}
478
479/* nghttp2_on_header_callback: Called when nghttp2 library emits
480   single header name/value pair. */
481static int on_header_callback(nghttp2_session *session,
482                              const nghttp2_frame *frame, const uint8_t *name,
483                              size_t namelen, const uint8_t *value,
484                              size_t valuelen, uint8_t flags, void *user_data) {
485  http2_stream_data *stream_data;
486  const char PATH[] = ":path";
487  (void)flags;
488  (void)user_data;
489
490  switch (frame->hd.type) {
491  case NGHTTP2_HEADERS:
492    if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
493      break;
494    }
495    stream_data =
496        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
497    if (!stream_data || stream_data->request_path) {
498      break;
499    }
500    if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
501      size_t j;
502      for (j = 0; j < valuelen && value[j] != '?'; ++j)
503        ;
504      stream_data->request_path = percent_decode(value, j);
505    }
506    break;
507  }
508  return 0;
509}
510
511static int on_begin_headers_callback(nghttp2_session *session,
512                                     const nghttp2_frame *frame,
513                                     void *user_data) {
514  http2_session_data *session_data = (http2_session_data *)user_data;
515  http2_stream_data *stream_data;
516
517  if (frame->hd.type != NGHTTP2_HEADERS ||
518      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
519    return 0;
520  }
521  stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
522  nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
523                                       stream_data);
524  return 0;
525}
526
527/* Minimum check for directory traversal. Returns nonzero if it is
528   safe. */
529static int check_path(const char *path) {
530  /* We don't like '\' in url. */
531  return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
532         strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
533         !ends_with(path, "/..") && !ends_with(path, "/.");
534}
535
536static int on_request_recv(nghttp2_session *session,
537                           http2_session_data *session_data,
538                           http2_stream_data *stream_data) {
539  int fd;
540  nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
541  char *rel_path;
542
543  if (!stream_data->request_path) {
544    if (error_reply(session, stream_data) != 0) {
545      return NGHTTP2_ERR_CALLBACK_FAILURE;
546    }
547    return 0;
548  }
549  fprintf(stderr, "%s GET %s\n", session_data->client_addr,
550          stream_data->request_path);
551  if (!check_path(stream_data->request_path)) {
552    if (error_reply(session, stream_data) != 0) {
553      return NGHTTP2_ERR_CALLBACK_FAILURE;
554    }
555    return 0;
556  }
557  for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
558    ;
559  fd = open(rel_path, O_RDONLY);
560  if (fd == -1) {
561    if (error_reply(session, stream_data) != 0) {
562      return NGHTTP2_ERR_CALLBACK_FAILURE;
563    }
564    return 0;
565  }
566  stream_data->fd = fd;
567
568  if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
569      0) {
570    close(fd);
571    return NGHTTP2_ERR_CALLBACK_FAILURE;
572  }
573  return 0;
574}
575
576static int on_frame_recv_callback(nghttp2_session *session,
577                                  const nghttp2_frame *frame, void *user_data) {
578  http2_session_data *session_data = (http2_session_data *)user_data;
579  http2_stream_data *stream_data;
580  switch (frame->hd.type) {
581  case NGHTTP2_DATA:
582  case NGHTTP2_HEADERS:
583    /* Check that the client request has finished */
584    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
585      stream_data =
586          nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
587      /* For DATA and HEADERS frame, this callback may be called after
588         on_stream_close_callback. Check that stream still alive. */
589      if (!stream_data) {
590        return 0;
591      }
592      return on_request_recv(session, session_data, stream_data);
593    }
594    break;
595  default:
596    break;
597  }
598  return 0;
599}
600
601static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
602                                    uint32_t error_code, void *user_data) {
603  http2_session_data *session_data = (http2_session_data *)user_data;
604  http2_stream_data *stream_data;
605  (void)error_code;
606
607  stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
608  if (!stream_data) {
609    return 0;
610  }
611  remove_stream(session_data, stream_data);
612  delete_http2_stream_data(stream_data);
613  return 0;
614}
615
616static void initialize_nghttp2_session(http2_session_data *session_data) {
617  nghttp2_session_callbacks *callbacks;
618
619  nghttp2_session_callbacks_new(&callbacks);
620
621  nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
622
623  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
624                                                       on_frame_recv_callback);
625
626  nghttp2_session_callbacks_set_on_stream_close_callback(
627      callbacks, on_stream_close_callback);
628
629  nghttp2_session_callbacks_set_on_header_callback(callbacks,
630                                                   on_header_callback);
631
632  nghttp2_session_callbacks_set_on_begin_headers_callback(
633      callbacks, on_begin_headers_callback);
634
635  nghttp2_session_server_new(&session_data->session, callbacks, session_data);
636
637  nghttp2_session_callbacks_del(callbacks);
638}
639
640/* Send HTTP/2 client connection header, which includes 24 bytes
641   magic octets and SETTINGS frame */
642static int send_server_connection_header(http2_session_data *session_data) {
643  nghttp2_settings_entry iv[1] = {
644      {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
645  int rv;
646
647  rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
648                               ARRLEN(iv));
649  if (rv != 0) {
650    warnx("Fatal error: %s", nghttp2_strerror(rv));
651    return -1;
652  }
653  return 0;
654}
655
656/* readcb for bufferevent after client connection header was
657   checked. */
658static void readcb(struct bufferevent *bev, void *ptr) {
659  http2_session_data *session_data = (http2_session_data *)ptr;
660  (void)bev;
661
662  if (session_recv(session_data) != 0) {
663    delete_http2_session_data(session_data);
664    return;
665  }
666}
667
668/* writecb for bufferevent. To greaceful shutdown after sending or
669   receiving GOAWAY, we check the some conditions on the nghttp2
670   library and output buffer of bufferevent. If it indicates we have
671   no business to this session, tear down the connection. If the
672   connection is not going to shutdown, we call session_send() to
673   process pending data in the output buffer. This is necessary
674   because we have a threshold on the buffer size to avoid too much
675   buffering. See send_callback(). */
676static void writecb(struct bufferevent *bev, void *ptr) {
677  http2_session_data *session_data = (http2_session_data *)ptr;
678  if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
679    return;
680  }
681  if (nghttp2_session_want_read(session_data->session) == 0 &&
682      nghttp2_session_want_write(session_data->session) == 0) {
683    delete_http2_session_data(session_data);
684    return;
685  }
686  if (session_send(session_data) != 0) {
687    delete_http2_session_data(session_data);
688    return;
689  }
690}
691
692/* eventcb for bufferevent */
693static void eventcb(struct bufferevent *bev, short events, void *ptr) {
694  http2_session_data *session_data = (http2_session_data *)ptr;
695  if (events & BEV_EVENT_CONNECTED) {
696    const unsigned char *alpn = NULL;
697    unsigned int alpnlen = 0;
698    SSL *ssl;
699    (void)bev;
700
701    fprintf(stderr, "%s connected\n", session_data->client_addr);
702
703    ssl = bufferevent_openssl_get_ssl(session_data->bev);
704
705#ifndef OPENSSL_NO_NEXTPROTONEG
706    SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
707#endif /* !OPENSSL_NO_NEXTPROTONEG */
708#if OPENSSL_VERSION_NUMBER >= 0x10002000L
709    if (alpn == NULL) {
710      SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
711    }
712#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
713
714    if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
715      fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
716      delete_http2_session_data(session_data);
717      return;
718    }
719
720    initialize_nghttp2_session(session_data);
721
722    if (send_server_connection_header(session_data) != 0 ||
723        session_send(session_data) != 0) {
724      delete_http2_session_data(session_data);
725      return;
726    }
727
728    return;
729  }
730  if (events & BEV_EVENT_EOF) {
731    fprintf(stderr, "%s EOF\n", session_data->client_addr);
732  } else if (events & BEV_EVENT_ERROR) {
733    fprintf(stderr, "%s network error\n", session_data->client_addr);
734  } else if (events & BEV_EVENT_TIMEOUT) {
735    fprintf(stderr, "%s timeout\n", session_data->client_addr);
736  }
737  delete_http2_session_data(session_data);
738}
739
740/* callback for evconnlistener */
741static void acceptcb(struct evconnlistener *listener, int fd,
742                     struct sockaddr *addr, int addrlen, void *arg) {
743  app_context *app_ctx = (app_context *)arg;
744  http2_session_data *session_data;
745  (void)listener;
746
747  session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
748
749  bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
750}
751
752static void start_listen(struct event_base *evbase, const char *service,
753                         app_context *app_ctx) {
754  int rv;
755  struct addrinfo hints;
756  struct addrinfo *res, *rp;
757
758  memset(&hints, 0, sizeof(hints));
759  hints.ai_family = AF_UNSPEC;
760  hints.ai_socktype = SOCK_STREAM;
761  hints.ai_flags = AI_PASSIVE;
762#ifdef AI_ADDRCONFIG
763  hints.ai_flags |= AI_ADDRCONFIG;
764#endif /* AI_ADDRCONFIG */
765
766  rv = getaddrinfo(NULL, service, &hints, &res);
767  if (rv != 0) {
768    errx(1, "Could not resolve server address");
769  }
770  for (rp = res; rp; rp = rp->ai_next) {
771    struct evconnlistener *listener;
772    listener = evconnlistener_new_bind(
773        evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
774        16, rp->ai_addr, (int)rp->ai_addrlen);
775    if (listener) {
776      freeaddrinfo(res);
777
778      return;
779    }
780  }
781  errx(1, "Could not start listener");
782}
783
784static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
785                                   struct event_base *evbase) {
786  memset(app_ctx, 0, sizeof(app_context));
787  app_ctx->ssl_ctx = ssl_ctx;
788  app_ctx->evbase = evbase;
789}
790
791static void run(const char *service, const char *key_file,
792                const char *cert_file) {
793  SSL_CTX *ssl_ctx;
794  app_context app_ctx;
795  struct event_base *evbase;
796
797  ssl_ctx = create_ssl_ctx(key_file, cert_file);
798  evbase = event_base_new();
799  initialize_app_context(&app_ctx, ssl_ctx, evbase);
800  start_listen(evbase, service, &app_ctx);
801
802  event_base_loop(evbase, 0);
803
804  event_base_free(evbase);
805  SSL_CTX_free(ssl_ctx);
806}
807
808int main(int argc, char **argv) {
809  struct sigaction act;
810
811  if (argc < 4) {
812    fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
813    exit(EXIT_FAILURE);
814  }
815
816  memset(&act, 0, sizeof(struct sigaction));
817  act.sa_handler = SIG_IGN;
818  sigaction(SIGPIPE, &act, NULL);
819
820#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
821  /* No explicit initialization is required. */
822#elif defined(OPENSSL_IS_BORINGSSL)
823  CRYPTO_library_init();
824#else  /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) &&                          \
825          !defined(OPENSSL_IS_BORINGSSL) */
826  OPENSSL_config(NULL);
827  SSL_load_error_strings();
828  SSL_library_init();
829  OpenSSL_add_all_algorithms();
830#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) &&                          \
831          !defined(OPENSSL_IS_BORINGSSL) */
832
833  run(argv[1], argv[2], argv[3]);
834  return 0;
835}
836