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