Coverage Report

Created: 2026-09-06 06:17

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