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