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