Coverage Report

Created: 2026-01-17 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/netmgr/proxystream.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 <isc/netmgr.h>
15
16
#include "netmgr-int.h"
17
18
/*
19
 * The idea behind the transport is simple after accepting the
20
 * connection or connecting to a remote server it enters PROXYv2
21
 * handling mode: that is, it either attempts to read (when accepting
22
 * the connection) or send (when establishing a connection) a PROXYv2
23
 * header. After that it works like a mere wrapper on top of the
24
 * underlying stream-based transport (TCP).
25
 */
26
27
typedef struct proxystream_send_req {
28
  isc_nm_cb_t cb;        /* send callback */
29
  void *cbarg;         /* send callback argument */
30
  isc_nmhandle_t *proxyhandle; /* PROXY Stream socket handle */
31
} proxystream_send_req_t;
32
33
static void
34
proxystream_on_header_data_cb(const isc_result_t result,
35
            const isc_proxy2_command_t cmd,
36
            const int socktype,
37
            const isc_sockaddr_t *restrict src_addr,
38
            const isc_sockaddr_t *restrict dst_addr,
39
            const isc_region_t *restrict tlv_blob,
40
            const isc_region_t *restrict extra, void *cbarg);
41
42
static isc_nmsocket_t *
43
proxystream_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type,
44
         isc_sockaddr_t *addr, const bool is_server);
45
46
static isc_result_t
47
proxystream_accept_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
48
49
static void
50
proxystream_connect_cb(isc_nmhandle_t *handle, isc_result_t result,
51
           void *cbarg);
52
53
static void
54
proxystream_failed_read_cb_async(void *arg);
55
56
static void
57
proxystream_clear_proxy_header_data(isc_nmsocket_t *sock);
58
59
static void
60
proxystream_read_start(isc_nmsocket_t *sock);
61
62
static void
63
proxystream_read_stop(isc_nmsocket_t *sock);
64
65
static void
66
proxystream_try_close_unused(isc_nmsocket_t *sock);
67
68
static void
69
proxystream_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
70
          isc_result_t result);
71
72
static bool
73
proxystream_closing(isc_nmsocket_t *sock);
74
75
static void
76
proxystream_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result);
77
78
static void
79
proxystream_read_cb(isc_nmhandle_t *handle, isc_result_t result,
80
        isc_region_t *region, void *cbarg);
81
82
static void
83
proxystream_read_extra_cb(void *arg);
84
85
static proxystream_send_req_t *
86
proxystream_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
87
       isc_nmhandle_t *proxyhandle, isc_nm_cb_t cb,
88
       void *cbarg);
89
90
static void
91
proxystream_put_send_req(isc_mem_t *mctx, proxystream_send_req_t *send_req,
92
       const bool force_destroy);
93
94
static void
95
proxystream_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
96
97
static void
98
proxystream_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
99
     void *cbarg, const bool dnsmsg);
100
101
static void
102
proxystream_on_header_data_cb(const isc_result_t result,
103
            const isc_proxy2_command_t cmd,
104
            const int socktype,
105
            const isc_sockaddr_t *restrict src_addr,
106
            const isc_sockaddr_t *restrict dst_addr,
107
            const isc_region_t *restrict tlvs,
108
0
            const isc_region_t *restrict extra, void *cbarg) {
109
0
  isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
110
111
0
  switch (result) {
112
0
  case ISC_R_SUCCESS: {
113
0
    isc_nmhandle_t *proxyhandle = NULL;
114
0
    isc_result_t accept_result = ISC_R_FAILURE;
115
0
    bool call_accept = false;
116
0
    bool is_unspec = false;
117
118
    /*
119
     * After header has been processed - stop reading (thus,
120
     * stopping the timer) and disable manual timer control as in
121
     * the case of TCP it is disabled by default
122
     */
123
0
    proxystream_read_stop(sock);
124
0
    isc__nmsocket_timer_stop(sock);
125
0
    isc__nmhandle_set_manual_timer(sock->outerhandle, false);
126
127
0
    sock->proxy.header_processed = true;
128
0
    if (extra == NULL) {
129
0
      sock->proxy.extra_processed = true;
130
0
    }
131
132
    /* Process header data */
133
0
    if (cmd == ISC_PROXY2_CMD_LOCAL) {
134
0
      is_unspec = true;
135
0
      call_accept = true;
136
0
    } else if (cmd == ISC_PROXY2_CMD_PROXY) {
137
0
      switch (socktype) {
138
0
      case 0:
139
        /*
140
         * Treat unsupported addresses (aka AF_UNSPEC)
141
         * as LOCAL.
142
         */
143
0
        is_unspec = true;
144
0
        call_accept = true;
145
0
        break;
146
0
      case SOCK_DGRAM:
147
        /*
148
         * In some cases proxies can do protocol
149
         * conversion. In this case, the original
150
         * request might have arrived over UDP-based
151
         * transport and, thus, the PROXYv2 header can
152
         * contain SOCK_DGRAM, while for TCP one would
153
         * expect SOCK_STREAM. That might be unexpected,
154
         * but, as the main idea behind PROXYv2 is to
155
         * carry the original endpoint information to
156
         * back-ends, that is fine.
157
         *
158
         * At least "dnsdist" does that when redirecting
159
         * a UDP request to a TCP or TLS-only server.
160
         */
161
0
      case SOCK_STREAM:
162
0
        INSIST(isc_sockaddr_pf(src_addr) ==
163
0
               isc_sockaddr_pf(dst_addr));
164
        /* We will treat AF_UNIX as unspec */
165
0
        if (isc_sockaddr_pf(src_addr) == AF_UNIX) {
166
0
          is_unspec = true;
167
0
        }
168
169
0
        if (!is_unspec &&
170
0
            !isc__nm_valid_proxy_addresses(src_addr,
171
0
                   dst_addr))
172
0
        {
173
0
          break;
174
0
        }
175
176
0
        call_accept = true;
177
0
        break;
178
0
      default:
179
0
        break;
180
0
      }
181
0
    }
182
183
0
    if (call_accept) {
184
0
      if (is_unspec) {
185
0
        proxyhandle = isc__nmhandle_get(
186
0
          sock, &sock->peer, &sock->iface);
187
0
      } else {
188
0
        INSIST(src_addr != NULL);
189
0
        INSIST(dst_addr != NULL);
190
0
        proxyhandle = isc__nmhandle_get(sock, src_addr,
191
0
                dst_addr);
192
0
      }
193
0
      proxyhandle->proxy_is_unspec = is_unspec;
194
0
      isc__nm_received_proxy_header_log(proxyhandle, cmd,
195
0
                socktype, src_addr,
196
0
                dst_addr, tlvs);
197
0
      accept_result = sock->accept_cb(proxyhandle, result,
198
0
              sock->accept_cbarg);
199
0
      isc_nmhandle_detach(&proxyhandle);
200
0
    }
201
202
0
    if (accept_result != ISC_R_SUCCESS) {
203
0
      isc__nmsocket_detach(&sock->listener);
204
0
      isc_nmhandle_detach(&sock->outerhandle);
205
0
      sock->closed = true;
206
0
    }
207
208
0
    sock->accepting = false;
209
210
0
    proxystream_try_close_unused(sock);
211
0
  } break;
212
0
  case ISC_R_NOMORE:
213
    /*
214
     * That is fine, wait for more data to complete the PROXY
215
     * header
216
     */
217
0
    break;
218
0
  default:
219
0
    proxystream_failed_read_cb(sock, result);
220
0
    break;
221
0
  };
222
0
}
223
224
static void
225
proxystream_handle_incoming_header_data(isc_nmsocket_t *sock,
226
0
          isc_region_t *restrict data) {
227
0
  isc_proxy2_handler_t *restrict handler = sock->proxy.proxy2.handler;
228
229
0
  (void)isc_proxy2_handler_push(handler, data);
230
0
  proxystream_try_close_unused(sock);
231
0
}
232
233
static isc_nmsocket_t *
234
proxystream_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type,
235
0
         isc_sockaddr_t *addr, const bool is_server) {
236
0
  isc_nmsocket_t *sock;
237
0
  INSIST(type == isc_nm_proxystreamsocket ||
238
0
         type == isc_nm_proxystreamlistener);
239
240
0
  sock = isc_mempool_get(worker->nmsocket_pool);
241
0
  isc__nmsocket_init(sock, worker, type, addr, NULL);
242
0
  sock->result = ISC_R_UNSET;
243
0
  if (type == isc_nm_proxystreamsocket) {
244
0
    sock->read_timeout = isc_nm_getinitialtimeout();
245
0
    sock->client = !is_server;
246
0
    sock->connecting = !is_server;
247
0
    if (is_server) {
248
      /*
249
       * Smallest TCP (over IPv6) segment size we required to
250
       * support. An adequate value for both IPv4 and IPv6.
251
       */
252
0
      sock->proxy.proxy2.handler = isc_proxy2_handler_new(
253
0
        worker->mctx, NM_MAXSEG,
254
0
        proxystream_on_header_data_cb, sock);
255
0
    } else {
256
0
      isc_buffer_allocate(worker->mctx,
257
0
              &sock->proxy.proxy2.outbuf,
258
0
              ISC_NM_PROXY2_DEFAULT_BUFFER_SIZE);
259
0
    }
260
0
  }
261
262
0
  return sock;
263
0
}
264
265
static isc_result_t
266
proxystream_accept_cb(isc_nmhandle_t *handle, isc_result_t result,
267
0
          void *cbarg) {
268
0
  isc_nmsocket_t *listensock = (isc_nmsocket_t *)cbarg;
269
0
  isc_nmsocket_t *nsock = NULL;
270
0
  isc_sockaddr_t iface;
271
272
0
  if (result != ISC_R_SUCCESS) {
273
0
    return result;
274
0
  }
275
276
0
  INSIST(VALID_NMHANDLE(handle));
277
0
  INSIST(VALID_NMSOCK(handle->sock));
278
0
  INSIST(VALID_NMSOCK(listensock));
279
0
  INSIST(listensock->type == isc_nm_proxystreamlistener);
280
281
0
  if (isc__nm_closing(handle->sock->worker)) {
282
0
    return ISC_R_SHUTTINGDOWN;
283
0
  } else if (isc__nmsocket_closing(handle->sock)) {
284
0
    return ISC_R_CANCELED;
285
0
  }
286
287
0
  iface = isc_nmhandle_localaddr(handle);
288
0
  nsock = proxystream_sock_new(handle->sock->worker,
289
0
             isc_nm_proxystreamsocket, &iface, true);
290
0
  INSIST(listensock->accept_cb != NULL);
291
0
  nsock->accept_cb = listensock->accept_cb;
292
0
  nsock->accept_cbarg = listensock->accept_cbarg;
293
294
0
  nsock->peer = isc_nmhandle_peeraddr(handle);
295
0
  nsock->tid = isc_tid();
296
0
  nsock->accepting = true;
297
0
  nsock->active = true;
298
299
0
  isc__nmsocket_attach(listensock, &nsock->listener);
300
0
  isc_nmhandle_attach(handle, &nsock->outerhandle);
301
0
  handle->sock->proxy.sock = nsock;
302
303
  /*
304
   * We need to control the timer manually as we do *not* want it to
305
   * be reset on partial header data reads.
306
   */
307
0
  isc__nmhandle_set_manual_timer(nsock->outerhandle, true);
308
0
  isc__nmsocket_timer_restart(nsock);
309
310
0
  proxystream_read_start(nsock);
311
312
0
  return ISC_R_SUCCESS;
313
0
}
314
315
isc_result_t
316
isc_nm_listenproxystream(uint32_t workers, isc_sockaddr_t *iface,
317
       isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
318
       int backlog, isc_quota_t *quota, isc_tlsctx_t *tlsctx,
319
0
       isc_nmsocket_t **sockp) {
320
0
  isc_result_t result;
321
0
  isc_nmsocket_t *listener = NULL;
322
0
  isc__networker_t *worker = isc__networker_current();
323
324
0
  REQUIRE(isc_tid() == 0);
325
0
  REQUIRE(sockp != NULL && *sockp == NULL);
326
327
0
  if (isc__nm_closing(worker)) {
328
0
    return ISC_R_SHUTTINGDOWN;
329
0
  }
330
331
0
  listener = proxystream_sock_new(worker, isc_nm_proxystreamlistener,
332
0
          iface, true);
333
0
  listener->accept_cb = accept_cb;
334
0
  listener->accept_cbarg = accept_cbarg;
335
336
0
  if (tlsctx == NULL) {
337
0
    result = isc_nm_listentcp(workers, iface, proxystream_accept_cb,
338
0
            listener, backlog, quota,
339
0
            &listener->outer);
340
0
  } else {
341
0
    result = isc_nm_listentls(workers, iface, proxystream_accept_cb,
342
0
            listener, backlog, quota, tlsctx,
343
0
            false, &listener->outer);
344
0
  }
345
346
0
  if (result != ISC_R_SUCCESS) {
347
0
    listener->closed = true;
348
0
    isc__nmsocket_detach(&listener);
349
0
    return result;
350
0
  }
351
352
0
  listener->active = true;
353
0
  listener->result = result;
354
0
  listener->nchildren = listener->outer->nchildren;
355
356
0
  *sockp = listener;
357
358
0
  return result;
359
0
}
360
361
static void
362
0
proxystream_try_close_unused(isc_nmsocket_t *sock) {
363
  /* try to close unused socket */
364
0
  if (sock->statichandle == NULL && sock->proxy.nsending == 0) {
365
0
    isc__nmsocket_prep_destroy(sock);
366
0
  }
367
0
}
368
369
static void
370
proxystream_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
371
0
          isc_result_t result) {
372
0
  sock->connecting = false;
373
0
  if (sock->connect_cb == NULL) {
374
0
    return;
375
0
  }
376
377
0
  if (result == ISC_R_SUCCESS) {
378
0
    sock->connected = true;
379
0
  }
380
381
0
  sock->connect_cb(handle, result, sock->connect_cbarg);
382
0
  if (result != ISC_R_SUCCESS) {
383
0
    isc__nmsocket_clearcb(handle->sock);
384
0
  }
385
0
}
386
387
static void
388
proxystream_send_header_cb(isc_nmhandle_t *transphandle, isc_result_t result,
389
0
         void *cbarg) {
390
0
  isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
391
0
  isc_nmhandle_t *proxyhandle = NULL;
392
393
0
  REQUIRE(VALID_NMHANDLE(transphandle));
394
0
  REQUIRE(VALID_NMSOCK(sock));
395
396
0
  sock->proxy.nsending--;
397
0
  sock->proxy.header_processed = true;
398
399
0
  if (isc__nm_closing(transphandle->sock->worker)) {
400
0
    result = ISC_R_SHUTTINGDOWN;
401
0
  }
402
403
0
  proxyhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
404
0
  proxystream_call_connect_cb(sock, proxyhandle, result);
405
0
  isc_nmhandle_detach(&proxyhandle);
406
407
0
  proxystream_try_close_unused(sock);
408
0
}
409
410
static void
411
proxystream_connect_cb(isc_nmhandle_t *handle, isc_result_t result,
412
0
           void *cbarg) {
413
0
  isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
414
0
  isc_nmhandle_t *proxyhandle = NULL;
415
0
  isc_region_t header = { 0 };
416
417
0
  REQUIRE(VALID_NMSOCK(sock));
418
419
0
  sock->tid = isc_tid();
420
421
0
  if (result != ISC_R_SUCCESS) {
422
0
    goto error;
423
0
  }
424
425
0
  INSIST(VALID_NMHANDLE(handle));
426
427
0
  sock->iface = isc_nmhandle_localaddr(handle);
428
0
  sock->peer = isc_nmhandle_peeraddr(handle);
429
0
  if (isc__nm_closing(handle->sock->worker)) {
430
0
    result = ISC_R_SHUTTINGDOWN;
431
0
    goto error;
432
0
  } else if (isc__nmsocket_closing(handle->sock)) {
433
0
    result = ISC_R_CANCELED;
434
0
    goto error;
435
0
  }
436
437
0
  isc_nmhandle_attach(handle, &sock->outerhandle);
438
0
  handle->sock->proxy.sock = sock;
439
0
  sock->active = true;
440
441
0
  isc_buffer_usedregion(sock->proxy.proxy2.outbuf, &header);
442
0
  sock->proxy.nsending++;
443
0
  isc_nm_send(handle, &header, proxystream_send_header_cb, sock);
444
445
0
  proxystream_try_close_unused(sock);
446
447
0
  return;
448
0
error:
449
0
  proxyhandle = isc__nmhandle_get(sock, NULL, NULL);
450
0
  sock->closed = true;
451
0
  proxystream_call_connect_cb(sock, proxyhandle, result);
452
0
  isc_nmhandle_detach(&proxyhandle);
453
0
  isc__nmsocket_detach(&sock);
454
0
}
455
456
void
457
isc_nm_proxystreamconnect(isc_sockaddr_t *local, isc_sockaddr_t *peer,
458
        isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
459
        isc_tlsctx_t *tlsctx, const char *sni_hostname,
460
        isc_tlsctx_client_session_cache_t *client_sess_cache,
461
0
        isc_nm_proxyheader_info_t *proxy_info) {
462
0
  isc_result_t result = ISC_R_FAILURE;
463
0
  isc_nmsocket_t *nsock = NULL;
464
0
  isc__networker_t *worker = isc__networker_current();
465
466
0
  if (isc__nm_closing(worker)) {
467
0
    cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
468
0
    return;
469
0
  }
470
471
0
  nsock = proxystream_sock_new(worker, isc_nm_proxystreamsocket, local,
472
0
             false);
473
0
  nsock->connect_cb = cb;
474
0
  nsock->connect_cbarg = cbarg;
475
0
  nsock->connect_timeout = timeout;
476
477
0
  if (proxy_info == NULL) {
478
0
    result = isc_proxy2_make_header(nsock->proxy.proxy2.outbuf,
479
0
            ISC_PROXY2_CMD_LOCAL, 0, NULL,
480
0
            NULL, NULL);
481
0
  } else if (proxy_info->complete) {
482
0
    isc_buffer_putmem(nsock->proxy.proxy2.outbuf,
483
0
          proxy_info->complete_header.base,
484
0
          proxy_info->complete_header.length);
485
0
    result = ISC_R_SUCCESS;
486
0
  } else if (!proxy_info->complete) {
487
0
    result = isc_proxy2_make_header(
488
0
      nsock->proxy.proxy2.outbuf, ISC_PROXY2_CMD_PROXY,
489
0
      SOCK_STREAM, &proxy_info->proxy_info.src_addr,
490
0
      &proxy_info->proxy_info.dst_addr,
491
0
      &proxy_info->proxy_info.tlv_data);
492
0
  }
493
0
  RUNTIME_CHECK(result == ISC_R_SUCCESS);
494
495
0
  if (tlsctx == NULL) {
496
0
    isc_nm_tcpconnect(local, peer, proxystream_connect_cb, nsock,
497
0
          nsock->connect_timeout);
498
0
  } else {
499
0
    isc_nm_tlsconnect(local, peer, proxystream_connect_cb, nsock,
500
0
          tlsctx, sni_hostname, client_sess_cache,
501
0
          nsock->connect_timeout, false, NULL);
502
0
  }
503
0
}
504
505
static void
506
0
proxystream_failed_read_cb_async(void *arg) {
507
0
  isc__nm_uvreq_t *req = (isc__nm_uvreq_t *)arg;
508
509
0
  proxystream_failed_read_cb(req->sock, req->result);
510
0
  isc__nm_uvreq_put(&req);
511
0
}
512
513
void
514
isc__nm_proxystream_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
515
0
           bool async) {
516
0
  proxystream_read_stop(sock);
517
518
0
  if (!async) {
519
0
    proxystream_failed_read_cb(sock, result);
520
0
  } else {
521
0
    isc__nm_uvreq_t *req = isc__nm_uvreq_get(sock);
522
0
    req->result = result;
523
0
    req->cbarg = sock;
524
0
    isc_job_run(sock->worker->loop, &req->job,
525
0
          proxystream_failed_read_cb_async, req);
526
0
  }
527
0
}
528
529
void
530
0
isc__nm_proxystream_stoplistening(isc_nmsocket_t *sock) {
531
0
  REQUIRE(VALID_NMSOCK(sock));
532
0
  REQUIRE(sock->type == isc_nm_proxystreamlistener);
533
0
  REQUIRE(sock->proxy.sock == NULL);
534
535
0
  isc__nmsocket_stop(sock);
536
0
}
537
538
static void
539
0
proxystream_clear_proxy_header_data(isc_nmsocket_t *sock) {
540
0
  if (!sock->client && sock->proxy.proxy2.handler != NULL) {
541
0
    isc_proxy2_handler_free(&sock->proxy.proxy2.handler);
542
0
  } else if (sock->client && sock->proxy.proxy2.outbuf != NULL) {
543
0
    isc_buffer_free(&sock->proxy.proxy2.outbuf);
544
0
  }
545
0
}
546
547
void
548
0
isc__nm_proxystream_cleanup_data(isc_nmsocket_t *sock) {
549
0
  switch (sock->type) {
550
0
  case isc_nm_tcpsocket:
551
0
  case isc_nm_tlssocket:
552
0
    if (sock->proxy.sock != NULL) {
553
0
      isc__nmsocket_detach(&sock->proxy.sock);
554
0
    }
555
0
    break;
556
0
  case isc_nm_proxystreamsocket:
557
0
    if (sock->proxy.send_req != NULL) {
558
0
      proxystream_put_send_req(
559
0
        sock->worker->mctx,
560
0
        (proxystream_send_req_t *)sock->proxy.send_req,
561
0
        true);
562
0
    }
563
564
0
    proxystream_clear_proxy_header_data(sock);
565
0
    break;
566
0
  default:
567
0
    break;
568
0
  };
569
0
}
570
571
void
572
0
isc__nmhandle_proxystream_cleartimeout(isc_nmhandle_t *handle) {
573
0
  isc_nmsocket_t *sock = NULL;
574
575
0
  REQUIRE(VALID_NMHANDLE(handle));
576
0
  REQUIRE(VALID_NMSOCK(handle->sock));
577
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
578
579
0
  sock = handle->sock;
580
0
  if (sock->outerhandle != NULL) {
581
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
582
0
    isc_nmhandle_cleartimeout(sock->outerhandle);
583
0
  }
584
0
}
585
586
void
587
0
isc__nmhandle_proxystream_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
588
0
  isc_nmsocket_t *sock = NULL;
589
590
0
  REQUIRE(VALID_NMHANDLE(handle));
591
0
  REQUIRE(VALID_NMSOCK(handle->sock));
592
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
593
594
0
  sock = handle->sock;
595
0
  if (sock->outerhandle != NULL) {
596
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
597
0
    isc_nmhandle_settimeout(sock->outerhandle, timeout);
598
0
  }
599
0
}
600
601
void
602
0
isc__nmhandle_proxystream_keepalive(isc_nmhandle_t *handle, bool value) {
603
0
  isc_nmsocket_t *sock = NULL;
604
605
0
  REQUIRE(VALID_NMHANDLE(handle));
606
0
  REQUIRE(VALID_NMSOCK(handle->sock));
607
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
608
609
0
  sock = handle->sock;
610
0
  if (sock->outerhandle != NULL) {
611
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
612
613
0
    isc_nmhandle_keepalive(sock->outerhandle, value);
614
0
  }
615
0
}
616
617
void
618
isc__nmhandle_proxystream_setwritetimeout(isc_nmhandle_t *handle,
619
0
            uint64_t write_timeout) {
620
0
  isc_nmsocket_t *sock = NULL;
621
622
0
  REQUIRE(VALID_NMHANDLE(handle));
623
0
  REQUIRE(VALID_NMSOCK(handle->sock));
624
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
625
626
0
  sock = handle->sock;
627
0
  if (sock->outerhandle != NULL) {
628
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
629
630
0
    isc_nmhandle_setwritetimeout(sock->outerhandle, write_timeout);
631
0
  }
632
0
}
633
634
void
635
0
isc__nmsocket_proxystream_reset(isc_nmsocket_t *sock) {
636
0
  REQUIRE(VALID_NMSOCK(sock));
637
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
638
639
0
  if (sock->outerhandle != NULL) {
640
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
641
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
642
0
    isc__nmsocket_reset(sock->outerhandle->sock);
643
0
  }
644
0
}
645
646
bool
647
0
isc__nmsocket_proxystream_timer_running(isc_nmsocket_t *sock) {
648
0
  REQUIRE(VALID_NMSOCK(sock));
649
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
650
651
0
  if (sock->outerhandle != NULL) {
652
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
653
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
654
0
    return isc__nmsocket_timer_running(sock->outerhandle->sock);
655
0
  }
656
657
0
  return false;
658
0
}
659
660
void
661
0
isc__nmsocket_proxystream_timer_restart(isc_nmsocket_t *sock) {
662
0
  REQUIRE(VALID_NMSOCK(sock));
663
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
664
665
0
  if (sock->outerhandle != NULL) {
666
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
667
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
668
0
    isc__nmsocket_timer_restart(sock->outerhandle->sock);
669
0
  }
670
0
}
671
672
void
673
0
isc__nmsocket_proxystream_timer_stop(isc_nmsocket_t *sock) {
674
0
  REQUIRE(VALID_NMSOCK(sock));
675
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
676
677
0
  if (sock->outerhandle != NULL) {
678
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
679
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
680
0
    isc__nmsocket_timer_stop(sock->outerhandle->sock);
681
0
  }
682
0
}
683
684
void
685
isc__nmhandle_proxystream_set_manual_timer(isc_nmhandle_t *handle,
686
0
             const bool manual) {
687
0
  isc_nmsocket_t *sock = NULL;
688
689
0
  REQUIRE(VALID_NMHANDLE(handle));
690
0
  REQUIRE(VALID_NMSOCK(handle->sock));
691
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
692
693
0
  sock = handle->sock;
694
0
  if (sock->outerhandle != NULL) {
695
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
696
697
0
    isc__nmhandle_set_manual_timer(sock->outerhandle, manual);
698
0
  }
699
0
}
700
701
isc_result_t
702
isc__nmhandle_proxystream_set_tcp_nodelay(isc_nmhandle_t *handle,
703
0
            const bool value) {
704
0
  isc_nmsocket_t *sock = NULL;
705
0
  isc_result_t result = ISC_R_FAILURE;
706
707
0
  REQUIRE(VALID_NMHANDLE(handle));
708
0
  REQUIRE(VALID_NMSOCK(handle->sock));
709
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
710
711
0
  sock = handle->sock;
712
0
  if (sock->outerhandle != NULL) {
713
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
714
715
0
    result = isc_nmhandle_set_tcp_nodelay(sock->outerhandle, value);
716
0
  }
717
718
0
  return result;
719
0
}
720
721
static void
722
0
proxystream_read_start(isc_nmsocket_t *sock) {
723
0
  if (sock->proxy.reading == true) {
724
0
    return;
725
0
  }
726
727
0
  sock->proxy.reading = true;
728
0
  if (sock->outerhandle != NULL) {
729
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
730
731
0
    isc_nm_read(sock->outerhandle, proxystream_read_cb, sock);
732
0
  }
733
0
}
734
735
static void
736
0
proxystream_read_stop(isc_nmsocket_t *sock) {
737
0
  if (sock->proxy.reading == false) {
738
0
    return;
739
0
  }
740
741
0
  sock->proxy.reading = false;
742
0
  if (sock->outerhandle != NULL) {
743
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
744
745
0
    isc_nm_read_stop(sock->outerhandle);
746
0
  }
747
0
}
748
749
void
750
0
isc__nm_proxystream_read_stop(isc_nmhandle_t *handle) {
751
0
  REQUIRE(VALID_NMHANDLE(handle));
752
0
  REQUIRE(VALID_NMSOCK(handle->sock));
753
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
754
755
0
  handle->sock->reading = false;
756
0
  proxystream_read_stop(handle->sock);
757
0
}
758
759
void
760
0
isc__nm_proxystream_close(isc_nmsocket_t *sock) {
761
0
  REQUIRE(VALID_NMSOCK(sock));
762
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
763
0
  REQUIRE(sock->tid == isc_tid());
764
765
0
  sock->closing = true;
766
767
  /*
768
   * At this point we're certain that there are no
769
   * external references, we can close everything.
770
   */
771
0
  proxystream_read_stop(sock);
772
0
  isc__nmsocket_timer_stop(sock);
773
0
  if (sock->outerhandle != NULL) {
774
0
    sock->reading = false;
775
0
    isc_nm_read_stop(sock->outerhandle);
776
0
    isc_nmhandle_close(sock->outerhandle);
777
0
    isc_nmhandle_detach(&sock->outerhandle);
778
0
  }
779
780
0
  if (sock->listener != NULL) {
781
0
    isc__nmsocket_detach(&sock->listener);
782
0
  }
783
784
  /* Further cleanup performed in isc__nm_proxystream_cleanup_data() */
785
0
  sock->closed = true;
786
0
  sock->active = false;
787
0
}
788
789
static bool
790
0
proxystream_closing(isc_nmsocket_t *sock) {
791
0
  return isc__nmsocket_closing(sock) || sock->outerhandle == NULL ||
792
0
         (sock->outerhandle != NULL &&
793
0
    isc__nmsocket_closing(sock->outerhandle->sock));
794
0
}
795
796
static void
797
0
proxystream_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result) {
798
0
  REQUIRE(VALID_NMSOCK(sock));
799
0
  REQUIRE(result != ISC_R_SUCCESS);
800
801
0
  if (sock->client && sock->connect_cb != NULL && !sock->connected) {
802
0
    isc_nmhandle_t *handle = NULL;
803
0
    INSIST(sock->statichandle == NULL);
804
0
    handle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
805
0
    proxystream_call_connect_cb(sock, handle, result);
806
0
    isc__nmsocket_clearcb(sock);
807
0
    isc_nmhandle_detach(&handle);
808
809
0
    isc__nmsocket_prep_destroy(sock);
810
0
    return;
811
0
  }
812
813
0
  isc__nmsocket_timer_stop(sock);
814
815
0
  if (sock->statichandle == NULL) {
816
0
    isc__nmsocket_prep_destroy(sock);
817
0
    return;
818
0
  }
819
820
  /* See isc__nmsocket_readtimeout_cb() */
821
0
  if (sock->client && result == ISC_R_TIMEDOUT) {
822
0
    if (sock->recv_cb != NULL) {
823
0
      isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
824
0
      isc__nm_readcb(sock, req, result, false);
825
0
    }
826
827
0
    if (isc__nmsocket_timer_running(sock)) {
828
      /* Timer was restarted, bail-out */
829
0
      return;
830
0
    }
831
832
0
    isc__nmsocket_clearcb(sock);
833
834
0
    isc__nmsocket_prep_destroy(sock);
835
0
    return;
836
0
  }
837
838
0
  if (sock->recv_cb != NULL) {
839
0
    isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
840
0
    isc__nmsocket_clearcb(sock);
841
0
    isc__nm_readcb(sock, req, result, false);
842
0
  }
843
844
0
  isc__nmsocket_prep_destroy(sock);
845
0
}
846
847
static void
848
proxystream_read_cb(isc_nmhandle_t *handle, isc_result_t result,
849
0
        isc_region_t *region, void *cbarg) {
850
0
  isc_nmsocket_t *proxysock = (isc_nmsocket_t *)cbarg;
851
852
0
  REQUIRE(VALID_NMSOCK(proxysock));
853
0
  REQUIRE(VALID_NMHANDLE(handle));
854
0
  REQUIRE(proxysock->tid == isc_tid());
855
856
0
  if (result != ISC_R_SUCCESS) {
857
0
    goto failed;
858
859
0
  } else if (isc__nm_closing(proxysock->worker)) {
860
0
    result = ISC_R_SHUTTINGDOWN;
861
0
    goto failed;
862
0
  } else if (isc__nmsocket_closing(handle->sock)) {
863
0
    result = ISC_R_CANCELED;
864
0
    goto failed;
865
0
  }
866
867
  /* Handle initial PROXY header data */
868
0
  if (!proxysock->client && !proxysock->proxy.header_processed) {
869
0
    proxystream_handle_incoming_header_data(proxysock, region);
870
0
    return;
871
0
  }
872
873
0
  proxysock->recv_cb(proxysock->statichandle, ISC_R_SUCCESS, region,
874
0
         proxysock->recv_cbarg);
875
876
0
  proxystream_try_close_unused(proxysock);
877
878
0
  return;
879
0
failed:
880
0
  proxystream_failed_read_cb(proxysock, result);
881
0
}
882
883
static void
884
0
proxystream_read_extra_cb(void *arg) {
885
0
  isc_result_t result = ISC_R_SUCCESS;
886
0
  isc__nm_uvreq_t *req = arg;
887
0
  isc_region_t extra_data = { 0 }; /* data past PROXY header */
888
889
0
  REQUIRE(VALID_UVREQ(req));
890
891
0
  isc_nmsocket_t *sock = req->sock;
892
893
0
  REQUIRE(VALID_NMSOCK(sock));
894
0
  REQUIRE(sock->tid == isc_tid());
895
896
0
  sock->proxy.extra_processed = true;
897
898
0
  if (isc__nm_closing(sock->worker)) {
899
0
    result = ISC_R_SHUTTINGDOWN;
900
0
  } else if (proxystream_closing(sock)) {
901
0
    result = ISC_R_CANCELED;
902
0
  }
903
904
0
  if (result == ISC_R_SUCCESS) {
905
0
    extra_data.base = (uint8_t *)req->uvbuf.base;
906
0
    extra_data.length = req->uvbuf.len;
907
908
0
    INSIST(extra_data.length > 0);
909
910
0
    req->cb.recv(req->handle, result, &extra_data, req->cbarg);
911
912
0
    if (sock->reading) {
913
0
      proxystream_read_start(sock);
914
0
    }
915
0
  } else {
916
0
    isc__nm_proxystream_failed_read_cb(sock, result, false);
917
0
  }
918
919
0
  isc__nm_uvreq_put(&req);
920
0
}
921
922
void
923
isc__nm_proxystream_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb,
924
0
       void *cbarg) {
925
0
  isc_nmsocket_t *sock = NULL;
926
0
  isc_region_t extra_data = { 0 }; /* data past PROXY header */
927
928
0
  REQUIRE(VALID_NMHANDLE(handle));
929
0
  sock = handle->sock;
930
0
  REQUIRE(VALID_NMSOCK(sock));
931
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
932
0
  REQUIRE(sock->recv_handle == NULL);
933
0
  REQUIRE(sock->tid == isc_tid());
934
935
0
  sock->recv_cb = cb;
936
0
  sock->recv_cbarg = cbarg;
937
0
  sock->reading = true;
938
939
0
  if (isc__nm_closing(sock->worker)) {
940
0
    isc__nm_proxystream_failed_read_cb(sock, ISC_R_SHUTTINGDOWN,
941
0
               false);
942
0
    return;
943
0
  } else if (proxystream_closing(sock)) {
944
0
    isc__nm_proxystream_failed_read_cb(sock, ISC_R_CANCELED, true);
945
0
    return;
946
0
  }
947
948
  /* check if there is extra data on the server */
949
0
  if (!sock->client && sock->proxy.header_processed &&
950
0
      !sock->proxy.extra_processed &&
951
0
      isc_proxy2_handler_extra(sock->proxy.proxy2.handler, &extra_data) >
952
0
        0)
953
0
  {
954
0
    isc__nm_uvreq_t *req = isc__nm_uvreq_get(sock);
955
0
    isc_nmhandle_attach(handle, &req->handle);
956
0
    req->cb.recv = sock->recv_cb;
957
0
    req->cbarg = sock->recv_cbarg;
958
959
0
    req->uvbuf.base = (char *)extra_data.base;
960
0
    req->uvbuf.len = extra_data.length;
961
962
0
    isc_job_run(sock->worker->loop, &req->job,
963
0
          proxystream_read_extra_cb, req);
964
0
    return;
965
0
  }
966
967
0
  proxystream_read_start(sock);
968
0
}
969
970
static proxystream_send_req_t *
971
proxystream_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
972
       isc_nmhandle_t *proxyhandle, isc_nm_cb_t cb,
973
0
       void *cbarg) {
974
0
  proxystream_send_req_t *send_req = NULL;
975
976
0
  if (sock->proxy.send_req != NULL) {
977
    /*
978
     * We have a previously allocated object - let's use that.
979
     * That should help reducing stress on the memory allocator.
980
     */
981
0
    send_req = (proxystream_send_req_t *)sock->proxy.send_req;
982
0
    sock->proxy.send_req = NULL;
983
0
  } else {
984
    /* Allocate a new object. */
985
0
    send_req = isc_mem_get(mctx, sizeof(*send_req));
986
0
    *send_req = (proxystream_send_req_t){ 0 };
987
0
  }
988
989
  /* Initialise the send request object */
990
0
  send_req->cb = cb;
991
0
  send_req->cbarg = cbarg;
992
0
  isc_nmhandle_attach(proxyhandle, &send_req->proxyhandle);
993
994
0
  sock->proxy.nsending++;
995
996
0
  return send_req;
997
0
}
998
999
static void
1000
proxystream_put_send_req(isc_mem_t *mctx, proxystream_send_req_t *send_req,
1001
0
       const bool force_destroy) {
1002
  /*
1003
   * Attempt to put the object for reuse later if we are not
1004
   * wrapping up.
1005
   */
1006
0
  if (!force_destroy) {
1007
0
    isc_nmsocket_t *sock = send_req->proxyhandle->sock;
1008
0
    sock->proxy.nsending--;
1009
0
    isc_nmhandle_detach(&send_req->proxyhandle);
1010
0
    if (sock->proxy.send_req == NULL) {
1011
0
      sock->proxy.send_req = send_req;
1012
      /*
1013
       * An object has been recycled,
1014
       * if not - we are going to destroy it.
1015
       */
1016
0
      return;
1017
0
    }
1018
0
  }
1019
1020
0
  isc_mem_put(mctx, send_req, sizeof(*send_req));
1021
0
}
1022
1023
static void
1024
0
proxystream_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
1025
0
  proxystream_send_req_t *send_req = (proxystream_send_req_t *)cbarg;
1026
0
  isc_mem_t *mctx;
1027
0
  isc_nm_cb_t cb;
1028
0
  void *send_cbarg;
1029
0
  isc_nmhandle_t *proxyhandle = NULL;
1030
1031
0
  REQUIRE(VALID_NMHANDLE(handle));
1032
0
  REQUIRE(VALID_NMHANDLE(send_req->proxyhandle));
1033
0
  REQUIRE(VALID_NMSOCK(send_req->proxyhandle->sock));
1034
0
  REQUIRE(send_req->proxyhandle->sock->tid == isc_tid());
1035
1036
0
  mctx = send_req->proxyhandle->sock->worker->mctx;
1037
0
  cb = send_req->cb;
1038
0
  send_cbarg = send_req->cbarg;
1039
1040
0
  isc_nmhandle_attach(send_req->proxyhandle, &proxyhandle);
1041
  /* try to keep the send request object for reuse */
1042
0
  proxystream_put_send_req(mctx, send_req, false);
1043
0
  cb(proxyhandle, result, send_cbarg);
1044
0
  proxystream_try_close_unused(proxyhandle->sock);
1045
0
  isc_nmhandle_detach(&proxyhandle);
1046
0
}
1047
1048
static void
1049
proxystream_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
1050
0
     void *cbarg, const bool dnsmsg) {
1051
0
  isc_nmsocket_t *sock = NULL;
1052
0
  proxystream_send_req_t *send_req = NULL;
1053
0
  isc_result_t result = ISC_R_SUCCESS;
1054
0
  bool fail_async = true;
1055
1056
0
  REQUIRE(VALID_NMHANDLE(handle));
1057
0
  REQUIRE(VALID_NMSOCK(handle->sock));
1058
1059
0
  sock = handle->sock;
1060
1061
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
1062
1063
0
  if (isc__nm_closing(sock->worker)) {
1064
0
    result = ISC_R_SHUTTINGDOWN;
1065
0
    fail_async = false;
1066
0
  } else if (proxystream_closing(sock)) {
1067
0
    result = ISC_R_CANCELED;
1068
0
    fail_async = true;
1069
0
  }
1070
1071
0
  if (result != ISC_R_SUCCESS) {
1072
0
    isc__nm_uvreq_t *uvreq = isc__nm_uvreq_get(sock);
1073
0
    isc_nmhandle_attach(handle, &uvreq->handle);
1074
0
    uvreq->cb.send = cb;
1075
0
    uvreq->cbarg = cbarg;
1076
1077
0
    isc__nm_failed_send_cb(sock, uvreq, result, fail_async);
1078
0
    return;
1079
0
  }
1080
1081
0
  send_req = proxystream_get_send_req(sock->worker->mctx, sock, handle,
1082
0
              cb, cbarg);
1083
0
  if (dnsmsg) {
1084
0
    isc__nm_senddns(sock->outerhandle, region, proxystream_send_cb,
1085
0
        send_req);
1086
0
  } else {
1087
0
    isc_nm_send(sock->outerhandle, region, proxystream_send_cb,
1088
0
          send_req);
1089
0
  }
1090
0
}
1091
1092
void
1093
isc__nm_proxystream_send(isc_nmhandle_t *handle, isc_region_t *region,
1094
0
       isc_nm_cb_t cb, void *cbarg) {
1095
0
  proxystream_send(handle, region, cb, cbarg, false);
1096
0
}
1097
1098
void
1099
isc__nm_proxystream_senddns(isc_nmhandle_t *handle, isc_region_t *region,
1100
0
          isc_nm_cb_t cb, void *cbarg) {
1101
0
  proxystream_send(handle, region, cb, cbarg, true);
1102
0
}
1103
1104
void
1105
0
isc__nm_proxystream_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
1106
0
  REQUIRE(VALID_NMSOCK(listener));
1107
0
  REQUIRE(listener->type == isc_nm_proxystreamlistener);
1108
1109
0
  if (listener->outer != NULL) {
1110
0
    INSIST(VALID_NMSOCK(listener->outer));
1111
0
    isc_nmsocket_set_tlsctx(listener->outer, tlsctx);
1112
0
  }
1113
0
}
1114
1115
bool
1116
0
isc__nm_proxystream_has_encryption(const isc_nmhandle_t *handle) {
1117
0
  isc_nmsocket_t *sock = NULL;
1118
1119
0
  REQUIRE(VALID_NMHANDLE(handle));
1120
0
  REQUIRE(VALID_NMSOCK(handle->sock));
1121
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
1122
1123
0
  sock = handle->sock;
1124
0
  if (sock->outerhandle != NULL) {
1125
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
1126
0
    return isc_nm_has_encryption(sock->outerhandle);
1127
0
  }
1128
1129
0
  return false;
1130
0
}
1131
1132
const char *
1133
0
isc__nm_proxystream_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
1134
0
  isc_nmsocket_t *sock = NULL;
1135
1136
0
  REQUIRE(VALID_NMHANDLE(handle));
1137
0
  REQUIRE(VALID_NMSOCK(handle->sock));
1138
0
  REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
1139
1140
0
  sock = handle->sock;
1141
0
  if (sock->outerhandle != NULL) {
1142
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
1143
0
    return isc_nm_verify_tls_peer_result_string(sock->outerhandle);
1144
0
  }
1145
1146
0
  return NULL;
1147
0
}
1148
1149
void
1150
isc__nmhandle_proxystream_get_selected_alpn(isc_nmhandle_t *handle,
1151
              const unsigned char **alpn,
1152
0
              unsigned int *alpnlen) {
1153
0
  isc_nmsocket_t *sock;
1154
1155
0
  REQUIRE(VALID_NMHANDLE(handle));
1156
0
  sock = handle->sock;
1157
0
  REQUIRE(VALID_NMSOCK(sock));
1158
0
  REQUIRE(sock->type == isc_nm_proxystreamsocket);
1159
0
  REQUIRE(sock->tid == isc_tid());
1160
1161
0
  isc__nmhandle_get_selected_alpn(sock->outerhandle, alpn, alpnlen);
1162
0
}