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