Coverage Report

Created: 2026-10-17 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/netmgr/http.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <ctype.h>
15
#include <inttypes.h>
16
#include <limits.h>
17
#include <nghttp2/nghttp2.h>
18
#include <signal.h>
19
#include <string.h>
20
21
#include <isc/async.h>
22
#include <isc/base64.h>
23
#include <isc/log.h>
24
#include <isc/netmgr.h>
25
#include <isc/sockaddr.h>
26
#include <isc/tls.h>
27
#include <isc/url.h>
28
#include <isc/util.h>
29
30
#include "netmgr-int.h"
31
32
0
#define AUTHEXTRA 7
33
34
0
#define MAX_DNS_MESSAGE_SIZE (UINT16_MAX)
35
36
0
#define DNS_MEDIA_TYPE "application/dns-message"
37
38
/*
39
 * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
40
 * for additional details. Basically it means "avoid caching by any
41
 * means."
42
 */
43
0
#define DEFAULT_CACHE_CONTROL "no-cache, no-store, must-revalidate"
44
45
/*
46
 * If server during request processing surpasses any of the limits
47
 * below, it will just reset the stream without returning any error
48
 * codes in a response.  Ideally, these parameters should be
49
 * configurable both globally and per every HTTP endpoint description
50
 * in the configuration file, but for now it should be enough.
51
 */
52
53
/*
54
 * 128K should be enough to encode 64K of data into base64url inside GET
55
 * request and have extra space for other headers
56
 */
57
0
#define MAX_ALLOWED_DATA_IN_HEADERS (MAX_DNS_MESSAGE_SIZE * 2)
58
59
#define MAX_ALLOWED_DATA_IN_POST \
60
0
  (MAX_DNS_MESSAGE_SIZE + MAX_DNS_MESSAGE_SIZE / 2)
61
62
#define HEADER_MATCH(header, name, namelen)   \
63
0
  (((namelen) == sizeof(header) - 1) && \
64
0
   (strncasecmp((header), (const char *)(name), (namelen)) == 0))
65
66
0
#define MIN_SUCCESSFUL_HTTP_STATUS (200)
67
0
#define MAX_SUCCESSFUL_HTTP_STATUS (299)
68
69
/* This definition sets the upper limit of pending write buffer to an
70
 * adequate enough value. That is done mostly to fight a limitation
71
 * for a max TLS record size in flamethrower (2K).  In a perfect world
72
 * this constant should not be required, if we ever move closer to
73
 * that state, the constant, and corresponding code, should be
74
 * removed. For now the limit seems adequate enough to fight
75
 * "tinygrams" problem. */
76
0
#define FLUSH_HTTP_WRITE_BUFFER_AFTER (1536)
77
78
/* This switch is here mostly to test the code interoperability with
79
 * buggy implementations */
80
#define ENABLE_HTTP_WRITE_BUFFERING 1
81
82
#define SUCCESSFUL_HTTP_STATUS(code)             \
83
0
  ((code) >= MIN_SUCCESSFUL_HTTP_STATUS && \
84
0
   (code) <= MAX_SUCCESSFUL_HTTP_STATUS)
85
86
0
#define INITIAL_DNS_MESSAGE_BUFFER_SIZE (512)
87
88
/*
89
 * The value should be small enough to not allow a server to open too
90
 * many streams at once. It should not be too small either because
91
 * the incoming data will be split into too many chunks with each of
92
 * them processed asynchronously.
93
 */
94
#define INCOMING_DATA_CHUNK_SIZE (256)
95
96
/*
97
 * Often processing a chunk does not change the number of streams. In
98
 * that case we can process more than once, but we still should have a
99
 * hard limit on that.
100
 */
101
0
#define INCOMING_DATA_MAX_CHUNKS_AT_ONCE (4)
102
103
/*
104
 * These constants define the grace period to help detect flooding clients.
105
 *
106
 * The first one defines how much data can be processed before opening
107
 * a first stream and received at least some useful (=DNS) data.
108
 *
109
 * The second one defines how much data from a client we read before
110
 * trying to drop a clients who sends not enough useful data.
111
 *
112
 * The third constant defines how many streams we agree to process
113
 * before checking if there was at least one DNS request received.
114
 */
115
0
#define INCOMING_DATA_INITIAL_STREAM_SIZE (1536)
116
0
#define INCOMING_DATA_GRACE_SIZE    (MAX_ALLOWED_DATA_IN_HEADERS)
117
0
#define MAX_STREAMS_BEFORE_FIRST_REQUEST  (50)
118
119
typedef struct isc_nm_http_response_status {
120
  size_t code;
121
  size_t content_length;
122
  bool content_type_valid;
123
} isc_nm_http_response_status_t;
124
125
typedef struct http_cstream {
126
  isc_nm_recv_cb_t read_cb;
127
  void *read_cbarg;
128
  isc_nm_cb_t connect_cb;
129
  void *connect_cbarg;
130
131
  bool sending;
132
  bool reading;
133
134
  char *uri;
135
  isc_url_parser_t up;
136
137
  char *authority;
138
  size_t authoritylen;
139
  char *path;
140
141
  isc_buffer_t *rbuf;
142
143
  size_t pathlen;
144
  int32_t stream_id;
145
146
  bool post; /* POST or GET */
147
  isc_buffer_t *postdata;
148
  char *GET_path;
149
  size_t GET_path_len;
150
151
  isc_nm_http_response_status_t response_status;
152
  isc_nmsocket_t *httpsock;
153
  ISC_LINK(struct http_cstream) link;
154
} http_cstream_t;
155
156
0
#define HTTP2_SESSION_MAGIC    ISC_MAGIC('H', '2', 'S', 'S')
157
#define VALID_HTTP2_SESSION(t) ISC_MAGIC_VALID(t, HTTP2_SESSION_MAGIC)
158
159
typedef ISC_LIST(isc__nm_uvreq_t) isc__nm_http_pending_callbacks_t;
160
161
struct isc_nm_http_session {
162
  unsigned int magic;
163
  isc_refcount_t references;
164
  isc_mem_t *mctx;
165
166
  size_t sending;
167
  bool reading;
168
  bool closed;
169
  bool closing;
170
171
  nghttp2_session *ngsession;
172
  bool client;
173
174
  ISC_LIST(http_cstream_t) cstreams;
175
  ISC_LIST(isc_nmsocket_h2_t) sstreams;
176
  size_t nsstreams;
177
  uint64_t total_opened_sstreams;
178
179
  isc_nmhandle_t *handle;
180
  isc_nmhandle_t *client_httphandle;
181
  isc_nmsocket_t *serversocket;
182
183
  isc_buffer_t *buf;
184
185
  isc_tlsctx_t *tlsctx;
186
  uint32_t max_concurrent_streams;
187
188
  isc__nm_http_pending_callbacks_t pending_write_callbacks;
189
  isc_buffer_t *pending_write_data;
190
191
  size_t data_in_flight;
192
193
  bool async_queued;
194
195
  /*
196
   * The statistical values below are for usage on server-side
197
   * only. They are meant to detect clients that are taking too many
198
   * resources from the server.
199
   */
200
  uint64_t received;  /* How many requests have been received. */
201
  uint64_t submitted; /* How many responses were submitted to send */
202
  uint64_t processed; /* How many responses were processed. */
203
204
  uint64_t processed_incoming_data;
205
  uint64_t processed_useful_data; /* DNS data */
206
};
207
208
typedef enum isc_http_error_responses {
209
  ISC_HTTP_ERROR_SUCCESS,          /* 200 */
210
  ISC_HTTP_ERROR_NOT_FOUND,        /* 404 */
211
  ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE,      /* 413 */
212
  ISC_HTTP_ERROR_URI_TOO_LONG,         /* 414 */
213
  ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, /* 415 */
214
  ISC_HTTP_ERROR_BAD_REQUEST,        /* 400 */
215
  ISC_HTTP_ERROR_NOT_IMPLEMENTED,        /* 501 */
216
  ISC_HTTP_ERROR_GENERIC,          /* 500 Internal Server Error */
217
  ISC_HTTP_ERROR_MAX
218
} isc_http_error_responses_t;
219
220
typedef struct isc_http_send_req {
221
  isc_nm_http_session_t *session;
222
  isc_nmhandle_t *transphandle;
223
  isc_nmhandle_t *httphandle;
224
  isc_nm_cb_t cb;
225
  void *cbarg;
226
  isc_buffer_t *pending_write_data;
227
  isc__nm_http_pending_callbacks_t pending_write_callbacks;
228
  uint64_t submitted;
229
} isc_http_send_req_t;
230
231
0
#define HTTP_ENDPOINTS_MAGIC  ISC_MAGIC('H', 'T', 'E', 'P')
232
#define VALID_HTTP_ENDPOINTS(t) ISC_MAGIC_VALID(t, HTTP_ENDPOINTS_MAGIC)
233
234
0
#define HTTP_HANDLER_MAGIC    ISC_MAGIC('H', 'T', 'H', 'L')
235
#define VALID_HTTP_HANDLER(t) ISC_MAGIC_VALID(t, HTTP_HANDLER_MAGIC)
236
237
static void
238
http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
239
       isc_nm_cb_t cb, void *cbarg);
240
241
static void
242
http_log_flooding_peer(isc_nm_http_session_t *session);
243
244
static bool
245
http_is_flooding_peer(isc_nm_http_session_t *session);
246
247
static ssize_t
248
http_process_input_data(isc_nm_http_session_t *session,
249
      isc_buffer_t *input_data);
250
251
static inline bool
252
http_too_many_active_streams(isc_nm_http_session_t *session);
253
254
static void
255
http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
256
      isc_nm_cb_t send_cb, void *send_cbarg);
257
258
static void
259
http_do_bio_async(isc_nm_http_session_t *session);
260
261
static void
262
failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
263
        isc_nm_http_session_t *session);
264
265
static void
266
client_call_failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
267
268
static void
269
server_call_failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
270
271
static void
272
failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
273
274
static isc_result_t
275
server_send_error_response(const isc_http_error_responses_t error,
276
         nghttp2_session *ngsession, isc_nmsocket_t *socket);
277
278
static isc_result_t
279
client_send(isc_nmhandle_t *handle, const isc_region_t *region);
280
281
static void
282
finish_http_session(isc_nm_http_session_t *session);
283
284
static void
285
http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle);
286
287
static void
288
call_pending_callbacks(isc__nm_http_pending_callbacks_t pending_callbacks,
289
           isc_result_t result);
290
291
static void
292
server_call_cb(isc_nmsocket_t *socket, const isc_result_t result,
293
         isc_region_t *data);
294
295
static isc_nm_httphandler_t *
296
http_endpoints_find(const char *request_path,
297
        isc_nm_http_endpoints_t *restrict eps);
298
299
static void
300
http_init_listener_endpoints(isc_nmsocket_t *listener,
301
           isc_nm_http_endpoints_t *epset);
302
303
static void
304
http_cleanup_listener_endpoints(isc_nmsocket_t *listener);
305
306
static isc_nm_http_endpoints_t *
307
http_get_listener_endpoints(isc_nmsocket_t *listener, const isc_tid_t tid);
308
309
static void
310
http_initsocket(isc_nmsocket_t *sock);
311
312
static void *
313
0
http_malloc(size_t sz, isc_mem_t *mctx) {
314
0
  return isc_mem_allocate(mctx, sz);
315
0
}
316
317
static void *
318
0
http_calloc(size_t n, size_t sz, isc_mem_t *mctx) {
319
0
  return isc_mem_callocate(mctx, n, sz);
320
0
}
321
322
static void *
323
0
http_realloc(void *p, size_t newsz, isc_mem_t *mctx) {
324
0
  return isc_mem_reallocate(mctx, p, newsz);
325
0
}
326
327
static void
328
0
http_free(void *p, isc_mem_t *mctx) {
329
0
  if (p == NULL) { /* as standard free() behaves */
330
0
    return;
331
0
  }
332
0
  isc_mem_free(mctx, p);
333
0
}
334
335
static void
336
0
init_nghttp2_mem(isc_mem_t *mctx, nghttp2_mem *mem) {
337
0
  *mem = (nghttp2_mem){ .malloc = (nghttp2_malloc)http_malloc,
338
0
            .calloc = (nghttp2_calloc)http_calloc,
339
0
            .realloc = (nghttp2_realloc)http_realloc,
340
0
            .free = (nghttp2_free)http_free,
341
0
            .mem_user_data = mctx };
342
0
}
343
344
static void
345
new_session(isc_mem_t *mctx, isc_tlsctx_t *tctx,
346
0
      isc_nm_http_session_t **sessionp) {
347
0
  isc_nm_http_session_t *session = NULL;
348
349
0
  REQUIRE(sessionp != NULL && *sessionp == NULL);
350
0
  REQUIRE(mctx != NULL);
351
352
0
  session = isc_mem_get(mctx, sizeof(isc_nm_http_session_t));
353
0
  *session = (isc_nm_http_session_t){ .magic = HTTP2_SESSION_MAGIC,
354
0
              .tlsctx = tctx };
355
0
  isc_refcount_init(&session->references, 1);
356
0
  isc_mem_attach(mctx, &session->mctx);
357
0
  ISC_LIST_INIT(session->cstreams);
358
0
  ISC_LIST_INIT(session->sstreams);
359
0
  ISC_LIST_INIT(session->pending_write_callbacks);
360
361
0
  *sessionp = session;
362
0
}
363
364
void
365
isc__nm_httpsession_attach(isc_nm_http_session_t *source,
366
0
         isc_nm_http_session_t **targetp) {
367
0
  REQUIRE(VALID_HTTP2_SESSION(source));
368
0
  REQUIRE(targetp != NULL && *targetp == NULL);
369
370
0
  isc_refcount_increment(&source->references);
371
372
0
  *targetp = source;
373
0
}
374
375
void
376
0
isc__nm_httpsession_detach(isc_nm_http_session_t **sessionp) {
377
0
  isc_nm_http_session_t *session = NULL;
378
379
0
  REQUIRE(sessionp != NULL);
380
381
0
  session = *sessionp;
382
0
  *sessionp = NULL;
383
384
0
  REQUIRE(VALID_HTTP2_SESSION(session));
385
386
0
  if (isc_refcount_decrement(&session->references) > 1) {
387
0
    return;
388
0
  }
389
390
0
  finish_http_session(session);
391
392
0
  INSIST(ISC_LIST_EMPTY(session->sstreams));
393
0
  INSIST(ISC_LIST_EMPTY(session->cstreams));
394
395
0
  if (session->ngsession != NULL) {
396
0
    nghttp2_session_del(session->ngsession);
397
0
    session->ngsession = NULL;
398
0
  }
399
400
0
  if (session->buf != NULL) {
401
0
    isc_buffer_free(&session->buf);
402
0
  }
403
404
  /* We need an acquire memory barrier here */
405
0
  (void)isc_refcount_current(&session->references);
406
407
0
  session->magic = 0;
408
0
  isc_mem_putanddetach(&session->mctx, session,
409
0
           sizeof(isc_nm_http_session_t));
410
0
}
411
412
isc_nmhandle_t *
413
0
isc__nm_httpsession_handle(isc_nm_http_session_t *session) {
414
0
  REQUIRE(VALID_HTTP2_SESSION(session));
415
416
0
  return session->handle;
417
0
}
418
419
bool
420
0
isc__nm_httpsession_active(isc_nm_http_session_t *session) {
421
0
  REQUIRE(VALID_HTTP2_SESSION(session));
422
423
0
  return !session->closed && !session->closing && session->handle != NULL;
424
0
}
425
426
static http_cstream_t *
427
0
find_http_cstream(int32_t stream_id, isc_nm_http_session_t *session) {
428
0
  REQUIRE(VALID_HTTP2_SESSION(session));
429
430
0
  if (ISC_LIST_EMPTY(session->cstreams)) {
431
0
    return NULL;
432
0
  }
433
434
0
  ISC_LIST_FOREACH(session->cstreams, cstream, link) {
435
0
    if (cstream->stream_id == stream_id) {
436
      /* LRU-like behaviour */
437
0
      if (ISC_LIST_HEAD(session->cstreams) != cstream) {
438
0
        ISC_LIST_UNLINK(session->cstreams, cstream,
439
0
            link);
440
0
        ISC_LIST_PREPEND(session->cstreams, cstream,
441
0
             link);
442
0
      }
443
444
0
      return cstream;
445
0
    }
446
0
  }
447
448
0
  return NULL;
449
0
}
450
451
static isc_result_t
452
0
new_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
453
0
  isc_mem_t *mctx = sock->worker->mctx;
454
0
  const char *uri = NULL;
455
0
  bool post;
456
0
  http_cstream_t *stream = NULL;
457
0
  isc_result_t result;
458
459
0
  uri = sock->h2->session->handle->sock->h2->connect.uri;
460
0
  post = sock->h2->session->handle->sock->h2->connect.post;
461
462
0
  stream = isc_mem_get(mctx, sizeof(http_cstream_t));
463
0
  *stream = (http_cstream_t){ .stream_id = -1,
464
0
            .post = post,
465
0
            .uri = isc_mem_strdup(mctx, uri) };
466
0
  ISC_LINK_INIT(stream, link);
467
468
0
  result = isc_url_parse(stream->uri, strlen(stream->uri), 0,
469
0
             &stream->up);
470
0
  if (result != ISC_R_SUCCESS) {
471
0
    isc_mem_free(mctx, stream->uri);
472
0
    isc_mem_put(mctx, stream, sizeof(http_cstream_t));
473
0
    return result;
474
0
  }
475
476
0
  isc__nmsocket_attach(sock, &stream->httpsock);
477
0
  stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len;
478
0
  stream->authority = isc_mem_get(mctx, stream->authoritylen + AUTHEXTRA);
479
0
  memmove(stream->authority, &uri[stream->up.field_data[ISC_UF_HOST].off],
480
0
    stream->up.field_data[ISC_UF_HOST].len);
481
482
0
  if (stream->up.field_set & (1 << ISC_UF_PORT)) {
483
0
    stream->authoritylen += (size_t)snprintf(
484
0
      stream->authority +
485
0
        stream->up.field_data[ISC_UF_HOST].len,
486
0
      AUTHEXTRA, ":%u", stream->up.port);
487
0
  }
488
489
  /* If we don't have path in URI, we use "/" as path. */
490
0
  stream->pathlen = 1;
491
0
  if (stream->up.field_set & (1 << ISC_UF_PATH)) {
492
0
    stream->pathlen = stream->up.field_data[ISC_UF_PATH].len;
493
0
  }
494
0
  if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
495
    /* +1 for '?' character */
496
0
    stream->pathlen +=
497
0
      (size_t)(stream->up.field_data[ISC_UF_QUERY].len + 1);
498
0
  }
499
500
0
  stream->path = isc_mem_get(mctx, stream->pathlen);
501
0
  if (stream->up.field_set & (1 << ISC_UF_PATH)) {
502
0
    memmove(stream->path,
503
0
      &uri[stream->up.field_data[ISC_UF_PATH].off],
504
0
      stream->up.field_data[ISC_UF_PATH].len);
505
0
  } else {
506
0
    stream->path[0] = '/';
507
0
  }
508
509
0
  if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
510
0
    stream->path[stream->pathlen -
511
0
           stream->up.field_data[ISC_UF_QUERY].len - 1] = '?';
512
0
    memmove(stream->path + stream->pathlen -
513
0
        stream->up.field_data[ISC_UF_QUERY].len,
514
0
      &uri[stream->up.field_data[ISC_UF_QUERY].off],
515
0
      stream->up.field_data[ISC_UF_QUERY].len);
516
0
  }
517
518
0
  isc_buffer_allocate(mctx, &stream->rbuf,
519
0
          INITIAL_DNS_MESSAGE_BUFFER_SIZE);
520
521
0
  ISC_LIST_PREPEND(sock->h2->session->cstreams, stream, link);
522
0
  *streamp = stream;
523
524
0
  return ISC_R_SUCCESS;
525
0
}
526
527
static void
528
0
put_http_cstream(isc_mem_t *mctx, http_cstream_t *stream) {
529
0
  isc_mem_put(mctx, stream->path, stream->pathlen);
530
0
  isc_mem_put(mctx, stream->authority,
531
0
        stream->up.field_data[ISC_UF_HOST].len + AUTHEXTRA);
532
0
  isc_mem_free(mctx, stream->uri);
533
0
  if (stream->GET_path != NULL) {
534
0
    isc_mem_free(mctx, stream->GET_path);
535
0
    stream->GET_path_len = 0;
536
0
  }
537
538
0
  if (stream->postdata != NULL) {
539
0
    INSIST(stream->post);
540
0
    isc_buffer_free(&stream->postdata);
541
0
  }
542
543
0
  if (stream == stream->httpsock->h2->connect.cstream) {
544
0
    stream->httpsock->h2->connect.cstream = NULL;
545
0
  }
546
0
  if (ISC_LINK_LINKED(stream, link)) {
547
0
    ISC_LIST_UNLINK(stream->httpsock->h2->session->cstreams, stream,
548
0
        link);
549
0
  }
550
0
  isc__nmsocket_detach(&stream->httpsock);
551
552
0
  isc_buffer_free(&stream->rbuf);
553
0
  isc_mem_put(mctx, stream, sizeof(http_cstream_t));
554
0
}
555
556
static void
557
0
finish_http_session(isc_nm_http_session_t *session) {
558
0
  if (session->closed) {
559
0
    return;
560
0
  }
561
562
0
  if (session->handle != NULL) {
563
0
    if (!session->closed) {
564
0
      session->closed = true;
565
0
      session->reading = false;
566
0
      isc_nm_read_stop(session->handle);
567
0
      isc__nmsocket_timer_stop(session->handle->sock);
568
0
      isc_nmhandle_close(session->handle);
569
0
    }
570
571
    /*
572
     * Free any unprocessed incoming data in order to not process
573
     * it during indirect calls to http_do_bio() that might happen
574
     * when calling the failed callbacks.
575
     */
576
0
    if (session->buf != NULL) {
577
0
      isc_buffer_free(&session->buf);
578
0
    }
579
580
0
    if (session->client) {
581
0
      client_call_failed_read_cb(ISC_R_UNEXPECTED, session);
582
0
    } else {
583
0
      server_call_failed_read_cb(ISC_R_UNEXPECTED, session);
584
0
    }
585
586
0
    call_pending_callbacks(session->pending_write_callbacks,
587
0
               ISC_R_UNEXPECTED);
588
0
    ISC_LIST_INIT(session->pending_write_callbacks);
589
590
0
    if (session->pending_write_data != NULL) {
591
0
      isc_buffer_free(&session->pending_write_data);
592
0
    }
593
594
0
    isc_nmhandle_detach(&session->handle);
595
0
  }
596
597
0
  if (session->client_httphandle != NULL) {
598
0
    isc_nmhandle_detach(&session->client_httphandle);
599
0
  }
600
601
0
  INSIST(ISC_LIST_EMPTY(session->cstreams));
602
603
  /* detach from server socket */
604
0
  if (session->serversocket != NULL) {
605
0
    isc__nmsocket_detach(&session->serversocket);
606
0
  }
607
0
  session->closed = true;
608
0
}
609
610
static int
611
on_client_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
612
0
           size_t len, isc_nm_http_session_t *session) {
613
0
  http_cstream_t *cstream = find_http_cstream(stream_id, session);
614
615
0
  if (cstream != NULL) {
616
0
    size_t new_rbufsize = len;
617
0
    INSIST(cstream->rbuf != NULL);
618
0
    new_rbufsize += isc_buffer_usedlength(cstream->rbuf);
619
0
    if (new_rbufsize <= MAX_DNS_MESSAGE_SIZE &&
620
0
        new_rbufsize <= cstream->response_status.content_length)
621
0
    {
622
0
      isc_buffer_putmem(cstream->rbuf, data, len);
623
0
    } else {
624
0
      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
625
0
    }
626
0
  } else {
627
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
628
0
  }
629
630
0
  return 0;
631
0
}
632
633
static int
634
on_server_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
635
0
           size_t len, isc_nm_http_session_t *session) {
636
0
  isc_nmsocket_h2_t *h2 = ISC_LIST_HEAD(session->sstreams);
637
0
  isc_mem_t *mctx = h2->psock->worker->mctx;
638
639
0
  while (h2 != NULL) {
640
0
    if (stream_id == h2->stream_id) {
641
0
      if (isc_buffer_base(&h2->rbuf) == NULL) {
642
0
        isc_buffer_init(
643
0
          &h2->rbuf,
644
0
          isc_mem_allocate(mctx,
645
0
               h2->content_length),
646
0
          h2->content_length);
647
0
      }
648
0
      size_t new_bufsize = isc_buffer_usedlength(&h2->rbuf) +
649
0
               len;
650
0
      if (new_bufsize <= h2->content_length) {
651
0
        session->processed_useful_data += len;
652
0
        isc_buffer_putmem(&h2->rbuf, data, len);
653
0
        break;
654
0
      }
655
656
0
      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
657
0
    }
658
0
    h2 = ISC_LIST_NEXT(h2, link);
659
0
  }
660
0
  if (h2 == NULL) {
661
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
662
0
  }
663
664
0
  return 0;
665
0
}
666
667
static int
668
on_data_chunk_recv_callback(nghttp2_session *ngsession, uint8_t flags,
669
          int32_t stream_id, const uint8_t *data, size_t len,
670
0
          void *user_data) {
671
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
672
0
  int rv;
673
674
0
  UNUSED(ngsession);
675
0
  UNUSED(flags);
676
677
0
  if (session->client) {
678
0
    rv = on_client_data_chunk_recv_callback(stream_id, data, len,
679
0
              session);
680
0
  } else {
681
0
    rv = on_server_data_chunk_recv_callback(stream_id, data, len,
682
0
              session);
683
0
  }
684
685
0
  return rv;
686
0
}
687
688
static void
689
call_unlink_cstream_readcb(http_cstream_t *cstream,
690
         isc_nm_http_session_t *session,
691
0
         isc_result_t result) {
692
0
  isc_region_t read_data;
693
0
  REQUIRE(VALID_HTTP2_SESSION(session));
694
0
  REQUIRE(cstream != NULL);
695
0
  ISC_LIST_UNLINK(session->cstreams, cstream, link);
696
0
  INSIST(VALID_NMHANDLE(session->client_httphandle));
697
0
  isc_buffer_usedregion(cstream->rbuf, &read_data);
698
0
  cstream->read_cb(session->client_httphandle, result, &read_data,
699
0
       cstream->read_cbarg);
700
0
  if (result == ISC_R_SUCCESS) {
701
0
    isc__nmsocket_timer_restart(session->handle->sock);
702
0
  }
703
0
  put_http_cstream(session->mctx, cstream);
704
0
}
705
706
static int
707
on_client_stream_close_callback(int32_t stream_id,
708
0
        isc_nm_http_session_t *session) {
709
0
  http_cstream_t *cstream = find_http_cstream(stream_id, session);
710
711
0
  if (cstream != NULL) {
712
0
    isc_result_t result =
713
0
      SUCCESSFUL_HTTP_STATUS(cstream->response_status.code)
714
0
        ? ISC_R_SUCCESS
715
0
        : ISC_R_FAILURE;
716
0
    call_unlink_cstream_readcb(cstream, session, result);
717
0
    if (ISC_LIST_EMPTY(session->cstreams)) {
718
0
      int rv = 0;
719
0
      rv = nghttp2_session_terminate_session(
720
0
        session->ngsession, NGHTTP2_NO_ERROR);
721
0
      if (rv != 0) {
722
0
        return rv;
723
0
      }
724
      /* Mark the session as closing one to finish it on a
725
       * subsequent call to http_do_bio() */
726
0
      session->closing = true;
727
0
    }
728
0
  } else {
729
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
730
0
  }
731
732
0
  return 0;
733
0
}
734
735
static int
736
on_server_stream_close_callback(int32_t stream_id,
737
0
        isc_nm_http_session_t *session) {
738
0
  isc_nmsocket_t *sock = nghttp2_session_get_stream_user_data(
739
0
    session->ngsession, stream_id);
740
0
  int rv = 0;
741
742
0
  ISC_LIST_UNLINK(session->sstreams, sock->h2, link);
743
0
  session->nsstreams--;
744
0
  if (sock->h2->request_received) {
745
0
    session->submitted++;
746
0
  }
747
748
  /*
749
   * By making a call to isc__nmsocket_prep_destroy(), we ensure that
750
   * the socket gets marked as inactive, allowing the HTTP/2 data
751
   * associated with it to be properly disposed of eventually.
752
   *
753
   * An HTTP/2 stream socket will normally be marked as inactive in
754
   * the normal course of operation. However, when browsers terminate
755
   * HTTP/2 streams prematurely (e.g. by sending RST_STREAM),
756
   * corresponding sockets can remain marked as active, retaining
757
   * references to the HTTP/2 data (most notably the session objects),
758
   * preventing them from being correctly freed and leading to BIND
759
   * hanging on shutdown.  Calling isc__nmsocket_prep_destroy()
760
   * ensures that this will not happen.
761
   */
762
0
  isc__nmsocket_prep_destroy(sock);
763
0
  isc__nmsocket_detach(&sock);
764
0
  return rv;
765
0
}
766
767
static int
768
on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
769
0
       uint32_t error_code, void *user_data) {
770
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
771
0
  int rv = 0;
772
773
0
  REQUIRE(VALID_HTTP2_SESSION(session));
774
0
  REQUIRE(session->ngsession == ngsession);
775
776
0
  UNUSED(error_code);
777
778
0
  if (session->client) {
779
0
    rv = on_client_stream_close_callback(stream_id, session);
780
0
  } else {
781
0
    rv = on_server_stream_close_callback(stream_id, session);
782
0
  }
783
784
0
  return rv;
785
0
}
786
787
static bool
788
client_handle_status_header(http_cstream_t *cstream, const uint8_t *value,
789
0
          const size_t valuelen) {
790
0
  char tmp[32] = { 0 };
791
0
  const size_t tmplen = sizeof(tmp) - 1;
792
793
0
  strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
794
0
  cstream->response_status.code = strtoul(tmp, NULL, 10);
795
796
0
  if (SUCCESSFUL_HTTP_STATUS(cstream->response_status.code)) {
797
0
    return true;
798
0
  }
799
800
0
  return false;
801
0
}
802
803
static bool
804
client_handle_content_length_header(http_cstream_t *cstream,
805
            const uint8_t *value,
806
0
            const size_t valuelen) {
807
0
  char tmp[32] = { 0 };
808
0
  const size_t tmplen = sizeof(tmp) - 1;
809
810
0
  strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
811
0
  cstream->response_status.content_length = strtoul(tmp, NULL, 10);
812
813
0
  if (cstream->response_status.content_length == 0 ||
814
0
      cstream->response_status.content_length > MAX_DNS_MESSAGE_SIZE)
815
0
  {
816
0
    return false;
817
0
  }
818
819
0
  return true;
820
0
}
821
822
static bool
823
client_handle_content_type_header(http_cstream_t *cstream, const uint8_t *value,
824
0
          const size_t valuelen) {
825
0
  const char type_dns_message[] = DNS_MEDIA_TYPE;
826
827
0
  if (HEADER_MATCH(type_dns_message, value, valuelen)) {
828
0
    cstream->response_status.content_type_valid = true;
829
0
    return true;
830
0
  }
831
832
0
  return false;
833
0
}
834
835
static int
836
client_on_header_callback(nghttp2_session *ngsession,
837
        const nghttp2_frame *frame, const uint8_t *name,
838
        size_t namelen, const uint8_t *value, size_t valuelen,
839
0
        uint8_t flags, void *user_data) {
840
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
841
0
  http_cstream_t *cstream = NULL;
842
0
  const char status[] = ":status";
843
0
  const char content_length[] = "Content-Length";
844
0
  const char content_type[] = "Content-Type";
845
0
  bool header_ok = true;
846
847
0
  REQUIRE(VALID_HTTP2_SESSION(session));
848
0
  REQUIRE(session->client);
849
850
0
  UNUSED(flags);
851
0
  UNUSED(ngsession);
852
853
0
  cstream = find_http_cstream(frame->hd.stream_id, session);
854
0
  if (cstream == NULL) {
855
    /*
856
     * This could happen in two cases:
857
     * - the server sent us bad data, or
858
     * - we closed the session prematurely before receiving all
859
     *   responses (i.e., because of a belated or partial response).
860
     */
861
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
862
0
  }
863
864
0
  INSIST(!ISC_LIST_EMPTY(session->cstreams));
865
866
0
  switch (frame->hd.type) {
867
0
  case NGHTTP2_HEADERS:
868
0
    if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
869
0
      break;
870
0
    }
871
872
0
    if (HEADER_MATCH(status, name, namelen)) {
873
0
      header_ok = client_handle_status_header(cstream, value,
874
0
                valuelen);
875
0
    } else if (HEADER_MATCH(content_length, name, namelen)) {
876
0
      header_ok = client_handle_content_length_header(
877
0
        cstream, value, valuelen);
878
0
    } else if (HEADER_MATCH(content_type, name, namelen)) {
879
0
      header_ok = client_handle_content_type_header(
880
0
        cstream, value, valuelen);
881
0
    }
882
0
    break;
883
0
  }
884
885
0
  if (!header_ok) {
886
0
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
887
0
  }
888
889
0
  return 0;
890
0
}
891
892
static void
893
0
initialize_nghttp2_client_session(isc_nm_http_session_t *session) {
894
0
  nghttp2_session_callbacks *callbacks = NULL;
895
0
  nghttp2_option *option = NULL;
896
0
  nghttp2_mem mem;
897
898
0
  init_nghttp2_mem(session->mctx, &mem);
899
0
  RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
900
0
  RUNTIME_CHECK(nghttp2_option_new(&option) == 0);
901
902
0
#if NGHTTP2_VERSION_NUM >= (0x010c00)
903
0
  nghttp2_option_set_max_send_header_block_length(
904
0
    option, MAX_ALLOWED_DATA_IN_HEADERS);
905
0
#endif
906
907
0
  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
908
0
    callbacks, on_data_chunk_recv_callback);
909
910
0
  nghttp2_session_callbacks_set_on_stream_close_callback(
911
0
    callbacks, on_stream_close_callback);
912
913
0
  nghttp2_session_callbacks_set_on_header_callback(
914
0
    callbacks, client_on_header_callback);
915
916
0
  RUNTIME_CHECK(nghttp2_session_client_new3(&session->ngsession,
917
0
              callbacks, session, option,
918
0
              &mem) == 0);
919
920
0
  nghttp2_option_del(option);
921
0
  nghttp2_session_callbacks_del(callbacks);
922
0
}
923
924
static bool
925
0
send_client_connection_header(isc_nm_http_session_t *session) {
926
0
  nghttp2_settings_entry iv[] = { { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 } };
927
0
  int rv;
928
929
0
  rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
930
0
             sizeof(iv) / sizeof(iv[0]));
931
0
  if (rv != 0) {
932
0
    return false;
933
0
  }
934
935
0
  return true;
936
0
}
937
938
#define MAKE_NV(NAME, VALUE, VALUELEN)                                 \
939
0
  { (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
940
0
    sizeof(NAME) - 1, VALUELEN, NGHTTP2_NV_FLAG_NONE }
941
942
#define MAKE_NV2(NAME, VALUE)                                          \
943
0
  { (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
944
0
    sizeof(NAME) - 1, sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE }
945
946
static ssize_t
947
client_read_callback(nghttp2_session *ngsession, int32_t stream_id,
948
         uint8_t *buf, size_t length, uint32_t *data_flags,
949
0
         nghttp2_data_source *source, void *user_data) {
950
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
951
0
  http_cstream_t *cstream = NULL;
952
953
0
  REQUIRE(session->client);
954
0
  REQUIRE(!ISC_LIST_EMPTY(session->cstreams));
955
956
0
  UNUSED(ngsession);
957
0
  UNUSED(source);
958
959
0
  cstream = find_http_cstream(stream_id, session);
960
0
  if (!cstream || cstream->stream_id != stream_id) {
961
    /* We haven't found the stream, so we are not reading */
962
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
963
0
  }
964
965
0
  if (cstream->post) {
966
0
    size_t len = isc_buffer_remaininglength(cstream->postdata);
967
968
0
    if (len > length) {
969
0
      len = length;
970
0
    }
971
972
0
    if (len > 0) {
973
0
      memmove(buf, isc_buffer_current(cstream->postdata),
974
0
        len);
975
0
      isc_buffer_forward(cstream->postdata, len);
976
0
    }
977
978
0
    if (isc_buffer_remaininglength(cstream->postdata) == 0) {
979
0
      *data_flags |= NGHTTP2_DATA_FLAG_EOF;
980
0
    }
981
982
0
    return len;
983
0
  } else {
984
0
    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
985
0
    return 0;
986
0
  }
987
988
0
  return 0;
989
0
}
990
991
/*
992
 * Send HTTP request to the remote peer.
993
 */
994
static isc_result_t
995
0
client_submit_request(isc_nm_http_session_t *session, http_cstream_t *stream) {
996
0
  int32_t stream_id;
997
0
  char *uri = stream->uri;
998
0
  isc_url_parser_t *up = &stream->up;
999
0
  nghttp2_data_provider dp;
1000
1001
0
  if (stream->post) {
1002
0
    char p[64];
1003
0
    snprintf(p, sizeof(p), "%u",
1004
0
       isc_buffer_usedlength(stream->postdata));
1005
0
    nghttp2_nv hdrs[] = {
1006
0
      MAKE_NV2(":method", "POST"),
1007
0
      MAKE_NV(":scheme",
1008
0
        &uri[up->field_data[ISC_UF_SCHEMA].off],
1009
0
        up->field_data[ISC_UF_SCHEMA].len),
1010
0
      MAKE_NV(":authority", stream->authority,
1011
0
        stream->authoritylen),
1012
0
      MAKE_NV(":path", stream->path, stream->pathlen),
1013
0
      MAKE_NV2("content-type", DNS_MEDIA_TYPE),
1014
0
      MAKE_NV2("accept", DNS_MEDIA_TYPE),
1015
0
      MAKE_NV("content-length", p, strlen(p)),
1016
0
      MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
1017
0
    };
1018
1019
0
    dp = (nghttp2_data_provider){ .read_callback =
1020
0
                  client_read_callback };
1021
0
    stream_id = nghttp2_submit_request(
1022
0
      session->ngsession, NULL, hdrs,
1023
0
      sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
1024
0
  } else {
1025
0
    INSIST(stream->GET_path != NULL);
1026
0
    INSIST(stream->GET_path_len != 0);
1027
0
    nghttp2_nv hdrs[] = {
1028
0
      MAKE_NV2(":method", "GET"),
1029
0
      MAKE_NV(":scheme",
1030
0
        &uri[up->field_data[ISC_UF_SCHEMA].off],
1031
0
        up->field_data[ISC_UF_SCHEMA].len),
1032
0
      MAKE_NV(":authority", stream->authority,
1033
0
        stream->authoritylen),
1034
0
      MAKE_NV(":path", stream->GET_path,
1035
0
        stream->GET_path_len),
1036
0
      MAKE_NV2("accept", DNS_MEDIA_TYPE),
1037
0
      MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
1038
0
    };
1039
1040
0
    dp = (nghttp2_data_provider){ .read_callback =
1041
0
                  client_read_callback };
1042
0
    stream_id = nghttp2_submit_request(
1043
0
      session->ngsession, NULL, hdrs,
1044
0
      sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
1045
0
  }
1046
0
  if (stream_id < 0) {
1047
0
    return ISC_R_FAILURE;
1048
0
  }
1049
1050
0
  stream->stream_id = stream_id;
1051
1052
0
  return ISC_R_SUCCESS;
1053
0
}
1054
1055
static inline size_t
1056
0
http_in_flight_data_size(isc_nm_http_session_t *session) {
1057
0
  size_t in_flight = 0;
1058
1059
0
  if (session->pending_write_data != NULL) {
1060
0
    in_flight += isc_buffer_usedlength(session->pending_write_data);
1061
0
  }
1062
1063
0
  in_flight += session->data_in_flight;
1064
1065
0
  return in_flight;
1066
0
}
1067
1068
static ssize_t
1069
http_process_input_data(isc_nm_http_session_t *session,
1070
0
      isc_buffer_t *input_data) {
1071
0
  ssize_t readlen = 0;
1072
0
  ssize_t processed = 0;
1073
0
  isc_region_t chunk = { 0 };
1074
0
  size_t before, after;
1075
0
  size_t i;
1076
1077
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1078
0
  REQUIRE(input_data != NULL);
1079
1080
0
  if (!isc__nm_httpsession_active(session)) {
1081
0
    return 0;
1082
0
  }
1083
1084
  /*
1085
   * For clients that initiate request themselves just process
1086
   * everything.
1087
   */
1088
0
  if (session->client) {
1089
0
    isc_buffer_remainingregion(input_data, &chunk);
1090
0
    if (chunk.length == 0) {
1091
0
      return 0;
1092
0
    }
1093
1094
0
    readlen = nghttp2_session_mem_recv(session->ngsession,
1095
0
               chunk.base, chunk.length);
1096
1097
0
    if (readlen >= 0) {
1098
0
      isc_buffer_forward(input_data, readlen);
1099
0
      session->processed_incoming_data += readlen;
1100
0
    }
1101
1102
0
    return readlen;
1103
0
  }
1104
1105
  /*
1106
   * If no streams are created during processing, we might process
1107
   * more than one chunk at a time. Still we should not overdo that
1108
   * to avoid processing too much data at once as such behaviour is
1109
   * known for trashing the memory allocator at times.
1110
   */
1111
0
  for (before = after = session->nsstreams, i = 0;
1112
0
       after <= before && i < INCOMING_DATA_MAX_CHUNKS_AT_ONCE;
1113
0
       after = session->nsstreams, i++)
1114
0
  {
1115
0
    const uint64_t active_streams =
1116
0
      (session->received - session->processed);
1117
1118
    /*
1119
     * If there is too much outgoing data in flight - let's not
1120
     * process any incoming data, as it could lead to piling up
1121
     * too much send data in send buffers. With many clients
1122
     * connected it can lead to excessive memory consumption on
1123
     * the server instance.
1124
     */
1125
0
    const size_t in_flight = http_in_flight_data_size(session);
1126
0
    if (in_flight >= ISC_NETMGR_TCP_SENDBUF_SIZE) {
1127
0
      break;
1128
0
    }
1129
1130
    /*
1131
     * If we have reached the maximum number of streams used, we
1132
     * might stop processing for now, as nghttp2 will happily
1133
     * consume as much data as possible.
1134
     */
1135
0
    if (session->nsstreams >= session->max_concurrent_streams &&
1136
0
        active_streams > 0)
1137
0
    {
1138
0
      break;
1139
0
    }
1140
1141
0
    if (http_too_many_active_streams(session)) {
1142
0
      break;
1143
0
    }
1144
1145
0
    isc_buffer_remainingregion(input_data, &chunk);
1146
0
    if (chunk.length == 0) {
1147
0
      break;
1148
0
    }
1149
1150
0
    chunk.length = ISC_MIN(chunk.length, INCOMING_DATA_CHUNK_SIZE);
1151
1152
0
    readlen = nghttp2_session_mem_recv(session->ngsession,
1153
0
               chunk.base, chunk.length);
1154
1155
0
    if (readlen >= 0) {
1156
0
      isc_buffer_forward(input_data, readlen);
1157
0
      session->processed_incoming_data += readlen;
1158
0
      processed += readlen;
1159
0
    } else {
1160
0
      isc_buffer_clear(input_data);
1161
0
      return readlen;
1162
0
    }
1163
0
  }
1164
1165
0
  return processed;
1166
0
}
1167
1168
static void
1169
0
http_log_flooding_peer(isc_nm_http_session_t *session) {
1170
0
  const int log_level = ISC_LOG_DEBUG(1);
1171
0
  if (session->handle != NULL && isc_log_wouldlog(log_level)) {
1172
0
    char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
1173
0
    char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
1174
1175
0
    isc_sockaddr_format(&session->handle->sock->peer, client_sabuf,
1176
0
            sizeof(client_sabuf));
1177
0
    isc_sockaddr_format(&session->handle->sock->iface, local_sabuf,
1178
0
            sizeof(local_sabuf));
1179
0
    isc__nmsocket_log(session->handle->sock, log_level,
1180
0
          "Dropping a flooding HTTP/2 peer "
1181
0
          "%s (on %s) - processed: %" PRIu64
1182
0
          " bytes, of them useful: %" PRIu64 "",
1183
0
          client_sabuf, local_sabuf,
1184
0
          session->processed_incoming_data,
1185
0
          session->processed_useful_data);
1186
0
  }
1187
0
}
1188
1189
static bool
1190
0
http_is_flooding_peer(isc_nm_http_session_t *session) {
1191
0
  if (session->client) {
1192
0
    return false;
1193
0
  }
1194
1195
  /*
1196
   * A flooding client can try to open a lot of streams before
1197
   * submitting a request. Let's drop such clients.
1198
   */
1199
0
  if (session->received == 0 &&
1200
0
      session->total_opened_sstreams > MAX_STREAMS_BEFORE_FIRST_REQUEST)
1201
0
  {
1202
0
    return true;
1203
0
  }
1204
1205
  /*
1206
   * We have processed enough data to open at least one stream and
1207
   * get some useful data.
1208
   */
1209
0
  if (session->processed_incoming_data >
1210
0
        INCOMING_DATA_INITIAL_STREAM_SIZE &&
1211
0
      (session->total_opened_sstreams == 0 ||
1212
0
       session->processed_useful_data == 0))
1213
0
  {
1214
0
    return true;
1215
0
  }
1216
1217
0
  if (session->processed_incoming_data < INCOMING_DATA_GRACE_SIZE) {
1218
0
    return false;
1219
0
  }
1220
1221
  /*
1222
   * The overhead of DoH per DNS message can be minimum 160-180
1223
   * bytes. We should allow more for extra information that can be
1224
   * included in headers, so let's use 256 bytes. Minimum DNS
1225
   * message size is 12 bytes. So, (256+12)/12=22. Even that can be
1226
   * too restricting for some edge cases, but should be good enough
1227
   * for any practical purposes. Not to mention that HTTP/2 may
1228
   * include legitimate data that is completely useless for DNS
1229
   * purposes...
1230
   *
1231
   * Anyway, at that point we should have processed enough requests
1232
   * for such clients (if any).
1233
   */
1234
0
  if (session->processed_useful_data == 0 ||
1235
0
      (session->processed_incoming_data /
1236
0
       session->processed_useful_data) > 22)
1237
0
  {
1238
0
    return true;
1239
0
  }
1240
1241
0
  return false;
1242
0
}
1243
1244
/*
1245
 * Read callback from TLS socket.
1246
 */
1247
static void
1248
http_readcb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
1249
0
      isc_region_t *region, void *data) {
1250
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)data;
1251
0
  isc_nm_http_session_t *tmpsess = NULL;
1252
0
  ssize_t readlen;
1253
0
  isc_buffer_t input;
1254
1255
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1256
1257
  /*
1258
   * Let's ensure that HTTP/2 session and its associated data will
1259
   * not go "out of scope" too early.
1260
   */
1261
0
  isc__nm_httpsession_attach(session, &tmpsess);
1262
1263
0
  if (result != ISC_R_SUCCESS) {
1264
0
    if (result != ISC_R_TIMEDOUT) {
1265
0
      session->reading = false;
1266
0
    }
1267
0
    failed_read_cb(result, session);
1268
0
    goto done;
1269
0
  }
1270
1271
0
  isc_buffer_init(&input, region->base, region->length);
1272
0
  isc_buffer_add(&input, region->length);
1273
1274
0
  readlen = http_process_input_data(session, &input);
1275
0
  if (readlen < 0) {
1276
0
    failed_read_cb(ISC_R_UNEXPECTED, session);
1277
0
    goto done;
1278
0
  } else if (http_is_flooding_peer(session)) {
1279
0
    http_log_flooding_peer(session);
1280
0
    failed_read_cb(ISC_R_RANGE, session);
1281
0
    goto done;
1282
0
  }
1283
1284
0
  if ((size_t)readlen < region->length) {
1285
0
    size_t unread_size = region->length - readlen;
1286
0
    if (session->buf == NULL) {
1287
0
      isc_buffer_allocate(session->mctx, &session->buf,
1288
0
              unread_size);
1289
0
    }
1290
0
    isc_buffer_putmem(session->buf, region->base + readlen,
1291
0
          unread_size);
1292
0
    if (session->handle != NULL) {
1293
0
      INSIST(VALID_NMHANDLE(session->handle));
1294
0
      isc_nm_read_stop(session->handle);
1295
0
    }
1296
0
    http_do_bio_async(session);
1297
0
  } else {
1298
    /* We might have something to receive or send, do IO */
1299
0
    http_do_bio(session, NULL, NULL, NULL);
1300
0
  }
1301
1302
0
done:
1303
0
  isc__nm_httpsession_detach(&tmpsess);
1304
0
}
1305
1306
static void
1307
call_pending_callbacks(isc__nm_http_pending_callbacks_t pending_callbacks,
1308
0
           isc_result_t result) {
1309
0
  ISC_LIST_FOREACH(pending_callbacks, cbreq, link) {
1310
0
    ISC_LIST_UNLINK(pending_callbacks, cbreq, link);
1311
0
    isc__nm_sendcb(cbreq->handle->sock, cbreq, result, true);
1312
0
  }
1313
0
}
1314
1315
static void
1316
0
http_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
1317
0
  isc_http_send_req_t *req = (isc_http_send_req_t *)arg;
1318
0
  isc_nm_http_session_t *session = req->session;
1319
0
  isc_nmhandle_t *transphandle = req->transphandle;
1320
1321
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1322
0
  REQUIRE(VALID_NMHANDLE(handle));
1323
1324
0
  if (isc__nm_httpsession_active(session)) {
1325
0
    INSIST(session->handle == handle);
1326
0
  }
1327
1328
0
  call_pending_callbacks(req->pending_write_callbacks, result);
1329
1330
0
  if (req->cb != NULL) {
1331
0
    req->cb(req->httphandle, result, req->cbarg);
1332
0
    isc_nmhandle_detach(&req->httphandle);
1333
0
  }
1334
1335
0
  session->data_in_flight -=
1336
0
    isc_buffer_usedlength(req->pending_write_data);
1337
0
  isc_buffer_free(&req->pending_write_data);
1338
0
  session->processed += req->submitted;
1339
0
  isc_mem_put(session->mctx, req, sizeof(*req));
1340
1341
0
  session->sending--;
1342
1343
0
  if (result == ISC_R_SUCCESS) {
1344
0
    http_do_bio(session, NULL, NULL, NULL);
1345
0
  } else {
1346
0
    finish_http_session(session);
1347
0
  }
1348
0
  isc_nmhandle_detach(&transphandle);
1349
1350
0
  isc__nm_httpsession_detach(&session);
1351
0
}
1352
1353
static void
1354
move_pending_send_callbacks(isc_nm_http_session_t *session,
1355
0
          isc_http_send_req_t *send) {
1356
0
  STATIC_ASSERT(
1357
0
    sizeof(session->pending_write_callbacks) ==
1358
0
      sizeof(send->pending_write_callbacks),
1359
0
    "size of pending writes requests callbacks lists differs");
1360
0
  memmove(&send->pending_write_callbacks,
1361
0
    &session->pending_write_callbacks,
1362
0
    sizeof(session->pending_write_callbacks));
1363
0
  ISC_LIST_INIT(session->pending_write_callbacks);
1364
0
}
1365
1366
static inline void
1367
http_append_pending_send_request(isc_nm_http_session_t *session,
1368
         isc_nmhandle_t *httphandle, isc_nm_cb_t cb,
1369
0
         void *cbarg) {
1370
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1371
0
  REQUIRE(VALID_NMHANDLE(httphandle));
1372
0
  REQUIRE(cb != NULL);
1373
1374
0
  isc__nm_uvreq_t *newcb = isc__nm_uvreq_get(httphandle->sock);
1375
1376
0
  newcb->cb.send = cb;
1377
0
  newcb->cbarg = cbarg;
1378
0
  isc_nmhandle_attach(httphandle, &newcb->handle);
1379
0
  ISC_LIST_APPEND(session->pending_write_callbacks, newcb, link);
1380
0
}
1381
1382
static void
1383
http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
1384
0
       isc_nm_cb_t cb, void *cbarg) {
1385
0
  isc_http_send_req_t *send = NULL;
1386
0
  size_t total = 0;
1387
0
  isc_region_t send_data = { 0 };
1388
0
  isc_nmhandle_t *transphandle = NULL;
1389
0
#ifdef ENABLE_HTTP_WRITE_BUFFERING
1390
0
  size_t max_total_write_size = 0;
1391
0
#endif /* ENABLE_HTTP_WRITE_BUFFERING */
1392
1393
0
  if (!isc__nm_httpsession_active(session)) {
1394
0
    if (cb != NULL) {
1395
0
      isc__nm_uvreq_t *req =
1396
0
        isc__nm_uvreq_get(httphandle->sock);
1397
1398
0
      req->cb.send = cb;
1399
0
      req->cbarg = cbarg;
1400
0
      isc_nmhandle_attach(httphandle, &req->handle);
1401
0
      isc__nm_sendcb(httphandle->sock, req, ISC_R_CANCELED,
1402
0
               true);
1403
0
    }
1404
0
    return;
1405
0
  } else if (!nghttp2_session_want_write(session->ngsession) &&
1406
0
       session->pending_write_data == NULL)
1407
0
  {
1408
0
    if (cb != NULL) {
1409
0
      http_append_pending_send_request(session, httphandle,
1410
0
               cb, cbarg);
1411
0
    }
1412
0
    return;
1413
0
  }
1414
1415
  /*
1416
   * We need to attach to the session->handle earlier because as an
1417
   * indirect result of the nghttp2_session_mem_send() the session
1418
   * might get closed and the handle detached. However, there is
1419
   * still some outgoing data to handle and we need to call it
1420
   * anyway if only to get the write callback passed here to get
1421
   * called properly.
1422
   */
1423
0
  isc_nmhandle_attach(session->handle, &transphandle);
1424
1425
0
  while (nghttp2_session_want_write(session->ngsession)) {
1426
0
    const uint8_t *data = NULL;
1427
0
    const size_t pending =
1428
0
      nghttp2_session_mem_send(session->ngsession, &data);
1429
0
    const size_t new_total = total + pending;
1430
1431
    /*
1432
     * Sometimes nghttp2_session_mem_send() does not return any
1433
     * data to send even though nghttp2_session_want_write()
1434
     * returns success.
1435
     */
1436
0
    if (pending == 0 || data == NULL) {
1437
0
      break;
1438
0
    }
1439
1440
    /* reallocate buffer if required */
1441
0
    if (session->pending_write_data == NULL) {
1442
0
      isc_buffer_allocate(session->mctx,
1443
0
              &session->pending_write_data,
1444
0
              INITIAL_DNS_MESSAGE_BUFFER_SIZE);
1445
0
    }
1446
0
    isc_buffer_putmem(session->pending_write_data, data, pending);
1447
0
    total = new_total;
1448
0
  }
1449
1450
0
#ifdef ENABLE_HTTP_WRITE_BUFFERING
1451
0
  if (session->pending_write_data != NULL) {
1452
0
    max_total_write_size =
1453
0
      isc_buffer_usedlength(session->pending_write_data);
1454
0
  }
1455
1456
  /*
1457
   * Here we are trying to flush the pending writes buffer earlier
1458
   * to avoid hitting unnecessary limitations on a TLS record size
1459
   * within some tools (e.g. flamethrower).
1460
   */
1461
0
  if (cb != NULL) {
1462
    /*
1463
     * Case 0: The callback is specified, that means that a DNS
1464
     * message is ready. Let's flush the buffer.
1465
     */
1466
0
    total = max_total_write_size;
1467
0
  } else if (max_total_write_size >= FLUSH_HTTP_WRITE_BUFFER_AFTER) {
1468
    /*
1469
     * Case 1: We have equal or more than
1470
     * FLUSH_HTTP_WRITE_BUFFER_AFTER bytes to send. Let's flush it.
1471
     */
1472
0
    total = max_total_write_size;
1473
0
  } else if (session->sending > 0 && total > 0) {
1474
    /*
1475
     * Case 2: There is one or more write requests in flight and
1476
     * we have some new data from nghttp2 to send.
1477
     * Then let's return from the function: as soon as the
1478
     * "in-flight" write callback gets called or we have reached
1479
     * FLUSH_HTTP_WRITE_BUFFER_AFTER bytes in the write buffer, we
1480
     * will flush the buffer. */
1481
0
    INSIST(cb == NULL);
1482
0
    goto nothing_to_send;
1483
0
  } else if (session->sending == 0 && total == 0 &&
1484
0
       session->pending_write_data != NULL)
1485
0
  {
1486
    /*
1487
     * Case 3: There is no write in flight and we haven't got
1488
     * anything new from nghttp2, but there is some data pending
1489
     * in the write buffer. Let's flush the buffer.
1490
     */
1491
0
    isc_region_t region = { 0 };
1492
0
    total = isc_buffer_usedlength(session->pending_write_data);
1493
0
    INSIST(total > 0);
1494
0
    isc_buffer_usedregion(session->pending_write_data, &region);
1495
0
    INSIST(total == region.length);
1496
0
  } else {
1497
    /*
1498
     * The other cases are uninteresting, fall-through ones.
1499
     * In the following cases (4-6) we will just bail out:
1500
     *
1501
     * Case 4: There is nothing new to send, nor anything in the
1502
     * write buffer.
1503
     * Case 5: There is nothing new to send and there are write
1504
     * request(s) in flight.
1505
     * Case 6: There is nothing new to send nor are there any
1506
     * write requests in flight.
1507
     *
1508
     * Case 7: There is some new data to send and there are no
1509
     * write requests in flight: Let's send the data.
1510
     */
1511
0
    INSIST((total == 0 && session->pending_write_data == NULL) ||
1512
0
           (total == 0 && session->sending > 0) ||
1513
0
           (total == 0 && session->sending == 0) ||
1514
0
           (total > 0 && session->sending == 0));
1515
0
  }
1516
0
#endif /* ENABLE_HTTP_WRITE_BUFFERING */
1517
1518
0
  if (total == 0) {
1519
    /* No data returned */
1520
0
    if (cb != NULL) {
1521
0
      http_append_pending_send_request(session, httphandle,
1522
0
               cb, cbarg);
1523
0
    }
1524
0
    goto nothing_to_send;
1525
0
  }
1526
1527
  /*
1528
   * If we have reached this point it means that we need to send some
1529
   * data and flush the outgoing buffer. The code below does that.
1530
   */
1531
0
  send = isc_mem_get(session->mctx, sizeof(*send));
1532
1533
0
  *send = (isc_http_send_req_t){ .pending_write_data =
1534
0
                 session->pending_write_data,
1535
0
               .cb = cb,
1536
0
               .cbarg = cbarg,
1537
0
               .submitted = session->submitted };
1538
0
  session->submitted = 0;
1539
0
  session->pending_write_data = NULL;
1540
0
  move_pending_send_callbacks(session, send);
1541
1542
0
  send->transphandle = transphandle;
1543
0
  isc__nm_httpsession_attach(session, &send->session);
1544
1545
0
  if (cb != NULL) {
1546
0
    INSIST(VALID_NMHANDLE(httphandle));
1547
0
    isc_nmhandle_attach(httphandle, &send->httphandle);
1548
0
  }
1549
1550
0
  session->sending++;
1551
0
  isc_buffer_usedregion(send->pending_write_data, &send_data);
1552
0
  session->data_in_flight += send_data.length;
1553
0
  isc_nm_send(transphandle, &send_data, http_writecb, send);
1554
0
  return;
1555
1556
0
nothing_to_send:
1557
0
  isc_nmhandle_detach(&transphandle);
1558
0
}
1559
1560
static inline bool
1561
0
http_too_many_active_streams(isc_nm_http_session_t *session) {
1562
0
  const uint64_t active_streams = session->received - session->processed;
1563
  /*
1564
   * The motivation behind capping the maximum active streams number
1565
   * to a third of maximum streams is to allow the value to scale
1566
   * with the max number of streams.
1567
   *
1568
   * We do not want to have too many active streams at once as every
1569
   * stream is processed as a separate virtual connection by the
1570
   * higher level code. If a client sends a bulk of requests without
1571
   * waiting for the previous ones to complete we might want to
1572
   * throttle it as it might be not a friend knocking at the
1573
   * door. We already have some job to do for it.
1574
   */
1575
0
  const uint64_t max_active_streams =
1576
0
    ISC_MAX(ISC_NETMGR_MAX_STREAM_CLIENTS_PER_CONN,
1577
0
      (session->max_concurrent_streams * 6) / 10); /* 60% */
1578
1579
0
  if (session->client) {
1580
0
    return false;
1581
0
  }
1582
1583
  /*
1584
   * Do not process incoming data if there are too many active DNS
1585
   * clients (streams) per connection.
1586
   */
1587
0
  if (active_streams >= max_active_streams) {
1588
0
    return true;
1589
0
  }
1590
1591
0
  return false;
1592
0
}
1593
1594
static void
1595
http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
1596
0
      isc_nm_cb_t send_cb, void *send_cbarg) {
1597
0
  isc__nm_uvreq_t *req = NULL;
1598
0
  size_t remaining = 0;
1599
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1600
1601
0
  if (session->closed) {
1602
0
    goto cancel;
1603
0
  } else if (session->closing) {
1604
    /*
1605
     * There might be leftover callbacks waiting to be received
1606
     */
1607
0
    if (session->sending == 0) {
1608
0
      finish_http_session(session);
1609
0
    }
1610
0
    goto cancel;
1611
0
  } else if (nghttp2_session_want_read(session->ngsession) == 0 &&
1612
0
       nghttp2_session_want_write(session->ngsession) == 0 &&
1613
0
       session->pending_write_data == NULL)
1614
0
  {
1615
0
    session->closing = true;
1616
0
    if (session->handle != NULL) {
1617
0
      isc_nm_read_stop(session->handle);
1618
0
    }
1619
0
    if (session->sending == 0) {
1620
0
      finish_http_session(session);
1621
0
    }
1622
0
    goto cancel;
1623
0
  }
1624
1625
0
  else if (session->buf != NULL)
1626
0
  {
1627
0
    remaining = isc_buffer_remaininglength(session->buf);
1628
0
  }
1629
1630
0
  if (nghttp2_session_want_read(session->ngsession) != 0) {
1631
0
    if (!session->reading) {
1632
      /* We have not yet started reading from this handle */
1633
0
      isc__nmsocket_timer_start(session->handle->sock);
1634
0
      isc_nm_read(session->handle, http_readcb, session);
1635
0
      session->reading = true;
1636
0
    } else if (session->buf != NULL && remaining > 0) {
1637
      /* Leftover data in the buffer, use it */
1638
0
      size_t remaining_after = 0;
1639
0
      ssize_t readlen = 0;
1640
0
      isc_nm_http_session_t *tmpsess = NULL;
1641
1642
      /*
1643
       * Let's ensure that HTTP/2 session and its associated
1644
       * data will not go "out of scope" too early.
1645
       */
1646
0
      isc__nm_httpsession_attach(session, &tmpsess);
1647
1648
0
      readlen = http_process_input_data(session,
1649
0
                session->buf);
1650
1651
0
      remaining_after =
1652
0
        isc_buffer_remaininglength(session->buf);
1653
1654
0
      if (readlen < 0) {
1655
0
        failed_read_cb(ISC_R_UNEXPECTED, session);
1656
0
      } else if (http_is_flooding_peer(session)) {
1657
0
        http_log_flooding_peer(session);
1658
0
        failed_read_cb(ISC_R_RANGE, session);
1659
0
      } else if ((size_t)readlen == remaining) {
1660
0
        isc_buffer_clear(session->buf);
1661
0
        isc_buffer_compact(session->buf);
1662
0
        http_do_bio(session, send_httphandle, send_cb,
1663
0
              send_cbarg);
1664
0
        isc__nm_httpsession_detach(&tmpsess);
1665
0
        return;
1666
0
      } else if (remaining_after > 0 &&
1667
0
           remaining_after < remaining)
1668
0
      {
1669
        /*
1670
         * We have processed a part of the data, now
1671
         * let's delay processing of whatever is left
1672
         * here. We want it to be an async operation so
1673
         * that we will:
1674
         *
1675
         * a) let other things run;
1676
         * b) have finer grained control over how much
1677
         * data is processed at once, because nghttp2
1678
         * would happily consume as much data we pass to
1679
         * it and that could overwhelm the server.
1680
         */
1681
0
        http_do_bio_async(session);
1682
0
      }
1683
0
      isc__nm_httpsession_detach(&tmpsess);
1684
0
    } else if (session->handle != NULL) {
1685
0
      INSIST(VALID_NMHANDLE(session->handle));
1686
      /*
1687
       * Resume reading, it's idempotent, wait for more
1688
       */
1689
0
      isc__nmsocket_timer_start(session->handle->sock);
1690
0
      isc_nm_read(session->handle, http_readcb, session);
1691
0
    }
1692
0
  } else if (session->handle != NULL) {
1693
0
    INSIST(VALID_NMHANDLE(session->handle));
1694
    /* We don't want more data, stop reading for now */
1695
0
    isc_nm_read_stop(session->handle);
1696
0
  }
1697
1698
  /* we might have some data to send after processing */
1699
0
  http_send_outgoing(session, send_httphandle, send_cb, send_cbarg);
1700
1701
0
  return;
1702
0
cancel:
1703
0
  if (send_cb == NULL) {
1704
0
    return;
1705
0
  }
1706
0
  req = isc__nm_uvreq_get(send_httphandle->sock);
1707
1708
0
  req->cb.send = send_cb;
1709
0
  req->cbarg = send_cbarg;
1710
0
  isc_nmhandle_attach(send_httphandle, &req->handle);
1711
0
  isc__nm_sendcb(send_httphandle->sock, req, ISC_R_CANCELED, true);
1712
0
}
1713
1714
static void
1715
0
http_do_bio_async_cb(void *arg) {
1716
0
  isc_nm_http_session_t *session = arg;
1717
1718
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1719
1720
0
  session->async_queued = false;
1721
1722
0
  if (session->handle != NULL &&
1723
0
      !isc__nmsocket_closing(session->handle->sock))
1724
0
  {
1725
0
    http_do_bio(session, NULL, NULL, NULL);
1726
0
  }
1727
1728
0
  isc__nm_httpsession_detach(&session);
1729
0
}
1730
1731
static void
1732
0
http_do_bio_async(isc_nm_http_session_t *session) {
1733
0
  isc_nm_http_session_t *tmpsess = NULL;
1734
1735
0
  REQUIRE(VALID_HTTP2_SESSION(session));
1736
1737
0
  if (session->handle == NULL ||
1738
0
      isc__nmsocket_closing(session->handle->sock) ||
1739
0
      session->async_queued)
1740
0
  {
1741
0
    return;
1742
0
  }
1743
0
  session->async_queued = true;
1744
0
  isc__nm_httpsession_attach(session, &tmpsess);
1745
0
  isc_async_run(session->handle->sock->worker->loop, http_do_bio_async_cb,
1746
0
          tmpsess);
1747
0
}
1748
1749
static isc_result_t
1750
0
get_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
1751
0
  http_cstream_t *cstream = sock->h2->connect.cstream;
1752
0
  isc_result_t result;
1753
1754
0
  REQUIRE(streamp != NULL && *streamp == NULL);
1755
1756
0
  sock->h2->connect.cstream = NULL;
1757
1758
0
  if (cstream == NULL) {
1759
0
    result = new_http_cstream(sock, &cstream);
1760
0
    if (result != ISC_R_SUCCESS) {
1761
0
      INSIST(cstream == NULL);
1762
0
      return result;
1763
0
    }
1764
0
  }
1765
1766
0
  *streamp = cstream;
1767
0
  return ISC_R_SUCCESS;
1768
0
}
1769
1770
static void
1771
http_call_connect_cb(isc_nmsocket_t *sock, isc_nm_http_session_t *session,
1772
0
         isc_result_t result) {
1773
0
  isc_nmhandle_t *httphandle = isc__nmhandle_get(sock, &sock->peer,
1774
0
                   &sock->iface);
1775
0
  void *cbarg;
1776
0
  isc_nm_cb_t connect_cb;
1777
1778
0
  REQUIRE(sock->connect_cb != NULL);
1779
1780
0
  cbarg = sock->connect_cbarg;
1781
0
  connect_cb = sock->connect_cb;
1782
0
  isc__nmsocket_clearcb(sock);
1783
0
  if (result == ISC_R_SUCCESS) {
1784
0
    if (session != NULL) {
1785
0
      session->client_httphandle = httphandle;
1786
0
    }
1787
0
    connect_cb(httphandle, result, cbarg);
1788
0
  } else {
1789
0
    connect_cb(httphandle, result, cbarg);
1790
0
    isc_nmhandle_detach(&httphandle);
1791
0
  }
1792
0
}
1793
1794
static void
1795
0
transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
1796
0
  isc_nmsocket_t *http_sock = (isc_nmsocket_t *)cbarg;
1797
0
  isc_nmsocket_t *transp_sock = NULL;
1798
0
  isc_nm_http_session_t *session = NULL;
1799
0
  http_cstream_t *cstream = NULL;
1800
0
  isc_mem_t *mctx = NULL;
1801
1802
0
  REQUIRE(VALID_NMSOCK(http_sock));
1803
0
  REQUIRE(VALID_NMHANDLE(handle));
1804
1805
0
  transp_sock = handle->sock;
1806
1807
0
  REQUIRE(VALID_NMSOCK(transp_sock));
1808
1809
0
  mctx = transp_sock->worker->mctx;
1810
1811
0
  INSIST(http_sock->h2->connect.uri != NULL);
1812
1813
0
  http_sock->h2->connect.tls_peer_verify_string =
1814
0
    isc_nm_verify_tls_peer_result_string(handle);
1815
0
  if (result != ISC_R_SUCCESS) {
1816
0
    goto error;
1817
0
  }
1818
1819
0
  http_initsocket(transp_sock);
1820
0
  new_session(mctx, http_sock->h2->connect.tlsctx, &session);
1821
0
  session->client = true;
1822
0
  transp_sock->h2->session = session;
1823
0
  http_sock->h2->connect.tlsctx = NULL;
1824
  /* otherwise we will get some garbage output in DIG */
1825
0
  http_sock->iface = isc_nmhandle_localaddr(handle);
1826
0
  http_sock->peer = isc_nmhandle_peeraddr(handle);
1827
1828
0
  transp_sock->h2->connect.post = http_sock->h2->connect.post;
1829
0
  transp_sock->h2->connect.uri = http_sock->h2->connect.uri;
1830
0
  http_sock->h2->connect.uri = NULL;
1831
0
  isc__nm_httpsession_attach(session, &http_sock->h2->session);
1832
1833
0
  if (session->tlsctx != NULL) {
1834
0
    const unsigned char *alpn = NULL;
1835
0
    unsigned int alpnlen = 0;
1836
1837
0
    INSIST(transp_sock->type == isc_nm_tlssocket ||
1838
0
           transp_sock->type == isc_nm_proxystreamsocket);
1839
1840
0
    isc__nmhandle_get_selected_alpn(handle, &alpn, &alpnlen);
1841
0
    if (alpn == NULL || alpnlen != NGHTTP2_PROTO_VERSION_ID_LEN ||
1842
0
        memcmp(NGHTTP2_PROTO_VERSION_ID, alpn,
1843
0
         NGHTTP2_PROTO_VERSION_ID_LEN) != 0)
1844
0
    {
1845
      /*
1846
       * HTTP/2 negotiation error.
1847
       * Any sensible DoH client
1848
       * will fail if HTTP/2 cannot
1849
       * be negotiated via ALPN.
1850
       */
1851
0
      result = ISC_R_HTTP2ALPNERROR;
1852
0
      goto error;
1853
0
    }
1854
0
  }
1855
1856
0
  isc_nmhandle_attach(handle, &session->handle);
1857
1858
0
  initialize_nghttp2_client_session(session);
1859
0
  if (!send_client_connection_header(session)) {
1860
0
    goto error;
1861
0
  }
1862
1863
0
  result = get_http_cstream(http_sock, &cstream);
1864
0
  http_sock->h2->connect.cstream = cstream;
1865
0
  if (result != ISC_R_SUCCESS) {
1866
0
    goto error;
1867
0
  }
1868
1869
0
  http_transpost_tcp_nodelay(handle);
1870
0
  isc__nmhandle_set_manual_timer(session->handle, true);
1871
1872
0
  http_call_connect_cb(http_sock, session, result);
1873
1874
0
  http_do_bio(session, NULL, NULL, NULL);
1875
0
  isc__nmsocket_detach(&http_sock);
1876
0
  return;
1877
1878
0
error:
1879
0
  http_call_connect_cb(http_sock, session, result);
1880
1881
0
  if (http_sock->h2->connect.uri != NULL) {
1882
0
    isc_mem_free(http_sock->worker->mctx,
1883
0
           http_sock->h2->connect.uri);
1884
0
  }
1885
1886
0
  isc__nmsocket_prep_destroy(http_sock);
1887
0
  isc__nmsocket_detach(&http_sock);
1888
0
}
1889
1890
void
1891
isc_nm_httpconnect(isc_sockaddr_t *local, isc_sockaddr_t *peer, const char *uri,
1892
       bool post, isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *tlsctx,
1893
       const char *sni_hostname,
1894
       isc_tlsctx_client_session_cache_t *client_sess_cache,
1895
       unsigned int timeout, isc_nm_proxy_type_t proxy_type,
1896
0
       isc_nm_proxyheader_info_t *proxy_info) {
1897
0
  isc_sockaddr_t local_interface;
1898
0
  isc_nmsocket_t *sock = NULL;
1899
0
  isc__networker_t *worker = isc__networker_current();
1900
1901
0
  REQUIRE(cb != NULL);
1902
0
  REQUIRE(peer != NULL);
1903
0
  REQUIRE(uri != NULL);
1904
0
  REQUIRE(*uri != '\0');
1905
1906
0
  if (isc__nm_closing(worker)) {
1907
0
    cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
1908
0
    return;
1909
0
  }
1910
1911
0
  if (local == NULL) {
1912
0
    isc_sockaddr_anyofpf(&local_interface, peer->type.sa.sa_family);
1913
0
    local = &local_interface;
1914
0
  }
1915
1916
0
  sock = isc_mempool_get(worker->nmsocket_pool);
1917
0
  isc__nmsocket_init(sock, worker, isc_nm_httpsocket, local, NULL);
1918
0
  http_initsocket(sock);
1919
1920
0
  sock->connect_timeout = timeout;
1921
0
  sock->connect_cb = cb;
1922
0
  sock->connect_cbarg = cbarg;
1923
0
  sock->client = true;
1924
1925
0
  if (isc__nm_closing(worker)) {
1926
0
    isc__nm_uvreq_t *req = isc__nm_uvreq_get(sock);
1927
1928
0
    req->cb.connect = cb;
1929
0
    req->cbarg = cbarg;
1930
0
    req->peer = *peer;
1931
0
    req->local = *local;
1932
0
    req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
1933
1934
0
    isc__nmsocket_clearcb(sock);
1935
0
    isc__nm_connectcb(sock, req, ISC_R_SHUTTINGDOWN, true);
1936
0
    isc__nmsocket_prep_destroy(sock);
1937
0
    isc__nmsocket_detach(&sock);
1938
0
    return;
1939
0
  }
1940
1941
0
  *sock->h2 = (isc_nmsocket_h2_t){ .connect.uri = isc_mem_strdup(
1942
0
             sock->worker->mctx, uri),
1943
0
           .connect.post = post,
1944
0
           .connect.tlsctx = tlsctx };
1945
0
  ISC_LINK_INIT(sock->h2, link);
1946
1947
  /*
1948
   * We need to prevent the interface object data from going out of
1949
   * scope too early.
1950
   */
1951
0
  if (local == &local_interface) {
1952
0
    sock->h2->connect.local_interface = local_interface;
1953
0
    sock->iface = sock->h2->connect.local_interface;
1954
0
  }
1955
1956
0
  switch (proxy_type) {
1957
0
  case ISC_NM_PROXY_NONE:
1958
0
    if (tlsctx != NULL) {
1959
0
      isc_nm_tlsconnect(local, peer, transport_connect_cb,
1960
0
            sock, tlsctx, sni_hostname,
1961
0
            client_sess_cache, timeout, false,
1962
0
            NULL);
1963
0
    } else {
1964
0
      isc_nm_tcpconnect(local, peer, transport_connect_cb,
1965
0
            sock, timeout);
1966
0
    }
1967
0
    break;
1968
0
  case ISC_NM_PROXY_PLAIN:
1969
0
    if (tlsctx != NULL) {
1970
0
      isc_nm_tlsconnect(local, peer, transport_connect_cb,
1971
0
            sock, tlsctx, sni_hostname,
1972
0
            client_sess_cache, timeout, true,
1973
0
            proxy_info);
1974
0
    } else {
1975
0
      isc_nm_proxystreamconnect(
1976
0
        local, peer, transport_connect_cb, sock,
1977
0
        timeout, NULL, NULL, NULL, proxy_info);
1978
0
    }
1979
0
    break;
1980
0
  case ISC_NM_PROXY_ENCRYPTED:
1981
0
    INSIST(tlsctx != NULL);
1982
0
    isc_nm_proxystreamconnect(local, peer, transport_connect_cb,
1983
0
            sock, timeout, tlsctx, sni_hostname,
1984
0
            client_sess_cache, proxy_info);
1985
0
    break;
1986
0
  default:
1987
0
    UNREACHABLE();
1988
0
  }
1989
0
}
1990
1991
static isc_result_t
1992
0
client_send(isc_nmhandle_t *handle, const isc_region_t *region) {
1993
0
  isc_result_t result = ISC_R_SUCCESS;
1994
0
  isc_nmsocket_t *sock = handle->sock;
1995
0
  isc_mem_t *mctx = sock->worker->mctx;
1996
0
  isc_nm_http_session_t *session = sock->h2->session;
1997
0
  http_cstream_t *cstream = sock->h2->connect.cstream;
1998
1999
0
  REQUIRE(VALID_HTTP2_SESSION(handle->sock->h2->session));
2000
0
  REQUIRE(session->client);
2001
0
  REQUIRE(region != NULL);
2002
0
  REQUIRE(region->base != NULL);
2003
0
  REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
2004
2005
0
  if (session->closed) {
2006
0
    return ISC_R_CANCELED;
2007
0
  }
2008
2009
0
  INSIST(cstream != NULL);
2010
2011
0
  if (cstream->post) {
2012
    /* POST */
2013
0
    isc_buffer_allocate(mctx, &cstream->postdata, region->length);
2014
0
    isc_buffer_putmem(cstream->postdata, region->base,
2015
0
          region->length);
2016
0
  } else {
2017
    /* GET */
2018
0
    size_t path_size = 0;
2019
0
    char *base64url_data = NULL;
2020
0
    size_t base64url_data_len = 0;
2021
0
    isc_buffer_t *buf = NULL;
2022
0
    isc_region_t data = *region;
2023
0
    isc_region_t base64_region;
2024
0
    size_t base64_len = ((4 * data.length / 3) + 3) & ~3;
2025
2026
0
    isc_buffer_allocate(mctx, &buf, base64_len);
2027
2028
0
    result = isc_base64_totext(&data, -1, "", buf);
2029
0
    if (result != ISC_R_SUCCESS) {
2030
0
      isc_buffer_free(&buf);
2031
0
      goto error;
2032
0
    }
2033
2034
0
    isc_buffer_usedregion(buf, &base64_region);
2035
0
    INSIST(base64_region.length == base64_len);
2036
2037
0
    base64url_data = isc__nm_base64_to_base64url(
2038
0
      mctx, (const char *)base64_region.base,
2039
0
      base64_region.length, &base64url_data_len);
2040
0
    isc_buffer_free(&buf);
2041
0
    if (base64url_data == NULL) {
2042
0
      goto error;
2043
0
    }
2044
2045
    /* len("?dns=") + len(path) + len(base64url) + len("\0") */
2046
0
    path_size = cstream->pathlen + base64url_data_len + 5 + 1;
2047
0
    cstream->GET_path = isc_mem_allocate(mctx, path_size);
2048
0
    cstream->GET_path_len = (size_t)snprintf(
2049
0
      cstream->GET_path, path_size, "%.*s?dns=%s",
2050
0
      (int)cstream->pathlen, cstream->path, base64url_data);
2051
2052
0
    INSIST(cstream->GET_path_len == (path_size - 1));
2053
0
    isc_mem_free(mctx, base64url_data);
2054
0
  }
2055
2056
0
  cstream->sending = true;
2057
2058
0
  sock->h2->connect.cstream = NULL;
2059
0
  result = client_submit_request(session, cstream);
2060
0
  if (result != ISC_R_SUCCESS) {
2061
0
    put_http_cstream(session->mctx, cstream);
2062
0
    goto error;
2063
0
  }
2064
2065
0
error:
2066
0
  return result;
2067
0
}
2068
2069
isc_result_t
2070
isc__nm_http_request(isc_nmhandle_t *handle, isc_region_t *region,
2071
0
         isc_nm_recv_cb_t cb, void *cbarg) {
2072
0
  isc_result_t result = ISC_R_SUCCESS;
2073
0
  isc_nmsocket_t *sock = NULL;
2074
0
  http_cstream_t *cstream = NULL;
2075
2076
0
  REQUIRE(VALID_NMHANDLE(handle));
2077
0
  REQUIRE(VALID_NMSOCK(handle->sock));
2078
0
  REQUIRE(handle->sock->tid == isc_tid());
2079
0
  REQUIRE(handle->sock->client);
2080
2081
0
  REQUIRE(cb != NULL);
2082
2083
0
  sock = handle->sock;
2084
2085
0
  isc__nm_http_read(handle, cb, cbarg);
2086
0
  if (!isc__nm_httpsession_active(handle->sock->h2->session)) {
2087
    /* the callback was called by isc__nm_http_read() */
2088
0
    return ISC_R_CANCELED;
2089
0
  }
2090
0
  result = client_send(handle, region);
2091
0
  if (result != ISC_R_SUCCESS) {
2092
0
    goto error;
2093
0
  }
2094
2095
0
  return ISC_R_SUCCESS;
2096
2097
0
error:
2098
  /*
2099
   * client_send() detaches and frees the stream on a submit failure
2100
   * (it nullifies sock->h2->connect.cstream before submitting, then
2101
   * frees it on the failure branch), so the reloaded pointer can be
2102
   * NULL here.  The caller still gets the error result and reports the
2103
   * failure itself.
2104
   */
2105
0
  cstream = sock->h2->connect.cstream;
2106
0
  if (cstream != NULL && cstream->read_cb != NULL) {
2107
0
    cstream->read_cb(handle, result, NULL, cstream->read_cbarg);
2108
0
  }
2109
0
  return result;
2110
0
}
2111
2112
static int
2113
server_on_begin_headers_callback(nghttp2_session *ngsession,
2114
0
         const nghttp2_frame *frame, void *user_data) {
2115
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
2116
0
  isc_nmsocket_t *socket = NULL;
2117
0
  isc__networker_t *worker = NULL;
2118
0
  isc_sockaddr_t local;
2119
2120
0
  if (frame->hd.type != NGHTTP2_HEADERS ||
2121
0
      frame->headers.cat != NGHTTP2_HCAT_REQUEST)
2122
0
  {
2123
0
    return 0;
2124
0
  } else if (frame->hd.length > MAX_ALLOWED_DATA_IN_HEADERS) {
2125
0
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2126
0
  }
2127
2128
0
  if (session->nsstreams >= session->max_concurrent_streams) {
2129
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
2130
0
  }
2131
2132
0
  INSIST(session->handle->sock->tid == isc_tid());
2133
2134
0
  worker = session->handle->sock->worker;
2135
0
  socket = isc_mempool_get(worker->nmsocket_pool);
2136
0
  local = isc_nmhandle_localaddr(session->handle);
2137
0
  isc__nmsocket_init(socket, worker, isc_nm_httpsocket, &local, NULL);
2138
0
  http_initsocket(socket);
2139
0
  socket->peer = isc_nmhandle_peeraddr(session->handle);
2140
0
  *socket->h2 = (isc_nmsocket_h2_t){
2141
0
    .psock = socket,
2142
0
    .stream_id = frame->hd.stream_id,
2143
0
    .headers_error_code = ISC_HTTP_ERROR_SUCCESS,
2144
0
    .request_type = ISC_HTTP_REQ_UNSUPPORTED,
2145
0
    .request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED,
2146
0
    .link = ISC_LINK_INITIALIZER,
2147
0
  };
2148
0
  isc_buffer_initnull(&socket->h2->rbuf);
2149
0
  isc_buffer_initnull(&socket->h2->wbuf);
2150
0
  isc_nm_http_endpoints_attach(
2151
0
    http_get_listener_endpoints(session->serversocket, socket->tid),
2152
0
    &socket->h2->peer_endpoints);
2153
0
  session->nsstreams++;
2154
0
  isc__nm_httpsession_attach(session, &socket->h2->session);
2155
0
  ISC_LIST_APPEND(session->sstreams, socket->h2, link);
2156
0
  session->total_opened_sstreams++;
2157
2158
0
  nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
2159
0
               socket);
2160
0
  return 0;
2161
0
}
2162
2163
static isc_http_error_responses_t
2164
server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value,
2165
0
        const size_t valuelen) {
2166
0
  isc_nm_httphandler_t *handler = NULL;
2167
0
  const uint8_t *qstr = NULL;
2168
0
  size_t vlen = valuelen;
2169
2170
0
  qstr = memchr(value, '?', valuelen);
2171
0
  if (qstr != NULL) {
2172
0
    vlen = qstr - value;
2173
0
  }
2174
2175
0
  if (socket->h2->request_path != NULL) {
2176
0
    isc_mem_free(socket->worker->mctx, socket->h2->request_path);
2177
0
  }
2178
0
  socket->h2->request_path = isc_mem_allocate(socket->worker->mctx,
2179
0
                vlen + 1);
2180
0
  strlcpy(socket->h2->request_path, (const char *)value, vlen + 1);
2181
2182
0
  if (!isc_nm_http_path_isvalid(socket->h2->request_path)) {
2183
0
    isc_mem_free(socket->worker->mctx, socket->h2->request_path);
2184
0
    return ISC_HTTP_ERROR_BAD_REQUEST;
2185
0
  }
2186
2187
0
  handler = http_endpoints_find(socket->h2->request_path,
2188
0
              socket->h2->peer_endpoints);
2189
0
  if (handler != NULL) {
2190
0
    socket->h2->cb = handler->cb;
2191
0
    socket->h2->cbarg = handler->cbarg;
2192
0
  } else {
2193
0
    isc_mem_free(socket->worker->mctx, socket->h2->request_path);
2194
0
    return ISC_HTTP_ERROR_NOT_FOUND;
2195
0
  }
2196
2197
0
  if (qstr != NULL) {
2198
0
    const char *dns_value = NULL;
2199
0
    size_t dns_value_len = 0;
2200
2201
0
    if (isc__nm_parse_httpquery((const char *)qstr, &dns_value,
2202
0
              &dns_value_len))
2203
0
    {
2204
0
      const size_t decoded_size = dns_value_len / 4 * 3;
2205
0
      if (decoded_size <= MAX_DNS_MESSAGE_SIZE) {
2206
0
        if (socket->h2->query_data != NULL) {
2207
0
          isc_mem_free(socket->worker->mctx,
2208
0
                 socket->h2->query_data);
2209
0
        }
2210
0
        socket->h2->query_data =
2211
0
          isc__nm_base64url_to_base64(
2212
0
            socket->worker->mctx, dns_value,
2213
0
            dns_value_len,
2214
0
            &socket->h2->query_data_len);
2215
0
        socket->h2->session->processed_useful_data +=
2216
0
          dns_value_len;
2217
0
      } else {
2218
0
        socket->h2->query_too_large = true;
2219
0
        return ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
2220
0
      }
2221
0
    } else {
2222
0
      return ISC_HTTP_ERROR_BAD_REQUEST;
2223
0
    }
2224
0
  }
2225
0
  return ISC_HTTP_ERROR_SUCCESS;
2226
0
}
2227
2228
static isc_http_error_responses_t
2229
server_handle_method_header(isc_nmsocket_t *socket, const uint8_t *value,
2230
0
          const size_t valuelen) {
2231
0
  const char get[] = "GET";
2232
0
  const char post[] = "POST";
2233
2234
0
  if (HEADER_MATCH(get, value, valuelen)) {
2235
0
    socket->h2->request_type = ISC_HTTP_REQ_GET;
2236
0
  } else if (HEADER_MATCH(post, value, valuelen)) {
2237
0
    socket->h2->request_type = ISC_HTTP_REQ_POST;
2238
0
  } else {
2239
0
    return ISC_HTTP_ERROR_NOT_IMPLEMENTED;
2240
0
  }
2241
0
  return ISC_HTTP_ERROR_SUCCESS;
2242
0
}
2243
2244
static isc_http_error_responses_t
2245
server_handle_scheme_header(isc_nmsocket_t *socket, const uint8_t *value,
2246
0
          const size_t valuelen) {
2247
0
  const char http[] = "http";
2248
0
  const char http_secure[] = "https";
2249
2250
0
  if (HEADER_MATCH(http_secure, value, valuelen)) {
2251
0
    socket->h2->request_scheme = ISC_HTTP_SCHEME_HTTP_SECURE;
2252
0
  } else if (HEADER_MATCH(http, value, valuelen)) {
2253
0
    socket->h2->request_scheme = ISC_HTTP_SCHEME_HTTP;
2254
0
  } else {
2255
0
    return ISC_HTTP_ERROR_BAD_REQUEST;
2256
0
  }
2257
0
  return ISC_HTTP_ERROR_SUCCESS;
2258
0
}
2259
2260
static isc_http_error_responses_t
2261
server_handle_content_length_header(isc_nmsocket_t *socket,
2262
            const uint8_t *value,
2263
0
            const size_t valuelen) {
2264
0
  char tmp[32] = { 0 };
2265
0
  const size_t tmplen = sizeof(tmp) - 1;
2266
2267
0
  strncpy(tmp, (const char *)value,
2268
0
    valuelen > tmplen ? tmplen : valuelen);
2269
0
  socket->h2->content_length = strtoul(tmp, NULL, 10);
2270
0
  if (socket->h2->content_length > MAX_DNS_MESSAGE_SIZE) {
2271
0
    return ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
2272
0
  } else if (socket->h2->content_length == 0) {
2273
0
    return ISC_HTTP_ERROR_BAD_REQUEST;
2274
0
  }
2275
0
  return ISC_HTTP_ERROR_SUCCESS;
2276
0
}
2277
2278
static isc_http_error_responses_t
2279
server_handle_content_type_header(isc_nmsocket_t *socket, const uint8_t *value,
2280
0
          const size_t valuelen) {
2281
0
  const char type_dns_message[] = DNS_MEDIA_TYPE;
2282
0
  isc_http_error_responses_t resp = ISC_HTTP_ERROR_SUCCESS;
2283
2284
0
  UNUSED(socket);
2285
2286
0
  if (!HEADER_MATCH(type_dns_message, value, valuelen)) {
2287
0
    resp = ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE;
2288
0
  }
2289
0
  return resp;
2290
0
}
2291
2292
static isc_http_error_responses_t
2293
server_handle_header(isc_nmsocket_t *socket, const uint8_t *name,
2294
         size_t namelen, const uint8_t *value,
2295
0
         const size_t valuelen) {
2296
0
  isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
2297
0
  bool was_error;
2298
0
  const char path[] = ":path";
2299
0
  const char method[] = ":method";
2300
0
  const char scheme[] = ":scheme";
2301
0
  const char content_length[] = "Content-Length";
2302
0
  const char content_type[] = "Content-Type";
2303
2304
0
  was_error = socket->h2->headers_error_code != ISC_HTTP_ERROR_SUCCESS;
2305
  /*
2306
   * process Content-Length even when there was an error,
2307
   * to drop the connection earlier if required.
2308
   */
2309
0
  if (HEADER_MATCH(content_length, name, namelen)) {
2310
0
    code = server_handle_content_length_header(socket, value,
2311
0
                 valuelen);
2312
0
  } else if (!was_error && HEADER_MATCH(path, name, namelen)) {
2313
0
    code = server_handle_path_header(socket, value, valuelen);
2314
0
  } else if (!was_error && HEADER_MATCH(method, name, namelen)) {
2315
0
    code = server_handle_method_header(socket, value, valuelen);
2316
0
  } else if (!was_error && HEADER_MATCH(scheme, name, namelen)) {
2317
0
    code = server_handle_scheme_header(socket, value, valuelen);
2318
0
  } else if (!was_error && HEADER_MATCH(content_type, name, namelen)) {
2319
0
    code = server_handle_content_type_header(socket, value,
2320
0
               valuelen);
2321
0
  }
2322
2323
0
  return code;
2324
0
}
2325
2326
static int
2327
server_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
2328
        const uint8_t *name, size_t namelen,
2329
        const uint8_t *value, size_t valuelen, uint8_t flags,
2330
0
        void *user_data) {
2331
0
  isc_nmsocket_t *socket = NULL;
2332
0
  isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
2333
2334
0
  UNUSED(flags);
2335
0
  UNUSED(user_data);
2336
2337
0
  socket = nghttp2_session_get_stream_user_data(session,
2338
0
                  frame->hd.stream_id);
2339
0
  if (socket == NULL) {
2340
0
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2341
0
  }
2342
2343
0
  socket->h2->headers_data_processed += (namelen + valuelen);
2344
2345
0
  switch (frame->hd.type) {
2346
0
  case NGHTTP2_HEADERS:
2347
0
    if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2348
0
      break;
2349
0
    }
2350
0
    code = server_handle_header(socket, name, namelen, value,
2351
0
              valuelen);
2352
0
    break;
2353
0
  }
2354
2355
0
  INSIST(socket != NULL);
2356
2357
0
  if (socket->h2->headers_data_processed > MAX_ALLOWED_DATA_IN_HEADERS) {
2358
0
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2359
0
  } else if (socket->h2->content_length > MAX_ALLOWED_DATA_IN_POST) {
2360
0
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2361
0
  }
2362
2363
0
  if (code == ISC_HTTP_ERROR_SUCCESS) {
2364
0
    return 0;
2365
0
  } else {
2366
0
    socket->h2->headers_error_code = code;
2367
0
  }
2368
2369
0
  return 0;
2370
0
}
2371
2372
static ssize_t
2373
server_read_callback(nghttp2_session *ngsession, int32_t stream_id,
2374
         uint8_t *buf, size_t length, uint32_t *data_flags,
2375
0
         nghttp2_data_source *source, void *user_data) {
2376
0
  isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
2377
0
  isc_nmsocket_t *socket = (isc_nmsocket_t *)source->ptr;
2378
0
  size_t buflen;
2379
2380
0
  REQUIRE(socket->h2->stream_id == stream_id);
2381
2382
0
  UNUSED(ngsession);
2383
0
  UNUSED(session);
2384
2385
0
  buflen = isc_buffer_remaininglength(&socket->h2->wbuf);
2386
0
  if (buflen > length) {
2387
0
    buflen = length;
2388
0
  }
2389
2390
0
  if (buflen > 0) {
2391
0
    (void)memmove(buf, isc_buffer_current(&socket->h2->wbuf),
2392
0
            buflen);
2393
0
    isc_buffer_forward(&socket->h2->wbuf, buflen);
2394
0
  }
2395
2396
0
  if (isc_buffer_remaininglength(&socket->h2->wbuf) == 0) {
2397
0
    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
2398
0
  }
2399
2400
0
  return buflen;
2401
0
}
2402
2403
static isc_result_t
2404
server_send_response(nghttp2_session *ngsession, int32_t stream_id,
2405
         const nghttp2_nv *nva, size_t nvlen,
2406
0
         isc_nmsocket_t *socket) {
2407
0
  nghttp2_data_provider data_prd;
2408
0
  int rv;
2409
2410
0
  if (socket->h2->response_submitted) {
2411
    /* NGHTTP2 will gladly accept new response (write request)
2412
     * from us even though we cannot send more than one over the
2413
     * same HTTP/2 stream. Thus, we need to handle this case
2414
     * manually. We will return failure code so that it will be
2415
     * passed to the write callback. */
2416
0
    return ISC_R_FAILURE;
2417
0
  }
2418
2419
0
  data_prd.source.ptr = socket;
2420
0
  data_prd.read_callback = server_read_callback;
2421
2422
0
  rv = nghttp2_submit_response(ngsession, stream_id, nva, nvlen,
2423
0
             &data_prd);
2424
0
  if (rv != 0) {
2425
0
    return ISC_R_FAILURE;
2426
0
  }
2427
2428
0
  socket->h2->response_submitted = true;
2429
0
  return ISC_R_SUCCESS;
2430
0
}
2431
2432
#define MAKE_ERROR_REPLY(tag, code, desc) \
2433
  { tag, MAKE_NV2(":status", #code), desc }
2434
2435
/*
2436
 * Here we use roughly the same error codes that Unbound uses.
2437
 * (https://blog.nlnetlabs.nl/dns-over-https-in-unbound/)
2438
 */
2439
2440
static struct http_error_responses {
2441
  const isc_http_error_responses_t type;
2442
  const nghttp2_nv header;
2443
  const char *desc;
2444
} error_responses[] = {
2445
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_BAD_REQUEST, 400, "Bad Request"),
2446
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_FOUND, 404, "Not Found"),
2447
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, 413,
2448
       "Payload Too Large"),
2449
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_URI_TOO_LONG, 414, "URI Too Long"),
2450
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, 415,
2451
       "Unsupported Media Type"),
2452
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_GENERIC, 500, "Internal Server Error"),
2453
  MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_IMPLEMENTED, 501, "Not Implemented")
2454
};
2455
2456
static void
2457
log_server_error_response(const isc_nmsocket_t *socket,
2458
0
        const struct http_error_responses *response) {
2459
0
  const int log_level = ISC_LOG_DEBUG(1);
2460
0
  char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
2461
0
  char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
2462
2463
0
  if (!isc_log_wouldlog(log_level)) {
2464
0
    return;
2465
0
  }
2466
2467
0
  isc_sockaddr_format(&socket->peer, client_sabuf, sizeof(client_sabuf));
2468
0
  isc_sockaddr_format(&socket->iface, local_sabuf, sizeof(local_sabuf));
2469
0
  isc__nmsocket_log(socket, log_level,
2470
0
        "HTTP/2 request from %s (on %s) failed: %s %s",
2471
0
        client_sabuf, local_sabuf, response->header.value,
2472
0
        response->desc);
2473
0
}
2474
2475
static isc_result_t
2476
server_send_error_response(const isc_http_error_responses_t error,
2477
0
         nghttp2_session *ngsession, isc_nmsocket_t *socket) {
2478
0
  void *base;
2479
2480
0
  REQUIRE(error != ISC_HTTP_ERROR_SUCCESS);
2481
2482
0
  base = isc_buffer_base(&socket->h2->rbuf);
2483
0
  if (base != NULL) {
2484
0
    isc_mem_free(socket->h2->session->mctx, base);
2485
0
    isc_buffer_initnull(&socket->h2->rbuf);
2486
0
  }
2487
2488
  /* We do not want the error response to be cached anywhere. */
2489
0
  socket->h2->min_ttl = 0;
2490
2491
0
  for (size_t i = 0;
2492
0
       i < sizeof(error_responses) / sizeof(error_responses[0]); i++)
2493
0
  {
2494
0
    if (error_responses[i].type == error) {
2495
0
      log_server_error_response(socket, &error_responses[i]);
2496
0
      return server_send_response(
2497
0
        ngsession, socket->h2->stream_id,
2498
0
        &error_responses[i].header, 1, socket);
2499
0
    }
2500
0
  }
2501
2502
0
  return server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession,
2503
0
            socket);
2504
0
}
2505
2506
static void
2507
server_call_cb(isc_nmsocket_t *socket, const isc_result_t result,
2508
0
         isc_region_t *data) {
2509
0
  isc_nmhandle_t *handle = NULL;
2510
2511
0
  REQUIRE(VALID_NMSOCK(socket));
2512
2513
  /*
2514
   * In some cases the callback could not have been set (e.g. when
2515
   * the stream was closed prematurely (before processing its HTTP
2516
   * path).
2517
   */
2518
0
  if (socket->h2->cb == NULL) {
2519
0
    return;
2520
0
  }
2521
2522
0
  handle = isc__nmhandle_get(socket, NULL, NULL);
2523
0
  if (result != ISC_R_SUCCESS) {
2524
0
    data = NULL;
2525
0
  } else if (socket->h2->session->handle != NULL) {
2526
0
    isc__nmsocket_timer_restart(socket->h2->session->handle->sock);
2527
0
  }
2528
0
  if (result == ISC_R_SUCCESS) {
2529
0
    socket->h2->request_received = true;
2530
0
    socket->h2->session->received++;
2531
0
  }
2532
0
  socket->h2->cb(handle, result, data, socket->h2->cbarg);
2533
0
  isc_nmhandle_detach(&handle);
2534
0
}
2535
2536
void
2537
0
isc__nm_http_bad_request(isc_nmhandle_t *handle) {
2538
0
  isc_nmsocket_t *sock = NULL;
2539
2540
0
  REQUIRE(VALID_NMHANDLE(handle));
2541
0
  REQUIRE(VALID_NMSOCK(handle->sock));
2542
0
  sock = handle->sock;
2543
0
  REQUIRE(sock->type == isc_nm_httpsocket);
2544
0
  REQUIRE(!sock->client);
2545
0
  REQUIRE(VALID_HTTP2_SESSION(sock->h2->session));
2546
2547
0
  if (sock->h2->response_submitted ||
2548
0
      !isc__nm_httpsession_active(sock->h2->session))
2549
0
  {
2550
0
    return;
2551
0
  }
2552
2553
0
  (void)server_send_error_response(ISC_HTTP_ERROR_BAD_REQUEST,
2554
0
           sock->h2->session->ngsession, sock);
2555
0
}
2556
2557
static int
2558
0
server_on_request_recv(nghttp2_session *ngsession, isc_nmsocket_t *socket) {
2559
0
  isc_result_t result;
2560
0
  isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
2561
0
  isc_region_t data;
2562
0
  uint8_t tmp_buf[MAX_DNS_MESSAGE_SIZE];
2563
2564
0
  code = socket->h2->headers_error_code;
2565
0
  if (code != ISC_HTTP_ERROR_SUCCESS) {
2566
0
    goto error;
2567
0
  }
2568
2569
0
  if (socket->h2->request_path == NULL || socket->h2->cb == NULL) {
2570
0
    code = ISC_HTTP_ERROR_NOT_FOUND;
2571
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
2572
0
       socket->h2->content_length == 0)
2573
0
  {
2574
0
    code = ISC_HTTP_ERROR_BAD_REQUEST;
2575
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
2576
0
       isc_buffer_usedlength(&socket->h2->rbuf) >
2577
0
         socket->h2->content_length)
2578
0
  {
2579
0
    code = ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
2580
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
2581
0
       isc_buffer_usedlength(&socket->h2->rbuf) !=
2582
0
         socket->h2->content_length)
2583
0
  {
2584
0
    code = ISC_HTTP_ERROR_BAD_REQUEST;
2585
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_POST &&
2586
0
       socket->h2->query_data != NULL)
2587
0
  {
2588
    /* The spec does not mention which value the query string for
2589
     * POST should have. For GET we use its value to decode a DNS
2590
     * message from it, for POST the message is transferred in the
2591
     * body of the request. Taking it into account, it is much safer
2592
     * to treat POST
2593
     * requests with query strings as malformed ones. */
2594
0
    code = ISC_HTTP_ERROR_BAD_REQUEST;
2595
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_GET &&
2596
0
       socket->h2->content_length > 0)
2597
0
  {
2598
0
    code = ISC_HTTP_ERROR_BAD_REQUEST;
2599
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_GET &&
2600
0
       socket->h2->query_data == NULL)
2601
0
  {
2602
    /* A GET request without any query data - there is nothing to
2603
     * decode. */
2604
0
    INSIST(socket->h2->query_data_len == 0);
2605
0
    code = ISC_HTTP_ERROR_BAD_REQUEST;
2606
0
  }
2607
2608
0
  if (code != ISC_HTTP_ERROR_SUCCESS) {
2609
0
    goto error;
2610
0
  }
2611
2612
0
  if (socket->h2->request_type == ISC_HTTP_REQ_GET) {
2613
0
    isc_buffer_t decoded_buf;
2614
0
    isc_buffer_init(&decoded_buf, tmp_buf, sizeof(tmp_buf));
2615
0
    if (isc_base64_decodestring(socket->h2->query_data,
2616
0
              &decoded_buf) != ISC_R_SUCCESS)
2617
0
    {
2618
0
      code = ISC_HTTP_ERROR_BAD_REQUEST;
2619
0
      goto error;
2620
0
    }
2621
0
    isc_buffer_usedregion(&decoded_buf, &data);
2622
0
  } else if (socket->h2->request_type == ISC_HTTP_REQ_POST) {
2623
0
    INSIST(socket->h2->content_length > 0);
2624
0
    isc_buffer_usedregion(&socket->h2->rbuf, &data);
2625
0
  } else {
2626
0
    UNREACHABLE();
2627
0
  }
2628
2629
0
  server_call_cb(socket, ISC_R_SUCCESS, &data);
2630
2631
0
  return 0;
2632
2633
0
error:
2634
0
  result = server_send_error_response(code, ngsession, socket);
2635
0
  if (result != ISC_R_SUCCESS) {
2636
0
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2637
0
  }
2638
0
  return 0;
2639
0
}
2640
2641
static void
2642
http_send_cb(void *arg);
2643
2644
void
2645
isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
2646
0
      isc_nm_cb_t cb, void *cbarg) {
2647
0
  isc_nmsocket_t *sock = NULL;
2648
0
  isc__nm_uvreq_t *uvreq = NULL;
2649
2650
0
  REQUIRE(VALID_NMHANDLE(handle));
2651
2652
0
  sock = handle->sock;
2653
2654
0
  REQUIRE(VALID_NMSOCK(sock));
2655
0
  REQUIRE(sock->tid == isc_tid());
2656
2657
0
  uvreq = isc__nm_uvreq_get(sock);
2658
0
  isc_nmhandle_attach(handle, &uvreq->handle);
2659
0
  uvreq->cb.send = cb;
2660
0
  uvreq->cbarg = cbarg;
2661
2662
0
  uvreq->uvbuf.base = (char *)region->base;
2663
0
  uvreq->uvbuf.len = region->length;
2664
2665
0
  isc_job_run(sock->worker->loop, &uvreq->job, http_send_cb, uvreq);
2666
0
}
2667
2668
static void
2669
failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
2670
0
         isc_result_t eresult) {
2671
0
  REQUIRE(VALID_NMSOCK(sock));
2672
0
  REQUIRE(VALID_UVREQ(req));
2673
2674
0
  if (req->cb.send != NULL) {
2675
0
    isc__nm_sendcb(sock, req, eresult, true);
2676
0
  } else {
2677
0
    isc__nm_uvreq_put(&req);
2678
0
  }
2679
0
}
2680
2681
static void
2682
client_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
2683
0
    isc__nm_uvreq_t *req) {
2684
0
  isc_result_t result = ISC_R_SUCCESS;
2685
0
  isc_nm_cb_t cb = req->cb.send;
2686
0
  void *cbarg = req->cbarg;
2687
2688
0
  result = client_send(
2689
0
    handle, &(isc_region_t){ .base = (uint8_t *)req->uvbuf.base,
2690
0
           .length = req->uvbuf.len });
2691
0
  if (result != ISC_R_SUCCESS) {
2692
0
    failed_send_cb(sock, req, result);
2693
0
    return;
2694
0
  }
2695
2696
0
  http_do_bio(sock->h2->session, handle, cb, cbarg);
2697
0
  isc__nm_uvreq_put(&req);
2698
0
}
2699
2700
static void
2701
server_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
2702
0
    isc__nm_uvreq_t *req) {
2703
0
  size_t content_len_buf_len, cache_control_buf_len;
2704
0
  isc_result_t result = ISC_R_SUCCESS;
2705
0
  isc_nm_cb_t cb = req->cb.send;
2706
0
  void *cbarg = req->cbarg;
2707
0
  if (isc__nmsocket_closing(sock) ||
2708
0
      !isc__nm_httpsession_active(handle->httpsession))
2709
0
  {
2710
0
    failed_send_cb(sock, req, ISC_R_CANCELED);
2711
0
    return;
2712
0
  }
2713
2714
0
  INSIST(handle->sock->tid == isc_tid());
2715
0
  INSIST(VALID_NMHANDLE(handle->httpsession->handle));
2716
0
  INSIST(VALID_NMSOCK(handle->httpsession->handle->sock));
2717
2718
0
  isc_buffer_init(&sock->h2->wbuf, req->uvbuf.base, req->uvbuf.len);
2719
0
  isc_buffer_add(&sock->h2->wbuf, req->uvbuf.len);
2720
2721
0
  content_len_buf_len = snprintf(sock->h2->clenbuf,
2722
0
               sizeof(sock->h2->clenbuf), "%lu",
2723
0
               (unsigned long)req->uvbuf.len);
2724
0
  if (sock->h2->min_ttl == 0) {
2725
0
    cache_control_buf_len =
2726
0
      snprintf(sock->h2->cache_control_buf,
2727
0
         sizeof(sock->h2->cache_control_buf), "%s",
2728
0
         DEFAULT_CACHE_CONTROL);
2729
0
  } else {
2730
0
    cache_control_buf_len =
2731
0
      snprintf(sock->h2->cache_control_buf,
2732
0
         sizeof(sock->h2->cache_control_buf),
2733
0
         "max-age=%" PRIu32, sock->h2->min_ttl);
2734
0
  }
2735
0
  const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "200"),
2736
0
            MAKE_NV2("Content-Type", DNS_MEDIA_TYPE),
2737
0
            MAKE_NV("Content-Length", sock->h2->clenbuf,
2738
0
              content_len_buf_len),
2739
0
            MAKE_NV("Cache-Control",
2740
0
              sock->h2->cache_control_buf,
2741
0
              cache_control_buf_len) };
2742
2743
0
  result = server_send_response(handle->httpsession->ngsession,
2744
0
              sock->h2->stream_id, hdrs,
2745
0
              sizeof(hdrs) / sizeof(nghttp2_nv), sock);
2746
2747
0
  if (result == ISC_R_SUCCESS) {
2748
0
    http_do_bio(handle->httpsession, handle, cb, cbarg);
2749
0
  } else {
2750
0
    cb(handle, result, cbarg);
2751
0
  }
2752
2753
0
  isc_buffer_initnull(&sock->h2->wbuf);
2754
0
  isc__nm_uvreq_put(&req);
2755
0
}
2756
2757
static void
2758
0
http_send_cb(void *arg) {
2759
0
  isc__nm_uvreq_t *req = arg;
2760
2761
0
  REQUIRE(VALID_UVREQ(req));
2762
2763
0
  isc_nmsocket_t *sock = req->sock;
2764
2765
0
  REQUIRE(VALID_NMSOCK(sock));
2766
0
  REQUIRE(VALID_HTTP2_SESSION(sock->h2->session));
2767
2768
0
  isc_nmhandle_t *handle = req->handle;
2769
2770
0
  REQUIRE(VALID_NMHANDLE(handle));
2771
2772
0
  isc_nm_http_session_t *session = sock->h2->session;
2773
0
  if (session != NULL && session->client) {
2774
0
    client_httpsend(handle, sock, req);
2775
0
  } else {
2776
0
    server_httpsend(handle, sock, req);
2777
0
  }
2778
0
}
2779
2780
void
2781
0
isc__nm_http_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
2782
0
  isc_result_t result;
2783
0
  http_cstream_t *cstream = NULL;
2784
0
  isc_nm_http_session_t *session = NULL;
2785
2786
0
  REQUIRE(VALID_NMHANDLE(handle));
2787
2788
0
  session = handle->sock->h2->session;
2789
0
  if (!isc__nm_httpsession_active(session)) {
2790
0
    cb(handle, ISC_R_CANCELED, NULL, cbarg);
2791
0
    return;
2792
0
  }
2793
2794
0
  result = get_http_cstream(handle->sock, &cstream);
2795
0
  if (result != ISC_R_SUCCESS) {
2796
0
    return;
2797
0
  }
2798
2799
0
  handle->sock->h2->connect.cstream = cstream;
2800
0
  cstream->read_cb = cb;
2801
0
  cstream->read_cbarg = cbarg;
2802
0
  cstream->reading = true;
2803
2804
0
  if (cstream->sending) {
2805
0
    result = client_submit_request(session, cstream);
2806
0
    if (result != ISC_R_SUCCESS) {
2807
0
      put_http_cstream(session->mctx, cstream);
2808
0
      return;
2809
0
    }
2810
2811
0
    http_do_bio(session, NULL, NULL, NULL);
2812
0
  }
2813
0
}
2814
2815
static int
2816
server_on_frame_recv_callback(nghttp2_session *ngsession,
2817
0
            const nghttp2_frame *frame, void *user_data) {
2818
0
  isc_nmsocket_t *socket = NULL;
2819
2820
0
  UNUSED(user_data);
2821
2822
0
  switch (frame->hd.type) {
2823
0
  case NGHTTP2_DATA:
2824
0
  case NGHTTP2_HEADERS:
2825
    /* Check that the client request has finished */
2826
0
    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2827
0
      socket = nghttp2_session_get_stream_user_data(
2828
0
        ngsession, frame->hd.stream_id);
2829
2830
      /*
2831
       * For DATA and HEADERS frame,
2832
       * this callback may be called
2833
       * after
2834
       * on_stream_close_callback.
2835
       * Check that the stream is
2836
       * still alive.
2837
       */
2838
0
      if (socket == NULL) {
2839
0
        return 0;
2840
0
      }
2841
2842
0
      return server_on_request_recv(ngsession, socket);
2843
0
    }
2844
0
    break;
2845
0
  default:
2846
0
    break;
2847
0
  }
2848
0
  return 0;
2849
0
}
2850
2851
static void
2852
0
initialize_nghttp2_server_session(isc_nm_http_session_t *session) {
2853
0
  nghttp2_session_callbacks *callbacks = NULL;
2854
0
  nghttp2_mem mem;
2855
2856
0
  init_nghttp2_mem(session->mctx, &mem);
2857
2858
0
  RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
2859
2860
0
  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
2861
0
    callbacks, on_data_chunk_recv_callback);
2862
2863
0
  nghttp2_session_callbacks_set_on_stream_close_callback(
2864
0
    callbacks, on_stream_close_callback);
2865
2866
0
  nghttp2_session_callbacks_set_on_header_callback(
2867
0
    callbacks, server_on_header_callback);
2868
2869
0
  nghttp2_session_callbacks_set_on_begin_headers_callback(
2870
0
    callbacks, server_on_begin_headers_callback);
2871
2872
0
  nghttp2_session_callbacks_set_on_frame_recv_callback(
2873
0
    callbacks, server_on_frame_recv_callback);
2874
2875
0
  RUNTIME_CHECK(nghttp2_session_server_new3(&session->ngsession,
2876
0
              callbacks, session, NULL,
2877
0
              &mem) == 0);
2878
2879
0
  nghttp2_session_callbacks_del(callbacks);
2880
0
}
2881
2882
static int
2883
0
server_send_connection_header(isc_nm_http_session_t *session) {
2884
0
  nghttp2_settings_entry iv[1] = {
2885
0
    { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
2886
0
      session->max_concurrent_streams }
2887
0
  };
2888
0
  int rv;
2889
2890
0
  rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
2891
0
             1);
2892
0
  if (rv != 0) {
2893
0
    return -1;
2894
0
  }
2895
0
  return 0;
2896
0
}
2897
2898
/*
2899
 * It is advisable to disable Nagle's algorithm for HTTP/2
2900
 * connections because multiple HTTP/2 streams could be multiplexed
2901
 * over one transport connection. Thus, delays when delivering small
2902
 * packets could bring down performance for the whole session.
2903
 * HTTP/2 is meant to be used this way.
2904
 */
2905
static void
2906
0
http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle) {
2907
0
  (void)isc_nmhandle_set_tcp_nodelay(transphandle, true);
2908
0
}
2909
2910
static isc_result_t
2911
0
httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
2912
0
  isc_nmsocket_t *httpserver = (isc_nmsocket_t *)cbarg;
2913
0
  isc_nm_http_session_t *session = NULL;
2914
2915
0
  REQUIRE(VALID_NMHANDLE(handle));
2916
0
  REQUIRE(VALID_NMSOCK(handle->sock));
2917
2918
0
  if (isc__nm_closing(handle->sock->worker)) {
2919
0
    return ISC_R_SHUTTINGDOWN;
2920
0
  } else if (result != ISC_R_SUCCESS) {
2921
0
    return result;
2922
0
  }
2923
2924
0
  REQUIRE(VALID_NMSOCK(httpserver));
2925
0
  REQUIRE(httpserver->type == isc_nm_httplistener);
2926
2927
0
  http_initsocket(handle->sock);
2928
2929
0
  http_transpost_tcp_nodelay(handle);
2930
2931
0
  new_session(handle->sock->worker->mctx, NULL, &session);
2932
0
  session->max_concurrent_streams =
2933
0
    atomic_load_relaxed(&httpserver->h2->max_concurrent_streams);
2934
0
  initialize_nghttp2_server_session(session);
2935
0
  handle->sock->h2->session = session;
2936
2937
0
  isc_nmhandle_attach(handle, &session->handle);
2938
0
  isc__nmsocket_attach(httpserver, &session->serversocket);
2939
0
  server_send_connection_header(session);
2940
2941
0
  isc__nmhandle_set_manual_timer(session->handle, true);
2942
2943
  /* TODO H2 */
2944
0
  http_do_bio(session, NULL, NULL, NULL);
2945
0
  return ISC_R_SUCCESS;
2946
0
}
2947
2948
isc_result_t
2949
isc_nm_listenhttp(uint32_t workers, isc_sockaddr_t *iface, int backlog,
2950
      isc_quota_t *quota, isc_tlsctx_t *ctx,
2951
      isc_nm_http_endpoints_t *eps, uint32_t max_concurrent_streams,
2952
0
      isc_nm_proxy_type_t proxy_type, isc_nmsocket_t **sockp) {
2953
0
  isc_nmsocket_t *sock = NULL;
2954
0
  isc_result_t result = ISC_R_FAILURE;
2955
0
  isc__networker_t *worker = isc__networker_current();
2956
2957
0
  REQUIRE(!ISC_LIST_EMPTY(eps->handlers));
2958
0
  REQUIRE(atomic_load(&eps->in_use) == false);
2959
0
  REQUIRE(isc_tid() == 0);
2960
2961
0
  sock = isc_mempool_get(worker->nmsocket_pool);
2962
0
  isc__nmsocket_init(sock, worker, isc_nm_httplistener, iface, NULL);
2963
0
  http_initsocket(sock);
2964
0
  atomic_init(&sock->h2->max_concurrent_streams,
2965
0
        NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS);
2966
2967
0
  isc_nmsocket_set_max_streams(sock, max_concurrent_streams);
2968
2969
0
  atomic_store(&eps->in_use, true);
2970
0
  http_init_listener_endpoints(sock, eps);
2971
2972
0
  switch (proxy_type) {
2973
0
  case ISC_NM_PROXY_NONE:
2974
0
    if (ctx != NULL) {
2975
0
      result = isc_nm_listentls(
2976
0
        workers, iface, httplisten_acceptcb, sock,
2977
0
        backlog, quota, ctx, false, &sock->outer);
2978
0
    } else {
2979
0
      result = isc_nm_listentcp(workers, iface,
2980
0
              httplisten_acceptcb, sock,
2981
0
              backlog, quota, &sock->outer);
2982
0
    }
2983
0
    break;
2984
0
  case ISC_NM_PROXY_PLAIN:
2985
0
    if (ctx != NULL) {
2986
0
      result = isc_nm_listentls(
2987
0
        workers, iface, httplisten_acceptcb, sock,
2988
0
        backlog, quota, ctx, true, &sock->outer);
2989
0
    } else {
2990
0
      result = isc_nm_listenproxystream(
2991
0
        workers, iface, httplisten_acceptcb, sock,
2992
0
        backlog, quota, NULL, &sock->outer);
2993
0
    }
2994
0
    break;
2995
0
  case ISC_NM_PROXY_ENCRYPTED:
2996
0
    INSIST(ctx != NULL);
2997
0
    result = isc_nm_listenproxystream(
2998
0
      workers, iface, httplisten_acceptcb, sock, backlog,
2999
0
      quota, ctx, &sock->outer);
3000
0
    break;
3001
0
  default:
3002
0
    UNREACHABLE();
3003
0
  }
3004
3005
0
  if (result != ISC_R_SUCCESS) {
3006
0
    sock->closed = true;
3007
0
    isc__nmsocket_detach(&sock);
3008
0
    return result;
3009
0
  }
3010
3011
0
  sock->nchildren = sock->outer->nchildren;
3012
0
  sock->fd = (uv_os_sock_t)-1;
3013
3014
0
  *sockp = sock;
3015
0
  return ISC_R_SUCCESS;
3016
0
}
3017
3018
isc_nm_http_endpoints_t *
3019
0
isc_nm_http_endpoints_new(isc_mem_t *mctx) {
3020
0
  isc_nm_http_endpoints_t *restrict eps;
3021
0
  REQUIRE(mctx != NULL);
3022
3023
0
  eps = isc_mem_get(mctx, sizeof(*eps));
3024
0
  *eps = (isc_nm_http_endpoints_t){ .mctx = NULL };
3025
3026
0
  isc_mem_attach(mctx, &eps->mctx);
3027
0
  ISC_LIST_INIT(eps->handlers);
3028
0
  isc_refcount_init(&eps->references, 1);
3029
0
  atomic_init(&eps->in_use, false);
3030
0
  eps->magic = HTTP_ENDPOINTS_MAGIC;
3031
3032
0
  return eps;
3033
0
}
3034
3035
void
3036
0
isc_nm_http_endpoints_detach(isc_nm_http_endpoints_t **restrict epsp) {
3037
0
  isc_nm_http_endpoints_t *restrict eps;
3038
0
  isc_mem_t *mctx;
3039
3040
0
  REQUIRE(epsp != NULL);
3041
0
  eps = *epsp;
3042
0
  REQUIRE(VALID_HTTP_ENDPOINTS(eps));
3043
3044
0
  if (isc_refcount_decrement(&eps->references) > 1) {
3045
0
    *epsp = NULL;
3046
0
    return;
3047
0
  }
3048
3049
0
  mctx = eps->mctx;
3050
3051
  /* Delete all handlers */
3052
0
  ISC_LIST_FOREACH(eps->handlers, handler, link) {
3053
0
    ISC_LIST_DEQUEUE(eps->handlers, handler, link);
3054
0
    isc_mem_free(mctx, handler->path);
3055
0
    handler->magic = 0;
3056
0
    isc_mem_put(mctx, handler, sizeof(*handler));
3057
0
  }
3058
3059
0
  eps->magic = 0;
3060
3061
0
  isc_mem_putanddetach(&mctx, eps, sizeof(*eps));
3062
0
  *epsp = NULL;
3063
0
}
3064
3065
void
3066
isc_nm_http_endpoints_attach(isc_nm_http_endpoints_t *source,
3067
0
           isc_nm_http_endpoints_t **targetp) {
3068
0
  REQUIRE(VALID_HTTP_ENDPOINTS(source));
3069
0
  REQUIRE(targetp != NULL && *targetp == NULL);
3070
3071
0
  isc_refcount_increment(&source->references);
3072
3073
0
  *targetp = source;
3074
0
}
3075
3076
static isc_nm_httphandler_t *
3077
http_endpoints_find(const char *request_path,
3078
0
        isc_nm_http_endpoints_t *restrict eps) {
3079
0
  REQUIRE(VALID_HTTP_ENDPOINTS(eps));
3080
3081
0
  if (request_path == NULL || *request_path == '\0') {
3082
0
    return NULL;
3083
0
  }
3084
3085
0
  ISC_LIST_FOREACH(eps->handlers, handler, link) {
3086
0
    if (!strcmp(request_path, handler->path)) {
3087
0
      INSIST(VALID_HTTP_HANDLER(handler));
3088
0
      INSIST(handler->cb != NULL);
3089
0
      return handler;
3090
0
    }
3091
0
  }
3092
3093
0
  return NULL;
3094
0
}
3095
3096
isc_result_t
3097
isc_nm_http_endpoints_add(isc_nm_http_endpoints_t *restrict eps,
3098
        const char *uri, const isc_nm_recv_cb_t cb,
3099
0
        void *cbarg) {
3100
0
  isc_mem_t *mctx;
3101
0
  isc_nm_httphandler_t *restrict handler = NULL;
3102
3103
0
  REQUIRE(VALID_HTTP_ENDPOINTS(eps));
3104
0
  REQUIRE(isc_nm_http_path_isvalid(uri));
3105
0
  REQUIRE(cb != NULL);
3106
0
  REQUIRE(atomic_load(&eps->in_use) == false);
3107
3108
0
  mctx = eps->mctx;
3109
3110
0
  if (http_endpoints_find(uri, eps) == NULL) {
3111
0
    handler = isc_mem_get(mctx, sizeof(*handler));
3112
0
    *handler = (isc_nm_httphandler_t){
3113
0
      .cb = cb,
3114
0
      .cbarg = cbarg,
3115
0
      .path = isc_mem_strdup(mctx, uri),
3116
0
      .link = ISC_LINK_INITIALIZER,
3117
0
      .magic = HTTP_HANDLER_MAGIC
3118
0
    };
3119
3120
0
    ISC_LIST_APPEND(eps->handlers, handler, link);
3121
0
  }
3122
3123
0
  return ISC_R_SUCCESS;
3124
0
}
3125
3126
void
3127
0
isc__nm_http_stoplistening(isc_nmsocket_t *sock) {
3128
0
  REQUIRE(VALID_NMSOCK(sock));
3129
0
  REQUIRE(sock->type == isc_nm_httplistener);
3130
0
  REQUIRE(isc_tid() == sock->tid);
3131
3132
0
  isc__nmsocket_stop(sock);
3133
0
}
3134
3135
static void
3136
0
http_close_direct(isc_nmsocket_t *sock) {
3137
0
  isc_nm_http_session_t *session = NULL;
3138
3139
0
  REQUIRE(VALID_NMSOCK(sock));
3140
3141
0
  sock->closed = true;
3142
0
  sock->active = false;
3143
0
  session = sock->h2->session;
3144
3145
0
  if (session != NULL && session->sending == 0 && !session->reading) {
3146
    /*
3147
     * The socket is going to be closed too early without been
3148
     * used even once (might happen in a case of low level
3149
     * error).
3150
     */
3151
0
    finish_http_session(session);
3152
0
  } else if (session != NULL && session->handle) {
3153
0
    http_do_bio(session, NULL, NULL, NULL);
3154
0
  }
3155
0
}
3156
3157
static void
3158
0
http_close_cb(void *arg) {
3159
0
  isc_nmsocket_t *sock = arg;
3160
0
  REQUIRE(VALID_NMSOCK(sock));
3161
3162
0
  http_close_direct(sock);
3163
0
  isc__nmsocket_detach(&sock);
3164
0
}
3165
3166
void
3167
0
isc__nm_http_close(isc_nmsocket_t *sock) {
3168
0
  bool destroy = false;
3169
0
  REQUIRE(VALID_NMSOCK(sock));
3170
0
  REQUIRE(sock->type == isc_nm_httpsocket);
3171
0
  REQUIRE(!isc__nmsocket_active(sock));
3172
0
  REQUIRE(!sock->closing);
3173
3174
0
  sock->closing = true;
3175
3176
0
  if (sock->h2->session != NULL && sock->h2->session->closed &&
3177
0
      sock->tid == isc_tid())
3178
0
  {
3179
0
    isc__nm_httpsession_detach(&sock->h2->session);
3180
0
    destroy = true;
3181
0
  } else if (sock->h2->session == NULL && sock->tid == isc_tid()) {
3182
0
    destroy = true;
3183
0
  }
3184
3185
0
  if (destroy) {
3186
0
    http_close_direct(sock);
3187
0
    isc__nmsocket_prep_destroy(sock);
3188
0
    return;
3189
0
  }
3190
3191
0
  isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
3192
0
  isc_async_run(sock->worker->loop, http_close_cb, sock);
3193
0
}
3194
3195
static void
3196
failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
3197
0
        isc_nm_http_session_t *session) {
3198
0
  isc_region_t data;
3199
0
  REQUIRE(VALID_NMSOCK(sock));
3200
0
  INSIST(sock->type == isc_nm_httpsocket);
3201
3202
0
  if (sock->h2->request_path == NULL) {
3203
0
    return;
3204
0
  }
3205
3206
0
  (void)nghttp2_submit_rst_stream(
3207
0
    session->ngsession, NGHTTP2_FLAG_END_STREAM,
3208
0
    sock->h2->stream_id, NGHTTP2_REFUSED_STREAM);
3209
0
  isc_buffer_usedregion(&sock->h2->rbuf, &data);
3210
0
  server_call_cb(sock, result, &data);
3211
0
}
3212
3213
static void
3214
client_call_failed_read_cb(isc_result_t result,
3215
0
         isc_nm_http_session_t *session) {
3216
0
  REQUIRE(VALID_HTTP2_SESSION(session));
3217
0
  REQUIRE(result != ISC_R_SUCCESS);
3218
3219
0
  ISC_LIST_FOREACH(session->cstreams, cstream, link) {
3220
    /*
3221
     * read_cb could be NULL if cstream was allocated and added
3222
     * to the tracking list, but was not properly initialized due
3223
     * to a low-level error. It is safe to get rid of the object
3224
     * in such a case.
3225
     */
3226
0
    if (cstream->read_cb != NULL) {
3227
0
      isc_region_t read_data;
3228
0
      isc_buffer_usedregion(cstream->rbuf, &read_data);
3229
0
      cstream->read_cb(session->client_httphandle, result,
3230
0
           &read_data, cstream->read_cbarg);
3231
0
    }
3232
3233
0
    if (result != ISC_R_TIMEDOUT || cstream->read_cb == NULL ||
3234
0
        !(session->handle != NULL &&
3235
0
          isc__nmsocket_timer_running(session->handle->sock)))
3236
0
    {
3237
0
      ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
3238
0
      put_http_cstream(session->mctx, cstream);
3239
0
    }
3240
0
  }
3241
0
}
3242
3243
static void
3244
server_call_failed_read_cb(isc_result_t result,
3245
0
         isc_nm_http_session_t *session) {
3246
0
  REQUIRE(VALID_HTTP2_SESSION(session));
3247
0
  REQUIRE(result != ISC_R_SUCCESS);
3248
3249
0
  ISC_LIST_FOREACH(session->sstreams, h2data, link) {
3250
0
    failed_httpstream_read_cb(h2data->psock, result, session);
3251
0
  }
3252
3253
0
  ISC_LIST_FOREACH(session->sstreams, h2data, link) {
3254
0
    ISC_LIST_DEQUEUE(session->sstreams, h2data, link);
3255
3256
    /* Cleanup socket in place */
3257
0
    h2data->psock->active = false;
3258
0
    h2data->psock->closed = true;
3259
0
    isc__nmsocket_detach(&h2data->psock);
3260
0
  }
3261
0
}
3262
3263
static void
3264
0
failed_read_cb(isc_result_t result, isc_nm_http_session_t *session) {
3265
0
  if (session->client) {
3266
0
    client_call_failed_read_cb(result, session);
3267
    /*
3268
     * If result was ISC_R_TIMEDOUT and the timer was reset,
3269
     * then we still have active streams and should not close
3270
     * the session.
3271
     */
3272
0
    if (ISC_LIST_EMPTY(session->cstreams)) {
3273
0
      finish_http_session(session);
3274
0
    }
3275
0
  } else {
3276
0
    server_call_failed_read_cb(result, session);
3277
    /*
3278
     * All streams are now destroyed; close the session.
3279
     */
3280
0
    finish_http_session(session);
3281
0
  }
3282
0
}
3283
3284
void
3285
0
isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
3286
0
  isc_nm_http_session_t *session;
3287
0
  isc_nmsocket_t *sock;
3288
3289
0
  REQUIRE(VALID_NMHANDLE(handle));
3290
0
  REQUIRE(VALID_NMSOCK(handle->sock));
3291
3292
0
  sock = handle->sock;
3293
0
  session = sock->h2->session;
3294
3295
0
  INSIST(VALID_HTTP2_SESSION(session));
3296
0
  INSIST(!session->client);
3297
3298
0
  sock->h2->min_ttl = ttl;
3299
0
}
3300
3301
bool
3302
0
isc__nm_http_has_encryption(const isc_nmhandle_t *handle) {
3303
0
  isc_nm_http_session_t *session;
3304
0
  isc_nmsocket_t *sock;
3305
3306
0
  REQUIRE(VALID_NMHANDLE(handle));
3307
0
  REQUIRE(VALID_NMSOCK(handle->sock));
3308
3309
0
  sock = handle->sock;
3310
0
  session = sock->h2->session;
3311
3312
0
  INSIST(VALID_HTTP2_SESSION(session));
3313
3314
0
  if (session->handle == NULL) {
3315
0
    return false;
3316
0
  }
3317
3318
0
  return isc_nm_has_encryption(session->handle);
3319
0
}
3320
3321
const char *
3322
0
isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
3323
0
  isc_nmsocket_t *sock = NULL;
3324
0
  isc_nm_http_session_t *session;
3325
3326
0
  REQUIRE(VALID_NMHANDLE(handle));
3327
0
  REQUIRE(VALID_NMSOCK(handle->sock));
3328
0
  REQUIRE(handle->sock->type == isc_nm_httpsocket);
3329
3330
0
  sock = handle->sock;
3331
0
  session = sock->h2->session;
3332
3333
  /*
3334
   * In the case of a low-level error the session->handle is not
3335
   * attached nor session object is created.
3336
   */
3337
0
  if (session == NULL && sock->h2->connect.tls_peer_verify_string != NULL)
3338
0
  {
3339
0
    return sock->h2->connect.tls_peer_verify_string;
3340
0
  }
3341
3342
0
  if (session == NULL) {
3343
0
    return NULL;
3344
0
  }
3345
3346
0
  INSIST(VALID_HTTP2_SESSION(session));
3347
3348
0
  if (session->handle == NULL) {
3349
0
    return NULL;
3350
0
  }
3351
3352
0
  return isc_nm_verify_tls_peer_result_string(session->handle);
3353
0
}
3354
3355
void
3356
0
isc__nm_http_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
3357
0
  REQUIRE(VALID_NMSOCK(listener));
3358
0
  REQUIRE(listener->type == isc_nm_httplistener);
3359
3360
0
  isc_nmsocket_set_tlsctx(listener->outer, tlsctx);
3361
0
}
3362
3363
void
3364
isc__nm_http_set_max_streams(isc_nmsocket_t *listener,
3365
0
           const uint32_t max_concurrent_streams) {
3366
0
  uint32_t max_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
3367
3368
0
  REQUIRE(VALID_NMSOCK(listener));
3369
0
  REQUIRE(listener->type == isc_nm_httplistener);
3370
3371
0
  if (max_concurrent_streams > 0 &&
3372
0
      max_concurrent_streams < NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS)
3373
0
  {
3374
0
    max_streams = max_concurrent_streams;
3375
0
  }
3376
3377
0
  atomic_store_relaxed(&listener->h2->max_concurrent_streams,
3378
0
           max_streams);
3379
0
}
3380
3381
typedef struct http_endpoints_data {
3382
  isc_nmsocket_t *listener;
3383
  isc_nm_http_endpoints_t *endpoints;
3384
} http_endpoints_data_t;
3385
3386
static void
3387
0
http_set_endpoints_cb(void *arg) {
3388
0
  http_endpoints_data_t *data = arg;
3389
0
  const isc_tid_t tid = isc_tid();
3390
0
  isc_nmsocket_t *listener = data->listener;
3391
0
  isc_nm_http_endpoints_t *endpoints = data->endpoints;
3392
0
  isc__networker_t *worker = isc__networker_current();
3393
3394
0
  isc_mem_put(worker->loop->mctx, data, sizeof(*data));
3395
3396
0
  isc_nm_http_endpoints_detach(&listener->h2->listener_endpoints[tid]);
3397
0
  isc_nm_http_endpoints_attach(endpoints,
3398
0
             &listener->h2->listener_endpoints[tid]);
3399
3400
0
  isc_nm_http_endpoints_detach(&endpoints);
3401
0
  isc__nmsocket_detach(&listener);
3402
0
}
3403
3404
void
3405
isc_nm_http_set_endpoints(isc_nmsocket_t *listener,
3406
0
        isc_nm_http_endpoints_t *eps) {
3407
0
  REQUIRE(VALID_NMSOCK(listener));
3408
0
  REQUIRE(listener->type == isc_nm_httplistener);
3409
0
  REQUIRE(VALID_HTTP_ENDPOINTS(eps));
3410
3411
0
  atomic_store(&eps->in_use, true);
3412
3413
0
  for (size_t i = 0; i < isc_loopmgr_nloops(); i++) {
3414
0
    isc__networker_t *worker = isc__networker_get(i);
3415
0
    http_endpoints_data_t *data = isc_mem_cget(worker->loop->mctx,
3416
0
                 1, sizeof(*data));
3417
3418
0
    isc__nmsocket_attach(listener, &data->listener);
3419
0
    isc_nm_http_endpoints_attach(eps, &data->endpoints);
3420
3421
0
    isc_async_run(worker->loop, http_set_endpoints_cb, data);
3422
0
  }
3423
0
}
3424
3425
static void
3426
http_init_listener_endpoints(isc_nmsocket_t *listener,
3427
0
           isc_nm_http_endpoints_t *epset) {
3428
0
  size_t nworkers;
3429
3430
0
  REQUIRE(VALID_NMSOCK(listener));
3431
0
  REQUIRE(listener->worker != NULL);
3432
0
  REQUIRE(VALID_HTTP_ENDPOINTS(epset));
3433
3434
0
  nworkers = (size_t)isc_loopmgr_nloops();
3435
0
  INSIST(nworkers > 0);
3436
3437
0
  listener->h2->listener_endpoints =
3438
0
    isc_mem_cget(listener->worker->mctx, nworkers,
3439
0
           sizeof(isc_nm_http_endpoints_t *));
3440
0
  listener->h2->n_listener_endpoints = nworkers;
3441
0
  for (size_t i = 0; i < nworkers; i++) {
3442
0
    listener->h2->listener_endpoints[i] = NULL;
3443
0
    isc_nm_http_endpoints_attach(
3444
0
      epset, &listener->h2->listener_endpoints[i]);
3445
0
  }
3446
0
}
3447
3448
static void
3449
0
http_cleanup_listener_endpoints(isc_nmsocket_t *listener) {
3450
0
  REQUIRE(listener->worker != NULL);
3451
3452
0
  if (listener->h2->listener_endpoints == NULL) {
3453
0
    return;
3454
0
  }
3455
3456
0
  for (size_t i = 0; i < listener->h2->n_listener_endpoints; i++) {
3457
0
    isc_nm_http_endpoints_detach(
3458
0
      &listener->h2->listener_endpoints[i]);
3459
0
  }
3460
0
  isc_mem_cput(listener->worker->mctx, listener->h2->listener_endpoints,
3461
0
         listener->h2->n_listener_endpoints,
3462
0
         sizeof(isc_nm_http_endpoints_t *));
3463
0
  listener->h2->n_listener_endpoints = 0;
3464
0
}
3465
3466
static isc_nm_http_endpoints_t *
3467
0
http_get_listener_endpoints(isc_nmsocket_t *listener, const isc_tid_t tid) {
3468
0
  isc_nm_http_endpoints_t *eps;
3469
0
  REQUIRE(VALID_NMSOCK(listener));
3470
0
  REQUIRE(tid >= 0);
3471
0
  REQUIRE((size_t)tid < listener->h2->n_listener_endpoints);
3472
3473
0
  eps = listener->h2->listener_endpoints[tid];
3474
0
  INSIST(eps != NULL);
3475
0
  return eps;
3476
0
}
3477
3478
static const bool base64url_validation_table[256] = {
3479
  false, false, false, false, false, false, false, false, false, false,
3480
  false, false, false, false, false, false, false, false, false, false,
3481
  false, false, false, false, false, false, false, false, false, false,
3482
  false, false, false, false, false, false, false, false, false, false,
3483
  false, false, false, false, false, true,  false, false, true,  true,
3484
  true,  true,  true,  true,  true,  true,  true,  true,  false, false,
3485
  false, false, false, false, false, true,  true,  true,  true,  true,
3486
  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
3487
  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
3488
  true,  false, false, false, false, true,  false, true,  true,  true,
3489
  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
3490
  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
3491
  true,  true,  true,  false, false, false, false, false, false, false,
3492
  false, false, false, false, false, false, false, false, false, false,
3493
  false, false, false, false, false, false, false, false, false, false,
3494
  false, false, false, false, false, false, false, false, false, false,
3495
  false, false, false, false, false, false, false, false, false, false,
3496
  false, false, false, false, false, false, false, false, false, false,
3497
  false, false, false, false, false, false, false, false, false, false,
3498
  false, false, false, false, false, false, false, false, false, false,
3499
  false, false, false, false, false, false, false, false, false, false,
3500
  false, false, false, false, false, false, false, false, false, false,
3501
  false, false, false, false, false, false, false, false, false, false,
3502
  false, false, false, false, false, false, false, false, false, false,
3503
  false, false, false, false, false, false, false, false, false, false,
3504
  false, false, false, false, false, false
3505
};
3506
3507
char *
3508
isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
3509
0
          const size_t base64url_len, size_t *res_len) {
3510
0
  char *res = NULL;
3511
0
  size_t i, k, len;
3512
3513
0
  if (mem == NULL || base64url == NULL || base64url_len == 0) {
3514
0
    return NULL;
3515
0
  }
3516
3517
0
  len = base64url_len % 4 ? base64url_len + (4 - base64url_len % 4)
3518
0
        : base64url_len;
3519
0
  res = isc_mem_allocate(mem, len + 1); /* '\0' */
3520
3521
0
  for (i = 0; i < base64url_len; i++) {
3522
0
    switch (base64url[i]) {
3523
0
    case '-':
3524
0
      res[i] = '+';
3525
0
      break;
3526
0
    case '_':
3527
0
      res[i] = '/';
3528
0
      break;
3529
0
    default:
3530
0
      if (base64url_validation_table[(size_t)base64url[i]]) {
3531
0
        res[i] = base64url[i];
3532
0
      } else {
3533
0
        isc_mem_free(mem, res);
3534
0
        return NULL;
3535
0
      }
3536
0
      break;
3537
0
    }
3538
0
  }
3539
3540
0
  if (base64url_len % 4 != 0) {
3541
0
    for (k = 0; k < (4 - base64url_len % 4); k++, i++) {
3542
0
      res[i] = '=';
3543
0
    }
3544
0
  }
3545
3546
0
  INSIST(i == len);
3547
3548
0
  SET_IF_NOT_NULL(res_len, len);
3549
3550
0
  res[len] = '\0';
3551
3552
0
  return res;
3553
0
}
3554
3555
char *
3556
isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
3557
0
          const size_t base64_len, size_t *res_len) {
3558
0
  char *res = NULL;
3559
0
  size_t i;
3560
3561
0
  if (mem == NULL || base64 == NULL || base64_len == 0) {
3562
0
    return NULL;
3563
0
  }
3564
3565
0
  res = isc_mem_allocate(mem, base64_len + 1); /* '\0' */
3566
3567
0
  for (i = 0; i < base64_len; i++) {
3568
0
    switch (base64[i]) {
3569
0
    case '+':
3570
0
      res[i] = '-';
3571
0
      break;
3572
0
    case '/':
3573
0
      res[i] = '_';
3574
0
      break;
3575
0
    case '=':
3576
0
      goto end;
3577
0
      break;
3578
0
    default:
3579
      /*
3580
       * All other characters from
3581
       * the alphabet are the same
3582
       * for both base64 and
3583
       * base64url, so we can reuse
3584
       * the validation table for
3585
       * the rest of the characters.
3586
       */
3587
0
      if (base64[i] != '-' && base64[i] != '_' &&
3588
0
          base64url_validation_table[(size_t)base64[i]])
3589
0
      {
3590
0
        res[i] = base64[i];
3591
0
      } else {
3592
0
        isc_mem_free(mem, res);
3593
0
        return NULL;
3594
0
      }
3595
0
      break;
3596
0
    }
3597
0
  }
3598
0
end:
3599
0
  SET_IF_NOT_NULL(res_len, i);
3600
3601
0
  res[i] = '\0';
3602
3603
0
  return res;
3604
0
}
3605
3606
static void
3607
0
http_initsocket(isc_nmsocket_t *sock) {
3608
0
  REQUIRE(sock != NULL);
3609
3610
0
  sock->h2 = isc_mem_get(sock->worker->mctx, sizeof(*sock->h2));
3611
0
  *sock->h2 = (isc_nmsocket_h2_t){
3612
0
    .request_type = ISC_HTTP_REQ_UNSUPPORTED,
3613
0
    .request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED,
3614
0
  };
3615
0
}
3616
3617
void
3618
0
isc__nm_http_cleanup_data(isc_nmsocket_t *sock) {
3619
0
  switch (sock->type) {
3620
0
  case isc_nm_httplistener:
3621
0
  case isc_nm_httpsocket:
3622
0
    if (sock->type == isc_nm_httplistener &&
3623
0
        sock->h2->listener_endpoints != NULL)
3624
0
    {
3625
      /* Delete all handlers */
3626
0
      http_cleanup_listener_endpoints(sock);
3627
0
    }
3628
3629
0
    if (sock->type == isc_nm_httpsocket &&
3630
0
        sock->h2->peer_endpoints != NULL)
3631
0
    {
3632
0
      isc_nm_http_endpoints_detach(&sock->h2->peer_endpoints);
3633
0
    }
3634
3635
0
    if (sock->h2->request_path != NULL) {
3636
0
      isc_mem_free(sock->worker->mctx,
3637
0
             sock->h2->request_path);
3638
0
    }
3639
3640
0
    if (sock->h2->query_data != NULL) {
3641
0
      isc_mem_free(sock->worker->mctx, sock->h2->query_data);
3642
0
    }
3643
3644
0
    INSIST(sock->h2->connect.cstream == NULL);
3645
3646
0
    if (isc_buffer_base(&sock->h2->rbuf) != NULL) {
3647
0
      void *base = isc_buffer_base(&sock->h2->rbuf);
3648
0
      isc_mem_free(sock->worker->mctx, base);
3649
0
      isc_buffer_initnull(&sock->h2->rbuf);
3650
0
    }
3651
0
    FALLTHROUGH;
3652
0
  case isc_nm_proxystreamlistener:
3653
0
  case isc_nm_proxystreamsocket:
3654
0
  case isc_nm_tcpsocket:
3655
0
  case isc_nm_tlssocket:
3656
0
    if (sock->h2 != NULL) {
3657
0
      if (sock->h2->session != NULL) {
3658
0
        if (sock->h2->connect.uri != NULL) {
3659
0
          isc_mem_free(sock->worker->mctx,
3660
0
                 sock->h2->connect.uri);
3661
0
        }
3662
0
        isc__nm_httpsession_detach(&sock->h2->session);
3663
0
      }
3664
3665
0
      isc_mem_put(sock->worker->mctx, sock->h2,
3666
0
            sizeof(*sock->h2));
3667
0
    };
3668
0
    break;
3669
0
  default:
3670
0
    break;
3671
0
  }
3672
0
}
3673
3674
void
3675
0
isc__nm_http_cleartimeout(isc_nmhandle_t *handle) {
3676
0
  isc_nmsocket_t *sock = NULL;
3677
3678
0
  REQUIRE(VALID_NMHANDLE(handle));
3679
0
  REQUIRE(VALID_NMSOCK(handle->sock));
3680
0
  REQUIRE(handle->sock->type == isc_nm_httpsocket);
3681
3682
0
  sock = handle->sock;
3683
0
  if (sock->h2->session != NULL && sock->h2->session->handle != NULL) {
3684
0
    INSIST(VALID_HTTP2_SESSION(sock->h2->session));
3685
0
    INSIST(VALID_NMHANDLE(sock->h2->session->handle));
3686
0
    isc_nmhandle_cleartimeout(sock->h2->session->handle);
3687
0
  }
3688
0
}
3689
3690
void
3691
0
isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
3692
0
  isc_nmsocket_t *sock = NULL;
3693
3694
0
  REQUIRE(VALID_NMHANDLE(handle));
3695
0
  REQUIRE(VALID_NMSOCK(handle->sock));
3696
0
  REQUIRE(handle->sock->type == isc_nm_httpsocket);
3697
3698
0
  sock = handle->sock;
3699
0
  if (sock->h2->session != NULL && sock->h2->session->handle != NULL) {
3700
0
    INSIST(VALID_HTTP2_SESSION(sock->h2->session));
3701
0
    INSIST(VALID_NMHANDLE(sock->h2->session->handle));
3702
0
    isc_nmhandle_settimeout(sock->h2->session->handle, timeout);
3703
0
  }
3704
0
}
3705
3706
void
3707
0
isc__nmhandle_http_keepalive(isc_nmhandle_t *handle, bool value) {
3708
0
  isc_nmsocket_t *sock = NULL;
3709
3710
0
  REQUIRE(VALID_NMHANDLE(handle));
3711
0
  REQUIRE(VALID_NMSOCK(handle->sock));
3712
0
  REQUIRE(handle->sock->type == isc_nm_httpsocket);
3713
3714
0
  sock = handle->sock;
3715
0
  if (sock->h2->session != NULL && sock->h2->session->handle) {
3716
0
    INSIST(VALID_HTTP2_SESSION(sock->h2->session));
3717
0
    INSIST(VALID_NMHANDLE(sock->h2->session->handle));
3718
3719
0
    isc_nmhandle_keepalive(sock->h2->session->handle, value);
3720
0
  }
3721
0
}
3722
3723
void
3724
isc_nm_http_makeuri(const bool https, const isc_sockaddr_t *sa,
3725
        const char *hostname, const uint16_t http_port,
3726
        const char *abs_path, char *outbuf,
3727
0
        const size_t outbuf_len) {
3728
0
  char saddr[INET6_ADDRSTRLEN] = { 0 };
3729
0
  int family;
3730
0
  bool ipv6_addr = false;
3731
0
  struct sockaddr_in6 sa6;
3732
0
  uint16_t host_port = http_port;
3733
0
  const char *host = NULL;
3734
3735
0
  REQUIRE(outbuf != NULL);
3736
0
  REQUIRE(outbuf_len != 0);
3737
0
  REQUIRE(isc_nm_http_path_isvalid(abs_path));
3738
3739
  /* If hostname is specified, use that. */
3740
0
  if (hostname != NULL && hostname[0] != '\0') {
3741
    /*
3742
     * The host name could be an IPv6 address. If so,
3743
     * wrap it between [ and ].
3744
     */
3745
0
    if (inet_pton(AF_INET6, hostname, &sa6) == 1 &&
3746
0
        hostname[0] != '[')
3747
0
    {
3748
0
      ipv6_addr = true;
3749
0
    }
3750
0
    host = hostname;
3751
0
  } else {
3752
    /*
3753
     * A hostname was not specified; build one from
3754
     * the given IP address.
3755
     */
3756
0
    INSIST(sa != NULL);
3757
0
    family = ((const struct sockaddr *)&sa->type.sa)->sa_family;
3758
0
    host_port = ntohs(family == AF_INET ? sa->type.sin.sin_port
3759
0
                : sa->type.sin6.sin6_port);
3760
0
    ipv6_addr = family == AF_INET6;
3761
0
    (void)inet_ntop(
3762
0
      family,
3763
0
      family == AF_INET
3764
0
        ? (const struct sockaddr *)&sa->type.sin.sin_addr
3765
0
        : (const struct sockaddr *)&sa->type.sin6
3766
0
            .sin6_addr,
3767
0
      saddr, sizeof(saddr));
3768
0
    host = saddr;
3769
0
  }
3770
3771
  /*
3772
   * If the port number was not specified, the default
3773
   * depends on whether we're using encryption or not.
3774
   */
3775
0
  if (host_port == 0) {
3776
0
    host_port = https ? 443 : 80;
3777
0
  }
3778
3779
0
  (void)snprintf(outbuf, outbuf_len, "%s://%s%s%s:%u%s",
3780
0
           https ? "https" : "http", ipv6_addr ? "[" : "", host,
3781
0
           ipv6_addr ? "]" : "", host_port, abs_path);
3782
0
}
3783
3784
/*
3785
 * DoH GET Query String Scanner-less Recursive Descent Parser/Verifier
3786
 *
3787
 * It is based on the following grammar (using WSN/EBNF):
3788
 *
3789
 * S                = query-string.
3790
 * query-string     = ['?'] { key-value-pair } EOF.
3791
 * key-value-pair   = key '=' value [ '&' ].
3792
 * key              = ('_' | alpha) { '_' | alnum}.
3793
 * value            = value-char {value-char}.
3794
 * value-char       = unreserved-char | percent-charcode.
3795
 * unreserved-char  = alnum |'_' | '.' | '-' | '~'. (* RFC3986, Section 2.3 *)
3796
 * percent-charcode = '%' hexdigit hexdigit.
3797
 * ...
3798
 *
3799
 * Should be good enough.
3800
 */
3801
typedef struct isc_httpparser_state {
3802
  const char *str;
3803
3804
  const char *last_key;
3805
  size_t last_key_len;
3806
3807
  const char *last_value;
3808
  size_t last_value_len;
3809
3810
  bool query_found;
3811
  const char *query;
3812
  size_t query_len;
3813
} isc_httpparser_state_t;
3814
3815
0
#define MATCH(ch)      (st->str[0] == (ch))
3816
0
#define MATCH_ALPHA()  isalpha((unsigned char)(st->str[0]))
3817
0
#define MATCH_DIGIT()  isdigit((unsigned char)(st->str[0]))
3818
0
#define MATCH_ALNUM()  isalnum((unsigned char)(st->str[0]))
3819
0
#define MATCH_XDIGIT() isxdigit((unsigned char)(st->str[0]))
3820
0
#define ADVANCE()      st->str++
3821
0
#define GETP()         (st->str)
3822
3823
static bool
3824
rule_query_string(isc_httpparser_state_t *st);
3825
3826
bool
3827
isc__nm_parse_httpquery(const char *query_string, const char **start,
3828
0
      size_t *len) {
3829
0
  isc_httpparser_state_t state;
3830
3831
0
  REQUIRE(start != NULL);
3832
0
  REQUIRE(len != NULL);
3833
3834
0
  if (query_string == NULL || query_string[0] == '\0') {
3835
0
    return false;
3836
0
  }
3837
3838
0
  state = (isc_httpparser_state_t){ .str = query_string };
3839
0
  if (!rule_query_string(&state)) {
3840
0
    return false;
3841
0
  }
3842
3843
0
  if (!state.query_found) {
3844
0
    return false;
3845
0
  }
3846
3847
0
  *start = state.query;
3848
0
  *len = state.query_len;
3849
3850
0
  return true;
3851
0
}
3852
3853
static bool
3854
rule_key_value_pair(isc_httpparser_state_t *st);
3855
3856
static bool
3857
rule_key(isc_httpparser_state_t *st);
3858
3859
static bool
3860
rule_value(isc_httpparser_state_t *st);
3861
3862
static bool
3863
rule_value_char(isc_httpparser_state_t *st);
3864
3865
static bool
3866
rule_percent_charcode(isc_httpparser_state_t *st);
3867
3868
static bool
3869
rule_unreserved_char(isc_httpparser_state_t *st);
3870
3871
static bool
3872
0
rule_query_string(isc_httpparser_state_t *st) {
3873
0
  if (MATCH('?')) {
3874
0
    ADVANCE();
3875
0
  }
3876
3877
0
  while (rule_key_value_pair(st)) {
3878
0
    /* skip */;
3879
0
  }
3880
3881
0
  if (!MATCH('\0')) {
3882
0
    return false;
3883
0
  }
3884
3885
0
  ADVANCE();
3886
0
  return true;
3887
0
}
3888
3889
static bool
3890
0
rule_key_value_pair(isc_httpparser_state_t *st) {
3891
0
  if (!rule_key(st)) {
3892
0
    return false;
3893
0
  }
3894
3895
0
  if (MATCH('=')) {
3896
0
    ADVANCE();
3897
0
  } else {
3898
0
    return false;
3899
0
  }
3900
3901
0
  if (rule_value(st)) {
3902
0
    const char dns[] = "dns";
3903
0
    if (st->last_key_len == sizeof(dns) - 1 &&
3904
0
        memcmp(st->last_key, dns, sizeof(dns) - 1) == 0)
3905
0
    {
3906
0
      st->query_found = true;
3907
0
      st->query = st->last_value;
3908
0
      st->query_len = st->last_value_len;
3909
0
    }
3910
0
  } else {
3911
0
    return false;
3912
0
  }
3913
3914
0
  if (MATCH('&')) {
3915
0
    ADVANCE();
3916
0
  }
3917
3918
0
  return true;
3919
0
}
3920
3921
static bool
3922
0
rule_key(isc_httpparser_state_t *st) {
3923
0
  if (MATCH('_') || MATCH_ALPHA()) {
3924
0
    st->last_key = GETP();
3925
0
    ADVANCE();
3926
0
  } else {
3927
0
    return false;
3928
0
  }
3929
3930
0
  while (MATCH('_') || MATCH_ALNUM()) {
3931
0
    ADVANCE();
3932
0
  }
3933
3934
0
  st->last_key_len = GETP() - st->last_key;
3935
0
  return true;
3936
0
}
3937
3938
static bool
3939
0
rule_value(isc_httpparser_state_t *st) {
3940
0
  const char *s = GETP();
3941
0
  if (!rule_value_char(st)) {
3942
0
    return false;
3943
0
  }
3944
3945
0
  st->last_value = s;
3946
0
  while (rule_value_char(st)) {
3947
0
    /* skip */;
3948
0
  }
3949
0
  st->last_value_len = GETP() - st->last_value;
3950
0
  return true;
3951
0
}
3952
3953
static bool
3954
0
rule_value_char(isc_httpparser_state_t *st) {
3955
0
  if (rule_unreserved_char(st)) {
3956
0
    return true;
3957
0
  }
3958
3959
0
  return rule_percent_charcode(st);
3960
0
}
3961
3962
static bool
3963
0
rule_unreserved_char(isc_httpparser_state_t *st) {
3964
0
  if (MATCH_ALNUM() || MATCH('_') || MATCH('.') || MATCH('-') ||
3965
0
      MATCH('~'))
3966
0
  {
3967
0
    ADVANCE();
3968
0
    return true;
3969
0
  }
3970
0
  return false;
3971
0
}
3972
3973
static bool
3974
0
rule_percent_charcode(isc_httpparser_state_t *st) {
3975
0
  if (MATCH('%')) {
3976
0
    ADVANCE();
3977
0
  } else {
3978
0
    return false;
3979
0
  }
3980
3981
0
  if (!MATCH_XDIGIT()) {
3982
0
    return false;
3983
0
  }
3984
0
  ADVANCE();
3985
3986
0
  if (!MATCH_XDIGIT()) {
3987
0
    return false;
3988
0
  }
3989
0
  ADVANCE();
3990
3991
0
  return true;
3992
0
}
3993
3994
/*
3995
 * DoH URL Location Verifier. Based on the following grammar (EBNF/WSN
3996
 * notation):
3997
 *
3998
 * S             = path_absolute.
3999
 * path_absolute = '/' [ segments ] '\0'.
4000
 * segments      = segment_nz { slash_segment }.
4001
 * slash_segment = '/' segment.
4002
 * segment       = { pchar }.
4003
 * segment_nz    = pchar { pchar }.
4004
 * pchar         = unreserved | pct_encoded | sub_delims | ':' | '@'.
4005
 * unreserved    = ALPHA | DIGIT | '-' | '.' | '_' | '~'.
4006
 * pct_encoded   = '%' XDIGIT XDIGIT.
4007
 * sub_delims    = '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' |
4008
 *                 ',' | ';' | '='.
4009
 *
4010
 * The grammar is extracted from RFC 3986. It is slightly modified to
4011
 * aid in parser creation, but the end result is the same
4012
 * (path_absolute is defined slightly differently - split into
4013
 * multiple productions).
4014
 *
4015
 * https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
4016
 */
4017
4018
typedef struct isc_http_location_parser_state {
4019
  const char *str;
4020
} isc_http_location_parser_state_t;
4021
4022
static bool
4023
rule_loc_path_absolute(isc_http_location_parser_state_t *);
4024
4025
static bool
4026
rule_loc_segments(isc_http_location_parser_state_t *);
4027
4028
static bool
4029
rule_loc_slash_segment(isc_http_location_parser_state_t *);
4030
4031
static bool
4032
rule_loc_segment(isc_http_location_parser_state_t *);
4033
4034
static bool
4035
rule_loc_segment_nz(isc_http_location_parser_state_t *);
4036
4037
static bool
4038
rule_loc_pchar(isc_http_location_parser_state_t *);
4039
4040
static bool
4041
rule_loc_unreserved(isc_http_location_parser_state_t *);
4042
4043
static bool
4044
rule_loc_pct_encoded(isc_http_location_parser_state_t *);
4045
4046
static bool
4047
rule_loc_sub_delims(isc_http_location_parser_state_t *);
4048
4049
static bool
4050
0
rule_loc_path_absolute(isc_http_location_parser_state_t *st) {
4051
0
  if (MATCH('/')) {
4052
0
    ADVANCE();
4053
0
  } else {
4054
0
    return false;
4055
0
  }
4056
4057
0
  (void)rule_loc_segments(st);
4058
4059
0
  if (MATCH('\0')) {
4060
0
    ADVANCE();
4061
0
  } else {
4062
0
    return false;
4063
0
  }
4064
4065
0
  return true;
4066
0
}
4067
4068
static bool
4069
0
rule_loc_segments(isc_http_location_parser_state_t *st) {
4070
0
  if (!rule_loc_segment_nz(st)) {
4071
0
    return false;
4072
0
  }
4073
4074
0
  while (rule_loc_slash_segment(st)) {
4075
0
    /* zero or more */;
4076
0
  }
4077
4078
0
  return true;
4079
0
}
4080
4081
static bool
4082
0
rule_loc_slash_segment(isc_http_location_parser_state_t *st) {
4083
0
  if (MATCH('/')) {
4084
0
    ADVANCE();
4085
0
  } else {
4086
0
    return false;
4087
0
  }
4088
4089
0
  return rule_loc_segment(st);
4090
0
}
4091
4092
static bool
4093
0
rule_loc_segment(isc_http_location_parser_state_t *st) {
4094
0
  while (rule_loc_pchar(st)) {
4095
0
    /* zero or more */;
4096
0
  }
4097
4098
0
  return true;
4099
0
}
4100
4101
static bool
4102
0
rule_loc_segment_nz(isc_http_location_parser_state_t *st) {
4103
0
  if (!rule_loc_pchar(st)) {
4104
0
    return false;
4105
0
  }
4106
4107
0
  while (rule_loc_pchar(st)) {
4108
0
    /* zero or more */;
4109
0
  }
4110
4111
0
  return true;
4112
0
}
4113
4114
static bool
4115
0
rule_loc_pchar(isc_http_location_parser_state_t *st) {
4116
0
  if (rule_loc_unreserved(st)) {
4117
0
    return true;
4118
0
  } else if (rule_loc_pct_encoded(st)) {
4119
0
    return true;
4120
0
  } else if (rule_loc_sub_delims(st)) {
4121
0
    return true;
4122
0
  } else if (MATCH(':') || MATCH('@')) {
4123
0
    ADVANCE();
4124
0
    return true;
4125
0
  }
4126
4127
0
  return false;
4128
0
}
4129
4130
static bool
4131
0
rule_loc_unreserved(isc_http_location_parser_state_t *st) {
4132
0
  if (MATCH_ALPHA() | MATCH_DIGIT() | MATCH('-') | MATCH('.') |
4133
0
      MATCH('_') | MATCH('~'))
4134
0
  {
4135
0
    ADVANCE();
4136
0
    return true;
4137
0
  }
4138
0
  return false;
4139
0
}
4140
4141
static bool
4142
0
rule_loc_pct_encoded(isc_http_location_parser_state_t *st) {
4143
0
  if (!MATCH('%')) {
4144
0
    return false;
4145
0
  }
4146
0
  ADVANCE();
4147
4148
0
  if (!MATCH_XDIGIT()) {
4149
0
    return false;
4150
0
  }
4151
0
  ADVANCE();
4152
4153
0
  if (!MATCH_XDIGIT()) {
4154
0
    return false;
4155
0
  }
4156
0
  ADVANCE();
4157
4158
0
  return true;
4159
0
}
4160
4161
static bool
4162
0
rule_loc_sub_delims(isc_http_location_parser_state_t *st) {
4163
0
  if (MATCH('!') | MATCH('$') | MATCH('&') | MATCH('\'') | MATCH('(') |
4164
0
      MATCH(')') | MATCH('*') | MATCH('+') | MATCH(',') | MATCH(';') |
4165
0
      MATCH('='))
4166
0
  {
4167
0
    ADVANCE();
4168
0
    return true;
4169
0
  }
4170
4171
0
  return false;
4172
0
}
4173
4174
bool
4175
0
isc_nm_http_path_isvalid(const char *path) {
4176
0
  isc_http_location_parser_state_t state = { 0 };
4177
4178
0
  REQUIRE(path != NULL);
4179
4180
0
  state.str = path;
4181
4182
0
  return rule_loc_path_absolute(&state);
4183
0
}