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