Coverage Report

Created: 2026-01-10 06:28

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