• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
86 struct app_context;
87 typedef struct app_context app_context;
88 
89 typedef 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 
96 typedef 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 
104 struct app_context {
105   SSL_CTX *ssl_ctx;
106   struct event_base *evbase;
107 };
108 
109 static unsigned char next_proto_list[256];
110 static size_t next_proto_list_len;
111 
112 #ifndef OPENSSL_NO_NEXTPROTONEG
next_proto_cb(SSL * ssl,const unsigned char ** data,unsigned int * len,void * arg)113 static 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
alpn_select_proto_cb(SSL * ssl,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)125 static 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. */
create_ssl_ctx(const char * key_file,const char * cert_file)143 static 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 */
create_ssl(SSL_CTX * ssl_ctx)197 static 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 
add_stream(http2_session_data * session_data,http2_stream_data * stream_data)207 static 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 
remove_stream(http2_session_data * session_data,http2_stream_data * stream_data)217 static 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 
227 static http2_stream_data *
create_http2_stream_data(http2_session_data * session_data,int32_t stream_id)228 create_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 
delete_http2_stream_data(http2_stream_data * stream_data)239 static 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 
create_http2_session_data(app_context * app_ctx,int fd,struct sockaddr * addr,int addrlen)247 static 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 
delete_http2_session_data(http2_session_data * session_data)277 static 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. */
session_send(http2_session_data * session_data)297 static 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. */
session_recv(http2_session_data * session_data)311 static 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 
send_callback(nghttp2_session * session,const uint8_t * data,size_t length,int flags,void * user_data)332 static 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| */
ends_with(const char * s,const char * sub)349 static 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| */
hex_to_uint(uint8_t c)359 static 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. */
percent_decode(const uint8_t * value,size_t valuelen)376 static 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 
file_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)401 static 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 
send_response(nghttp2_session * session,int32_t stream_id,nghttp2_nv * nva,size_t nvlen,int fd)423 static 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 
438 static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
439                                  "<body><h1>404 Not Found</h1></body></html>";
440 
error_reply(nghttp2_session * session,http2_stream_data * stream_data)441 static 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. */
on_header_callback(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)481 static 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 
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)511 static 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. */
check_path(const char * path)529 static 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 
on_request_recv(nghttp2_session * session,http2_session_data * session_data,http2_stream_data * stream_data)536 static 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 
on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)576 static 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 
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)601 static 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 
initialize_nghttp2_session(http2_session_data * session_data)616 static 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 */
send_server_connection_header(http2_session_data * session_data)642 static 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. */
readcb(struct bufferevent * bev,void * ptr)658 static 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(). */
writecb(struct bufferevent * bev,void * ptr)676 static 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 */
eventcb(struct bufferevent * bev,short events,void * ptr)693 static 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 */
acceptcb(struct evconnlistener * listener,int fd,struct sockaddr * addr,int addrlen,void * arg)741 static 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 
start_listen(struct event_base * evbase,const char * service,app_context * app_ctx)752 static 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 
initialize_app_context(app_context * app_ctx,SSL_CTX * ssl_ctx,struct event_base * evbase)784 static 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 
run(const char * service,const char * key_file,const char * cert_file)791 static 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 
main(int argc,char ** argv)808 int 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