1 /* http_h2.c - HTTP/2 support functions
2 *
3 * Copyright (c) 1994-2018 Carnegie Mellon University. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The name "Carnegie Mellon University" must not be used to
18 * endorse or promote products derived from this software without
19 * prior written permission. For permission or any legal
20 * details, please contact
21 * Carnegie Mellon University
22 * Center for Technology Transfer and Enterprise Creation
23 * 4615 Forbes Avenue
24 * Suite 302
25 * Pittsburgh, PA 15213
26 * (412) 268-7393, fax: (412) 268-7395
27 * innovation@andrew.cmu.edu
28 *
29 * 4. Redistributions of any form whatsoever must retain the following
30 * acknowledgment:
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33 *
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41 *
42 */
43
44 #include <config.h>
45 #include <sysexits.h>
46
47 #include "httpd.h"
48 #include "md5.h"
49 #include "util.h"
50
51 int (*alpn_select_cb)(SSL *ssl,
52 const unsigned char **out, unsigned char *outlen,
53 const unsigned char *in, unsigned int inlen,
54 void *arg) = NULL;
55
56 #ifdef HAVE_NGHTTP2
57
58 #include <errno.h>
59 #include <syslog.h>
60
61 #include <sasl/saslutil.h>
62
63 #include "http_h2.h"
64 #include "http_ws.h"
65 #include "prometheus.h"
66 #include "retry.h"
67
68 /* generated headers are not necessarily in current directory */
69 #include "imap/http_err.h"
70
71 #define HTTP2_MAX_HEADERS 100
72
73 /* HTTP/2 session context */
74 struct http2_context {
75 nghttp2_session *session; /* HTTP/2 session */
76 nghttp2_option *options; /* Config options for HTTP/2 session */
77 unsigned ws_count; /* Count of WebSocket streams */
78 };
79
80 /* HTTP/2 stream context */
81 struct http2_stream {
82 int32_t id; /* Stream ID */
83 size_t num_resp_hdrs; /* Number of response headers */
84 nghttp2_nv resp_hdrs[HTTP2_MAX_HEADERS]; /* Array of response headers */
85 };
86
87 static nghttp2_session_callbacks *http2_callbacks = NULL;
88
_alpn_select_cb(SSL * ssl,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)89 static int _alpn_select_cb(SSL *ssl __attribute__((unused)),
90 const unsigned char **out, unsigned char *outlen,
91 const unsigned char *in, unsigned int inlen,
92 void *arg)
93 {
94 struct http_connection *http_conn = (struct http_connection *) arg;
95
96 int r = nghttp2_select_next_protocol((u_char **) out, outlen, in, inlen);
97
98 switch (r) {
99 case 0: /* http/1.1 */
100 break;
101
102 case 1: /* h2 */
103 if (http2_start_session(NULL, http_conn) == 0) break;
104
105 /* Fall through as unsupported */
106 GCC_FALLTHROUGH
107
108 default:
109 return SSL_TLSEXT_ERR_NOACK;
110 }
111
112 return SSL_TLSEXT_ERR_OK;
113 }
114
send_cb(nghttp2_session * session,const uint8_t * data,size_t length,int flags,void * user_data)115 static ssize_t send_cb(nghttp2_session *session __attribute__((unused)),
116 const uint8_t *data, size_t length,
117 int flags __attribute__((unused)),
118 void *user_data)
119 {
120 struct http_connection *conn = (struct http_connection *) user_data;
121 struct protstream *pout = conn->pout;
122 int r;
123
124 r = prot_write(pout, (const char *) data, length);
125
126 syslog(LOG_DEBUG, "http2_send_cb(%zu): %d", length, r);
127
128 if (r) return NGHTTP2_ERR_CALLBACK_FAILURE;
129
130 return length;
131 }
132
recv_cb(nghttp2_session * session,uint8_t * buf,size_t length,int flags,void * user_data)133 static ssize_t recv_cb(nghttp2_session *session __attribute__((unused)),
134 uint8_t *buf, size_t length,
135 int flags __attribute__((unused)),
136 void *user_data)
137 {
138 struct http_connection *conn = (struct http_connection *) user_data;
139 struct protstream *pin = conn->pin;
140 ssize_t n;
141
142
143 n = prot_read(pin, (char *) buf, length);
144 if (n) {
145 /* We received some data - don't block next time
146 Note: This callback gets called multiple times until it
147 would block. We don't actually want to block and prevent
148 output from being submitted */
149 prot_NONBLOCK(pin);
150 }
151 else if (!((struct http2_context *) conn->sess_ctx)->ws_count) {
152 /* No data (and no WebSockets) - block next time (for client timeout) */
153 prot_BLOCK(pin);
154
155 if (pin->eof) n = NGHTTP2_ERR_EOF;
156 else if (pin->error) n = NGHTTP2_ERR_CALLBACK_FAILURE;
157 else n = NGHTTP2_ERR_WOULDBLOCK;
158 }
159
160 syslog(LOG_DEBUG,
161 "http2_recv_cb(%zu): n = %zd, eof = %d, err = '%s', errno = %m",
162 length, n, pin->eof, pin->error ? pin->error : "");
163
164 return n;
165 }
166
data_source_read_cb(nghttp2_session * sess,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)167 static ssize_t data_source_read_cb(nghttp2_session *sess __attribute__((unused)),
168 int32_t stream_id,
169 uint8_t *buf, size_t length,
170 uint32_t *data_flags,
171 nghttp2_data_source *source,
172 void *user_data __attribute__((unused)))
173 {
174 struct protstream *s = source->ptr;
175 size_t n = prot_read(s, (char *) buf, length);
176
177 syslog(LOG_DEBUG,
178 "http2_data_source_read_cb(id=%d, len=%zu): n=%zu, eof=%d",
179 stream_id, length, n, !s->cnt);
180
181 if (!s->cnt) {
182 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
183 prot_free(s); /* Done with the protstream */
184 }
185
186 return n;
187 }
188
begin_headers_cb(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)189 static int begin_headers_cb(nghttp2_session *session,
190 const nghttp2_frame *frame, void *user_data)
191 {
192 if (frame->hd.type != NGHTTP2_HEADERS ||
193 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
194 return 0;
195 }
196
197 syslog(LOG_DEBUG, "http2_begin_headers_cb(id=%d, type=%d)",
198 frame->hd.stream_id, frame->hd.type);
199
200 struct transaction_t *txn = xzmalloc(sizeof(struct transaction_t));
201
202 txn->conn = (struct http_connection *) user_data;
203 txn->meth = METH_UNKNOWN;
204 txn->flags.ver = VER_2;
205 txn->flags.vary = VARY_AE;
206 txn->req_line.ver = HTTP2_VERSION;
207
208 if (config_getswitch(IMAPOPT_HTTPALLOWCOMPRESS)) {
209 txn->zstrm = zlib_init();
210 txn->brotli = brotli_init();
211 txn->zstd = zstd_init();
212 }
213
214
215 struct http2_stream *strm = xzmalloc(sizeof(struct http2_stream));
216
217 strm->id = frame->hd.stream_id;
218 txn->strm_ctx = strm;
219
220 /* Create header cache */
221 if (!(txn->req_hdrs = spool_new_hdrcache())) {
222 syslog(LOG_ERR, "Unable to create header cache");
223 return NGHTTP2_ERR_CALLBACK_FAILURE;
224 }
225
226 nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, txn);
227
228 return 0;
229 }
230
header_cb(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)231 static int header_cb(nghttp2_session *session,
232 const nghttp2_frame *frame,
233 const uint8_t *name, size_t namelen,
234 const uint8_t *value, size_t valuelen,
235 uint8_t flags __attribute__((unused)),
236 void *user_data __attribute__((unused)))
237 {
238 if (frame->hd.type != NGHTTP2_HEADERS ||
239 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
240 return 0;
241 }
242
243 char *my_name, *my_value;
244 struct transaction_t *txn =
245 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
246
247 if (!txn) return 0;
248
249 my_name = xstrndup((const char *) name, namelen);
250 my_value = xstrndup((const char *) value, valuelen);
251
252 syslog(LOG_DEBUG, "http2_header_cb(%s: %s)", my_name, my_value);
253
254 if (my_name[0] == ':') {
255 switch (my_name[1]) {
256 case 'm': /* :method */
257 if (!strcmp("ethod", my_name+2)) txn->req_line.meth = my_value;
258 break;
259
260 case 's': /* :scheme */
261 break;
262
263 case 'a': /* :authority */
264 break;
265
266 case 'p': /* :path, :protocol */
267 if (!strcmp("ath", my_name+2)) txn->req_line.uri = my_value;
268 else if (!strcmp("rotocol", my_name+2) &&
269 !strcmp(my_value, WS_TOKEN)) {
270 txn->flags.upgrade |= UPGRADE_WS;
271 }
272 break;
273 }
274 }
275
276 spool_cache_header(my_name, my_value, txn->req_hdrs);
277
278 return 0;
279 }
280
data_chunk_recv_cb(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)281 static int data_chunk_recv_cb(nghttp2_session *session,
282 uint8_t flags __attribute__((unused)),
283 int32_t stream_id,
284 const uint8_t *data, size_t len,
285 void *user_data __attribute__((unused)))
286 {
287 struct transaction_t *txn =
288 nghttp2_session_get_stream_user_data(session, stream_id);
289
290 if (!txn) return 0;
291
292 syslog(LOG_DEBUG, "http2_data_chunk_recv_cb(id=%d, len=%zu, txnflags=%#x)",
293 stream_id, len, txn->req_body.flags);
294
295 if (txn->req_body.flags & BODY_DISCARD) return 0;
296
297 if (len) {
298 txn->req_body.framing = FRAMING_HTTP2;
299 txn->req_body.len += len;
300 buf_appendmap(&txn->req_body.payload, (const char *) data, len);
301 }
302
303 return 0;
304 }
305
frame_recv_cb(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)306 static int frame_recv_cb(nghttp2_session *session,
307 const nghttp2_frame *frame,
308 void *user_data __attribute__((unused)))
309 {
310 int ret = 0;
311 struct transaction_t *txn =
312 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
313 struct http2_context *ctx;
314 struct buf *logbuf = NULL;
315
316 if (!txn) return 0;
317
318 ctx = (struct http2_context *) txn->conn->sess_ctx;
319
320 syslog(LOG_DEBUG, "http2_frame_recv_cb(id=%d, type=%d, flags=%#x)",
321 frame->hd.stream_id, frame->hd.type, frame->hd.flags);
322
323 if ((txn->conn->logfd != -1) && (frame->hd.type <= NGHTTP2_HEADERS)) {
324 /* telemetry log */
325 logbuf = &txn->conn->logbuf;
326
327 buf_reset(logbuf);
328 buf_printf(logbuf, "<" TIME_T_FMT "<", time(NULL)); /* timestamp */
329 write(txn->conn->logfd, buf_base(logbuf), buf_len(logbuf));
330 }
331
332 switch (frame->hd.type) {
333 case NGHTTP2_HEADERS:
334 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
335 if (txn->conn->logfd != -1) {
336 /* telemetry log */
337 buf_reset(logbuf);
338 buf_printf(logbuf, "%s %s %s\r\n", /* request-line*/
339 txn->req_line.meth, txn->req_line.uri, HTTP2_VERSION);
340 spool_enum_hdrcache(txn->req_hdrs, /* header fields */
341 &log_cachehdr, logbuf);
342 buf_appendcstr(logbuf, "\r\n"); /* CRLF */
343 write(txn->conn->logfd, buf_base(logbuf), buf_len(logbuf));
344 }
345
346 /* Examine request */
347 ret = examine_request(txn, NULL);
348
349 if (ret) {
350 txn->req_body.flags |= BODY_DISCARD;
351 error_response(ret, txn);
352 break;
353 }
354
355 if (txn->req_body.flags & BODY_CONTINUE) {
356 txn->req_body.flags &= ~BODY_CONTINUE;
357 response_header(HTTP_CONTINUE, txn);
358 break;
359 }
360 }
361
362 GCC_FALLTHROUGH
363
364 case NGHTTP2_DATA:
365 if (txn->ws_ctx) {
366 /* WebSocket over HTTP/2 input */
367 ws_input(txn);
368
369 if (txn->flags.conn & CONN_CLOSE) {
370 /* Issue RST_STREAM so that stream does not hang around. */
371 syslog(LOG_DEBUG, "nghttp2_submit_rst stream()");
372 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
373 frame->hd.stream_id,
374 NGHTTP2_NO_ERROR);
375 }
376 break;
377 }
378
379 if (txn->conn->logfd != -1) {
380 /* telemetry log */
381 write(txn->conn->logfd, buf_base(&txn->req_body.payload),
382 buf_len(&txn->req_body.payload));
383 }
384
385 if (txn->meth != METH_CONNECT) {
386 /* Check that the client request has finished */
387 if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) break;
388
389 /* Check that we still want to process the request */
390 if (txn->req_body.flags & BODY_DISCARD) break;
391 }
392
393 /* Process the requested method */
394 ret = process_request(txn);
395
396 /* Handle errors (success responses handled by method functions) */
397 if (ret) error_response(ret, txn);
398
399 if (txn->flags.conn & CONN_CLOSE) {
400 int32_t stream_id =
401 nghttp2_session_get_last_proc_stream_id(ctx->session);
402
403 syslog(LOG_DEBUG, "nghttp2_submit_goaway()");
404 nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, stream_id,
405 NGHTTP2_NO_ERROR, NULL, 0);
406 }
407 else if (txn->ws_ctx) {
408 /* Increment WebSocket stream count */
409 ctx->ws_count++;
410 }
411
412 break;
413 }
414
415 return 0;
416 }
417
stream_close_cb(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)418 static int stream_close_cb(nghttp2_session *session, int32_t stream_id,
419 uint32_t error_code,
420 void *user_data __attribute__((unused)))
421 {
422 struct transaction_t *txn =
423 nghttp2_session_get_stream_user_data(session, stream_id);
424
425 syslog(LOG_DEBUG, "http2_stream_close_cb(id=%d): '%s'",
426 stream_id, nghttp2_http2_strerror(error_code));
427
428 if (txn) {
429 if (txn->ws_ctx) {
430 /* Decrement WebSocket stream count */
431 struct http2_context *http2_ctx =
432 (struct http2_context *) txn->conn->sess_ctx;
433 if (--http2_ctx->ws_count == 0) {
434 /* No WebSockets - block next time (for client timeout) */
435 prot_BLOCK(txn->conn->pin);
436 }
437 }
438
439 /* Memory cleanup */
440 transaction_free(txn);
441 free(txn);
442 }
443
444 return 0;
445 }
446
frame_not_send_cb(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * user_data)447 static int frame_not_send_cb(nghttp2_session *session,
448 const nghttp2_frame *frame,
449 int lib_error_code,
450 void *user_data __attribute__((unused)))
451 {
452 syslog(LOG_DEBUG, "http2_frame_not_send_cb(id=%d, type=%d, flags=%#x)",
453 frame->hd.stream_id, frame->hd.type, frame->hd.flags);
454
455 /* Issue RST_STREAM so that stream does not hang around. */
456 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
457 frame->hd.stream_id, lib_error_code);
458
459 return 0;
460 }
461
frame_send_cb(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)462 static int frame_send_cb(nghttp2_session *session __attribute__((unused)),
463 const nghttp2_frame *frame,
464 void *user_data __attribute__((unused)))
465 {
466 syslog(LOG_DEBUG, "http2_frame_send_cb(id=%d, type=%d, flags=%#x)",
467 frame->hd.stream_id, frame->hd.type, frame->hd.flags);
468
469 return 0;
470 }
471
begin_frame_cb(nghttp2_session * session,const nghttp2_frame_hd * hd,void * user_data)472 static int begin_frame_cb(nghttp2_session *session __attribute__((unused)),
473 const nghttp2_frame_hd *hd,
474 void *user_data __attribute__((unused)))
475 {
476 syslog(LOG_DEBUG, "http2_begin_frame_cb(id=%d, type=%d, flags=%#x)",
477 hd->stream_id, hd->type, hd->flags);
478
479 return 0;
480 }
481
482
http2_init(struct buf * serverinfo)483 HIDDEN void http2_init(struct buf *serverinfo)
484 {
485 int r;
486
487 buf_printf(serverinfo, " Nghttp2/%s", NGHTTP2_VERSION);
488
489 /* Setup HTTP/2 callbacks */
490 if ((r = nghttp2_session_callbacks_new(&http2_callbacks))) {
491 syslog(LOG_WARNING,
492 "nghttp2_session_callbacks_new: %s", nghttp2_strerror(r));
493 return;
494 }
495
496 nghttp2_session_callbacks_set_send_callback(http2_callbacks,
497 &send_cb);
498 nghttp2_session_callbacks_set_recv_callback(http2_callbacks,
499 &recv_cb);
500 nghttp2_session_callbacks_set_on_begin_headers_callback(http2_callbacks,
501 &begin_headers_cb);
502 nghttp2_session_callbacks_set_on_header_callback(http2_callbacks,
503 &header_cb);
504 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(http2_callbacks,
505 &data_chunk_recv_cb);
506 nghttp2_session_callbacks_set_on_frame_recv_callback(http2_callbacks,
507 &frame_recv_cb);
508 nghttp2_session_callbacks_set_on_stream_close_callback(http2_callbacks,
509 &stream_close_cb);
510 nghttp2_session_callbacks_set_on_frame_not_send_callback(http2_callbacks,
511 &frame_not_send_cb);
512
513 if (config_getswitch(IMAPOPT_DEBUG)) {
514 nghttp2_session_callbacks_set_on_begin_frame_callback(http2_callbacks,
515 &begin_frame_cb);
516 nghttp2_session_callbacks_set_on_frame_send_callback(http2_callbacks,
517 &frame_send_cb);
518 }
519
520 /* Setup for ALPN */
521 alpn_select_cb = &_alpn_select_cb;
522 }
523
524
http2_enabled()525 HIDDEN int http2_enabled()
526 {
527 return (http2_callbacks != NULL);
528 }
529
http2_done()530 HIDDEN void http2_done()
531 {
532 nghttp2_session_callbacks_del(http2_callbacks);
533 }
534
535
http2_preface(struct http_connection * conn)536 HIDDEN int http2_preface(struct http_connection *conn)
537 {
538 if (http2_enabled()) {
539 /* Check initial client input for HTTP/2 preface */
540 int c;
541
542 if (prot_lookahead(conn->pin,
543 NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN, &c)) {
544 syslog(LOG_DEBUG, "HTTP/2 client connection preface");
545 return 1;
546 }
547 }
548
549 return 0;
550 }
551
552
553 #if HAVE_DECL_NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL
554 static int nghttp2_extended_connect = 1;
555 #else
556 #define NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL 0x08
557 static int nghttp2_extended_connect = 0;
558 #endif
559
http2_start_session(struct transaction_t * txn,struct http_connection * conn)560 HIDDEN int http2_start_session(struct transaction_t *txn,
561 struct http_connection *conn)
562 {
563 nghttp2_settings_entry iv[] = {
564 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
565 { NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL, 1 } /* MUST be last */
566 };
567 size_t niv = (sizeof(iv) / sizeof(iv[0])) - !ws_enabled();
568 struct http2_context *ctx;
569 int r;
570
571 if (!conn) conn = txn->conn;
572
573 if (conn->sess_ctx) return 0;
574
575 ctx = xzmalloc(sizeof(struct http2_context));
576
577 r = nghttp2_option_new(&ctx->options);
578 if (r) {
579 syslog(LOG_WARNING,
580 "nghttp2_option_new: %s", nghttp2_strerror(r));
581 free(ctx);
582 return HTTP_SERVER_ERROR;
583 }
584
585 if (ws_enabled() && !nghttp2_extended_connect) {
586 /* Need to forcefully allow :scheme, :path, :protocol pseudo-headers
587 (this version of libnghttp2 doesn't understand extended CONNECT) */
588 nghttp2_option_set_no_http_messaging(ctx->options, 1);
589 }
590
591 r = nghttp2_session_server_new2(&ctx->session,
592 http2_callbacks, conn, ctx->options);
593 if (r) {
594 syslog(LOG_WARNING,
595 "nghttp2_session_server_new2: %s", nghttp2_strerror(r));
596 free(ctx);
597 return HTTP_SERVER_ERROR;
598 }
599
600 conn->sess_ctx = ctx;
601
602 if (txn && (txn->flags.conn & CONN_UPGRADE)) {
603 struct http2_stream *strm;
604 struct buf *buf = &txn->buf;
605 unsigned outlen;
606
607 const char **hdr = spool_getheader(txn->req_hdrs, "HTTP2-Settings");
608 if (!hdr || hdr[1]) return 0;
609
610 /* base64url decode the settings.
611 Use the SASL base64 decoder after replacing the encoded values
612 for chars 62 and 63 and adding appropriate padding. */
613 buf_setcstr(buf, hdr[0]);
614 buf_replace_char(buf, '-', '+');
615 buf_replace_char(buf, '_', '/');
616 buf_appendmap(buf, "==", (4 - (buf_len(buf) % 4)) % 4);
617 r = sasl_decode64(buf_base(buf), buf_len(buf),
618 (char *) buf_base(buf), buf_len(buf), &outlen);
619 if (r != SASL_OK) {
620 syslog(LOG_WARNING, "sasl_decode64 failed: %s",
621 sasl_errstring(r, NULL, NULL));
622 }
623 else {
624 r = nghttp2_session_upgrade2(ctx->session,
625 (const uint8_t *) buf_base(buf),
626 outlen, txn->meth == METH_HEAD, NULL);
627 if (r) {
628 syslog(LOG_WARNING, "nghttp2_session_upgrade: %s",
629 nghttp2_strerror(r));
630 }
631 }
632
633 buf_reset(buf);
634 if (r) return HTTP_BAD_REQUEST;
635
636 /* tell client to start h2c upgrade (RFC 7540) */
637 response_header(HTTP_SWITCH_PROT, txn);
638
639 strm = xzmalloc(sizeof(struct http2_stream));
640 strm->id = nghttp2_session_get_last_proc_stream_id(ctx->session);
641 txn->strm_ctx = strm;
642 txn->flags.ver = VER_2;
643 }
644
645 /* Don't do telemetry logging in prot layer */
646 prot_setlog(conn->pin, PROT_NO_FD);
647 prot_setlog(conn->pout, PROT_NO_FD);
648
649 tcp_disable_nagle(1); /* output fd */
650
651 r = nghttp2_submit_settings(ctx->session, NGHTTP2_FLAG_NONE, iv, niv);
652 if (r) {
653 syslog(LOG_ERR, "nghttp2_submit_settings: %s", nghttp2_strerror(r));
654 return HTTP_SERVER_ERROR;
655 }
656
657 return 0;
658 }
659
660
http2_end_session(void * http2_ctx)661 HIDDEN void http2_end_session(void *http2_ctx)
662 {
663 struct http2_context *ctx = (struct http2_context *) http2_ctx;
664
665 if (!ctx) return;
666
667 nghttp2_option_del(ctx->options);
668 nghttp2_session_del(ctx->session);
669 free(ctx);
670 }
671
672
http2_output(struct transaction_t * txn)673 HIDDEN void http2_output(struct transaction_t *txn)
674 {
675 struct http2_context *ctx = (struct http2_context *) txn->conn->sess_ctx;
676
677 if (nghttp2_session_want_write(ctx->session)) {
678 /* Send queued frame(s) */
679 int r = nghttp2_session_send(ctx->session);
680 if (r) {
681 syslog(LOG_ERR, "nghttp2_session_send: %s", nghttp2_strerror(r));
682 txn->flags.conn = CONN_CLOSE;
683 }
684 }
685 else if (!nghttp2_session_want_read(ctx->session)) {
686 /* We're done */
687 syslog(LOG_DEBUG, "closing connection");
688 txn->flags.conn = CONN_CLOSE;
689 return;
690 }
691 }
692
693
http2_input(struct transaction_t * txn)694 HIDDEN void http2_input(struct transaction_t *txn)
695 {
696 struct http2_context *ctx = (struct http2_context *) txn->conn->sess_ctx;
697 int want_read = nghttp2_session_want_read(ctx->session);
698 int goaway = txn->flags.conn & CONN_CLOSE;
699 nghttp2_error_code err = goaway ? NGHTTP2_REFUSED_STREAM : NGHTTP2_NO_ERROR;
700 struct protstream *pin = txn->conn->pin;
701
702 syslog(LOG_DEBUG, "http2_input() goaway: %d, eof: %d, want read: %d",
703 goaway, prot_IS_EOF(pin), want_read);
704
705
706 if (want_read && !goaway) {
707 /* Read frame(s) */
708 int r = nghttp2_session_recv(ctx->session);
709
710 if (!r) {
711 /* Successfully received frames */
712 syslog(LOG_DEBUG, "nghttp2_session_recv: success");
713 }
714 else if (r == NGHTTP2_ERR_EOF) {
715 /* Client closed connection */
716 syslog(LOG_DEBUG, "client closed connection");
717 txn->flags.conn = CONN_CLOSE;
718 }
719 else {
720 /* Failure */
721 syslog(LOG_DEBUG, "nghttp2_session_recv: %s (%s)",
722 nghttp2_strerror(r), prot_error(pin));
723 goaway = 1;
724
725 if (r == NGHTTP2_ERR_CALLBACK_FAILURE) {
726 /* Client timeout */
727 txn->error.desc = prot_error(pin);
728 err = NGHTTP2_REFUSED_STREAM;
729 }
730 else {
731 txn->error.desc = nghttp2_strerror(r);
732
733 if (r == NGHTTP2_ERR_NOMEM)
734 err = NGHTTP2_INTERNAL_ERROR;
735 else if (r == NGHTTP2_ERR_BAD_CLIENT_MAGIC)
736 err = NGHTTP2_PROTOCOL_ERROR;
737 else if (r == NGHTTP2_ERR_FLOODED)
738 err = NGHTTP2_ENHANCE_YOUR_CALM;
739 }
740 }
741 }
742
743 if (goaway) {
744 /* Tell client we are closing session */
745 int32_t stream_id =
746 nghttp2_session_get_last_proc_stream_id(ctx->session);
747
748 syslog(LOG_WARNING, "%s, closing connection", txn->error.desc);
749
750 syslog(LOG_DEBUG, "nghttp2_submit_goaway()");
751 int r = nghttp2_submit_goaway(ctx->session,
752 NGHTTP2_FLAG_NONE, stream_id, err,
753 (const uint8_t *) txn->error.desc,
754 strlen(txn->error.desc));
755 if (r) {
756 syslog(LOG_ERR, "nghttp2_submit_goaway: %s", nghttp2_strerror(r));
757 }
758
759 txn->flags.conn = CONN_CLOSE;
760 }
761
762 /* Write frame(s) */
763 http2_output(txn);
764
765 return;
766 }
767
768
http2_begin_headers(struct transaction_t * txn)769 HIDDEN void http2_begin_headers(struct transaction_t *txn)
770 {
771 struct http2_stream *strm = (struct http2_stream *) txn->strm_ctx;
772
773 strm->num_resp_hdrs = 0;
774
775 if (txn->conn->logfd != -1) {
776 /* telemetry log */
777 struct buf *logbuf = &txn->conn->logbuf;
778
779 buf_reset(logbuf);
780 buf_printf(logbuf, ">" TIME_T_FMT ">", time(NULL)); /* timestamp */
781 write(txn->conn->logfd, buf_base(logbuf), buf_len(logbuf));
782 }
783 }
784
785
http2_add_header(struct transaction_t * txn,const char * name,struct buf * value)786 HIDDEN void http2_add_header(struct transaction_t *txn,
787 const char *name, struct buf *value)
788 {
789 struct http2_stream *strm = (struct http2_stream *) txn->strm_ctx;
790
791 if (strm->num_resp_hdrs >= HTTP2_MAX_HEADERS) {
792 buf_free(value);
793 return;
794 }
795 else {
796 nghttp2_nv *nv = &strm->resp_hdrs[strm->num_resp_hdrs];
797
798 free(nv->value);
799
800 nv->namelen = strlen(name);
801 nv->name = (uint8_t *) name;
802 nv->valuelen = buf_len(value);
803 nv->value = (uint8_t *) buf_release(value);
804 nv->flags = NGHTTP2_NV_FLAG_NO_COPY_VALUE;
805
806 strm->num_resp_hdrs++;
807
808 if (txn->conn->logfd != -1) {
809 /* telemetry log */
810 struct iovec iov[4];
811 int niov = 0;
812
813 if (name[0] == ':') {
814 /* :status */
815 WRITEV_ADD_TO_IOVEC(iov, niov, "HTTP/2 ", 7);
816 }
817 else {
818 WRITEV_ADD_TO_IOVEC(iov, niov, nv->name, nv->namelen);
819 WRITEV_ADD_TO_IOVEC(iov, niov, ": ", 2);
820 }
821 WRITEV_ADD_TO_IOVEC(iov, niov, nv->value, nv->valuelen);
822 WRITEV_ADD_TO_IOVEC(iov, niov, "\r\n", 2);
823 writev(txn->conn->logfd, iov, niov);
824 }
825 }
826 }
827
828
http2_end_headers(struct transaction_t * txn,long code)829 HIDDEN int http2_end_headers(struct transaction_t *txn, long code)
830 {
831 struct http2_context *ctx = (struct http2_context *) txn->conn->sess_ctx;
832 struct http2_stream *strm = (struct http2_stream *) txn->strm_ctx;
833
834 uint8_t flags = NGHTTP2_FLAG_NONE;
835 int r;
836
837 syslog(LOG_DEBUG,
838 "end_resp_headers(code = %ld, len = %ld, flags.te = %#x)",
839 code, txn->resp_body.len, txn->flags.te);
840
841 if (txn->conn->logfd != -1) {
842 /* telemetry log */
843 write(txn->conn->logfd, "\r\n", 2);
844 }
845
846 switch (code) {
847 case 0:
848 /* Trailer */
849 flags = NGHTTP2_FLAG_END_STREAM;
850 break;
851
852 case HTTP_CONTINUE:
853 case HTTP_PROCESSING:
854 /* Provisional response */
855 break;
856
857 case HTTP_NO_CONTENT:
858 case HTTP_NOT_MODIFIED:
859 /* MUST NOT include a body */
860 flags = NGHTTP2_FLAG_END_STREAM;
861 break;
862
863 default:
864 if (txn->meth == METH_HEAD) {
865 /* MUST NOT include a body */
866 flags = NGHTTP2_FLAG_END_STREAM;
867 }
868 else if (!(txn->resp_body.len || (txn->flags.te & TE_CHUNKED))) {
869 /* Empty body */
870 flags = NGHTTP2_FLAG_END_STREAM;
871 }
872 break;
873 }
874
875 syslog(LOG_DEBUG, "%s(id=%d, flags=%#x)",
876 code ? "nghttp2_submit headers" : "nghttp2_submit_trailers",
877 strm->id, flags);
878
879 if (code) {
880 r = nghttp2_submit_headers(ctx->session, flags, strm->id, NULL,
881 strm->resp_hdrs, strm->num_resp_hdrs, NULL);
882 }
883 else {
884 r = nghttp2_submit_trailer(ctx->session, strm->id,
885 strm->resp_hdrs, strm->num_resp_hdrs);
886 }
887 if (r) {
888 syslog(LOG_ERR, "%s: %s",
889 code ? "nghttp2_submit headers" : "nghttp2_submit_trailers",
890 nghttp2_strerror(r));
891 }
892
893 return r;
894 }
895
896
http2_data_chunk(struct transaction_t * txn,const char * data,unsigned datalen,int last_chunk,MD5_CTX * md5ctx)897 HIDDEN int http2_data_chunk(struct transaction_t *txn,
898 const char *data, unsigned datalen,
899 int last_chunk, MD5_CTX *md5ctx)
900 {
901 static unsigned char md5[MD5_DIGEST_LENGTH];
902 struct http2_context *ctx = (struct http2_context *) txn->conn->sess_ctx;
903 struct http2_stream *strm = (struct http2_stream *) txn->strm_ctx;
904 uint8_t flags = NGHTTP2_FLAG_END_STREAM;
905 nghttp2_data_provider prd;
906 int r;
907
908 syslog(LOG_DEBUG, "http2_data_chunk(datalen=%u, last=%d)",
909 datalen, last_chunk);
910
911 if (datalen && (txn->conn->logfd != -1)) {
912 /* telemetry log */
913 struct buf *logbuf = &txn->conn->logbuf;
914 struct iovec iov[2];
915 int niov = 0;
916
917 buf_reset(logbuf);
918 buf_printf(logbuf, ">" TIME_T_FMT ">", time(NULL)); /* timestamp */
919 WRITEV_ADD_TO_IOVEC(iov, niov,
920 buf_base(logbuf), buf_len(logbuf));
921 WRITEV_ADD_TO_IOVEC(iov, niov, data, datalen);
922 writev(txn->conn->logfd, iov, niov);
923 }
924
925 /* NOTE: The protstream that we use as the data source MUST remain
926 available until the data source read callback has retrieved all data.
927 Also note that we need to make a copy of the data because data frames
928 may not be sent prior to the original pointer becoming invalid.
929 */
930 struct protstream *s = prot_readmap(xmemdup(data, datalen), datalen);
931 s->buf = s->ptr;
932 s->buf_size = datalen;
933
934 prd.source.ptr = s;
935 prd.read_callback = data_source_read_cb;
936
937 if (txn->flags.te) {
938 if (!last_chunk) {
939 flags = NGHTTP2_FLAG_NONE;
940 if (datalen && (txn->flags.trailer & TRAILER_CMD5)) {
941 MD5Update(md5ctx, data, datalen);
942 }
943 }
944 else if (txn->flags.trailer) {
945 flags = NGHTTP2_FLAG_NONE;
946 if (txn->flags.trailer & TRAILER_CMD5) MD5Final(md5, md5ctx);
947 }
948 }
949
950 syslog(LOG_DEBUG, "nghttp2_submit_data(id=%d, datalen=%d, flags=%#x)",
951 strm->id, datalen, flags);
952
953 r = nghttp2_submit_data(ctx->session, flags, strm->id, &prd);
954 if (r) {
955 syslog(LOG_ERR, "nghttp2_submit_data: %s", nghttp2_strerror(r));
956 return HTTP_SERVER_ERROR;
957 }
958 else {
959 http2_output(txn);
960
961 if (last_chunk && (txn->flags.trailer & ~TRAILER_PROXY)) {
962 begin_resp_headers(txn, 0);
963 if (txn->flags.trailer & TRAILER_CMD5) content_md5_hdr(txn, md5);
964 if ((txn->flags.trailer & TRAILER_CTAG) && txn->resp_body.ctag) {
965 simple_hdr(txn, "CTag", "%s", txn->resp_body.ctag);
966 }
967 end_resp_headers(txn, 0);
968 }
969 }
970
971 return 0;
972 }
973
http2_get_streamid(void * http2_strm)974 HIDDEN int32_t http2_get_streamid(void *http2_strm)
975 {
976 struct http2_stream *strm = (struct http2_stream *) http2_strm;
977
978 return strm ? strm->id : 0;
979 }
980
http2_end_stream(void * http2_strm)981 HIDDEN void http2_end_stream(void *http2_strm)
982 {
983 struct http2_stream *strm = (struct http2_stream *) http2_strm;
984 int i;
985
986 if (!strm) return;
987
988 for (i = 0; i < HTTP2_MAX_HEADERS; i++) {
989 free(strm->resp_hdrs[i].value);
990 }
991 free(strm);
992 }
993
994 #else /* !HAVE_NGHTTP2 */
995
http2_init(struct buf * serverinfo)996 HIDDEN void http2_init(struct buf *serverinfo __attribute__((unused))) {}
997
http2_enabled()998 HIDDEN int http2_enabled()
999 {
1000 return 0;
1001 }
1002
http2_done()1003 HIDDEN void http2_done() {}
1004
http2_preface(struct http_connection * conn)1005 HIDDEN int http2_preface(struct http_connection *conn __attribute__((unused)))
1006 {
1007 return 0;
1008 }
1009
http2_start_session(struct transaction_t * txn,struct http_connection * c)1010 HIDDEN int http2_start_session(struct transaction_t *txn __attribute__((unused)),
1011 struct http_connection *c __attribute__((unused)))
1012 {
1013 fatal("http2_start() called, but no Nghttp2", EX_SOFTWARE);
1014 }
1015
http2_end_session(void * http2_ctx)1016 HIDDEN void http2_end_session(void *http2_ctx __attribute__((unused)))
1017 {
1018 }
1019
http2_output(struct transaction_t * txn)1020 HIDDEN void http2_output(struct transaction_t *txn __attribute__((unused)))
1021 {
1022 fatal("http2_output() called, but no Nghttp2", EX_SOFTWARE);
1023 }
1024
http2_input(struct transaction_t * txn)1025 HIDDEN void http2_input(struct transaction_t *txn __attribute__((unused)))
1026 {
1027 fatal("http2_input() called, but no Nghttp2", EX_SOFTWARE);
1028 }
1029
http2_begin_headers(struct transaction_t * txn)1030 HIDDEN int http2_begin_headers(struct transaction_t *txn __attribute__((unused)))
1031 {
1032 fatal("http2_begin_headers() called, but no Nghttp2", EX_SOFTWARE);
1033 }
1034
http2_add_header(struct transaction_t * txn,const char * name,struct buf * value)1035 HIDDEN void http2_add_header(struct transaction_t *txn __attribute__((unused)),
1036 const char *name __attribute__((unused)),
1037 struct buf *value __attribute__((unused)))
1038 {
1039 fatal("http2_add_header() called, but no Nghttp2", EX_SOFTWARE);
1040 }
1041
http2_end_headers(struct transaction_t * txn,long code)1042 HIDDEN int http2_end_headers(struct transaction_t *txn __attribute__((unused)),
1043 long code __attribute__((unused)))
1044 {
1045 fatal("http2_end_headers() called, but no Nghttp2", EX_SOFTWARE);
1046 }
1047
http2_data_chunk(struct transaction_t * txn,const char * data,unsigned datalen,int last_chunk,MD5_CTX * md5ctx)1048 HIDDEN int http2_data_chunk(struct transaction_t *txn __attribute__((unused)),
1049 const char *data __attribute__((unused)),
1050 unsigned datalen __attribute__((unused)),
1051 int last_chunk __attribute__((unused)),
1052 MD5_CTX *md5ctx __attribute__((unused)))
1053 {
1054 fatal("http2_data_chunk() called, but no Nghttp2", EX_SOFTWARE);
1055 }
1056
http2_get_streamid(void * http2_strm)1057 HIDDEN int32_t http2_get_streamid(void *http2_strm __attribute__((unused)))
1058 {
1059 return 0;
1060 }
1061
http2_end_stream(void * http2_strm)1062 HIDDEN void http2_end_stream(void *http2_strm __attribute__((unused))) {}
1063
1064 #endif /* HAVE_NGHTTP2 */
1065