Coverage Report

Created: 2026-01-24 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/netmgr/proxyudp.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 MP1 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
typedef struct proxyudp_send_req {
19
  isc_nm_cb_t cb;        /* send callback */
20
  void *cbarg;         /* send callback argument */
21
  isc_nmhandle_t *proxyhandle; /* socket handle */
22
  isc_buffer_t *outbuf; /* PROXY header followed by data (client only) */
23
} proxyudp_send_req_t;
24
25
static bool
26
proxyudp_closing(isc_nmsocket_t *sock);
27
28
static void
29
proxyudp_stop_reading(isc_nmsocket_t *sock);
30
31
static void
32
proxyudp_on_header_data_cb(const isc_result_t result,
33
         const isc_proxy2_command_t cmd, const int socktype,
34
         const isc_sockaddr_t *restrict src_addr,
35
         const isc_sockaddr_t *restrict dst_addr,
36
         const isc_region_t *restrict tlvs,
37
         const isc_region_t *restrict extra, void *cbarg);
38
39
static isc_nmsocket_t *
40
proxyudp_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type,
41
      isc_sockaddr_t *addr, const bool is_server);
42
43
static void
44
proxyudp_read_cb(isc_nmhandle_t *handle, isc_result_t result,
45
     isc_region_t *region, void *cbarg);
46
47
static void
48
proxyudp_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
49
       isc_result_t result);
50
51
static void
52
proxyudp_try_close_unused(isc_nmsocket_t *sock);
53
54
static void
55
proxyudp_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
56
57
static void
58
stop_proxyudp_child_job(void *arg);
59
60
static void
61
stop_proxyudp_child(isc_nmsocket_t *sock);
62
63
static void
64
proxyudp_clear_proxy_header_data(isc_nmsocket_t *sock);
65
66
static proxyudp_send_req_t *
67
proxyudp_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
68
          isc_nmhandle_t *proxyhandle, isc_region_t *client_data,
69
          isc_nm_cb_t cb, void *cbarg);
70
71
static void
72
proxyudp_put_send_req(isc_mem_t *mctx, proxyudp_send_req_t *send_req,
73
          const bool force_destroy);
74
75
static void
76
proxyudp_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
77
78
static bool
79
0
proxyudp_closing(isc_nmsocket_t *sock) {
80
0
  return isc__nmsocket_closing(sock) ||
81
0
         (sock->client && sock->outerhandle == NULL) ||
82
0
         (sock->outerhandle != NULL &&
83
0
    isc__nmsocket_closing(sock->outerhandle->sock));
84
0
}
85
86
static void
87
0
proxyudp_stop_reading(isc_nmsocket_t *sock) {
88
0
  isc__nmsocket_timer_stop(sock);
89
0
  if (sock->outerhandle != NULL) {
90
0
    isc__nm_stop_reading(sock->outerhandle->sock);
91
0
  }
92
0
}
93
94
void
95
isc__nm_proxyudp_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result,
96
0
        const bool async) {
97
0
  REQUIRE(VALID_NMSOCK(sock));
98
0
  REQUIRE(result != ISC_R_SUCCESS);
99
0
  REQUIRE(sock->tid == isc_tid());
100
101
  /*
102
   * For UDP server socket, we don't have child socket via
103
   * "accept", so we:
104
   * - we continue to read
105
   * - we don't clear the callbacks
106
   * - we don't destroy it (only stoplistening could do that)
107
   */
108
109
0
  if (sock->client) {
110
0
    proxyudp_stop_reading(sock);
111
0
  }
112
113
0
  if (sock->reading) {
114
0
    sock->reading = false;
115
116
0
    if (sock->recv_cb != NULL) {
117
0
      isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
118
0
      isc__nm_readcb(sock, req, result, async);
119
0
    }
120
0
  }
121
122
0
  if (sock->client) {
123
0
    isc__nmsocket_clearcb(sock);
124
0
    isc__nmsocket_prep_destroy(sock);
125
0
  }
126
0
}
127
128
static void
129
proxyudp_on_header_data_cb(const isc_result_t result,
130
         const isc_proxy2_command_t cmd, const int socktype,
131
         const isc_sockaddr_t *restrict src_addr,
132
         const isc_sockaddr_t *restrict dst_addr,
133
         const isc_region_t *restrict tlvs,
134
0
         const isc_region_t *restrict extra, void *cbarg) {
135
0
  isc_nmhandle_t *proxyhandle = (isc_nmhandle_t *)cbarg;
136
0
  isc_nmsocket_t *proxysock = proxyhandle->sock;
137
138
0
  if (result != ISC_R_SUCCESS) {
139
0
    isc__nm_proxyudp_failed_read_cb(proxysock, result, false);
140
0
    return;
141
0
  } else if (extra == NULL) {
142
    /* a PROXYv2 header with no data is unexpected */
143
0
    goto unexpected;
144
0
  }
145
146
  /* Process header data */
147
0
  if (cmd == ISC_PROXY2_CMD_LOCAL) {
148
0
    proxyhandle->proxy_is_unspec = true;
149
0
  } else if (cmd == ISC_PROXY2_CMD_PROXY) {
150
0
    switch (socktype) {
151
0
    case 0:
152
      /*
153
       * Treat unsupported addresses (aka AF_UNSPEC)
154
       * as LOCAL.
155
       */
156
0
      proxyhandle->proxy_is_unspec = true;
157
0
      break;
158
0
    case SOCK_STREAM:
159
      /*
160
       * In some cases proxies can do protocol conversion. In
161
       * this case, the original request might have arrived
162
       * over TCP-based transport and, thus, the PROXYv2
163
       * header can contain SOCK_STREAM, while for UDP one
164
       * would expect SOCK_DGRAM. That might be unexpected,
165
       * but, as the main idea behind PROXYv2 is to carry the
166
       * original endpoint information to back-ends, that is
167
       * fine.
168
       */
169
0
    case SOCK_DGRAM:
170
0
      INSIST(isc_sockaddr_pf(src_addr) ==
171
0
             isc_sockaddr_pf(dst_addr));
172
      /* We will treat AF_UNIX as unspec */
173
0
      if (isc_sockaddr_pf(src_addr) == AF_UNIX) {
174
0
        proxyhandle->proxy_is_unspec = true;
175
0
      } else {
176
0
        if (!isc__nm_valid_proxy_addresses(src_addr,
177
0
                   dst_addr))
178
0
        {
179
0
          goto unexpected;
180
0
        }
181
0
      }
182
0
      break;
183
0
    default:
184
0
      goto unexpected;
185
0
    }
186
0
  }
187
188
0
  if (!proxyhandle->proxy_is_unspec) {
189
0
    INSIST(src_addr != NULL);
190
0
    INSIST(dst_addr != NULL);
191
0
    proxyhandle->local = *dst_addr;
192
0
    proxyhandle->peer = *src_addr;
193
0
  }
194
195
0
  isc__nm_received_proxy_header_log(proxyhandle, cmd, socktype, src_addr,
196
0
            dst_addr, tlvs);
197
0
  proxysock->recv_cb(proxyhandle, result, (isc_region_t *)extra,
198
0
         proxysock->recv_cbarg);
199
0
  return;
200
201
0
unexpected:
202
0
  isc__nm_proxyudp_failed_read_cb(proxysock, ISC_R_UNEXPECTED, false);
203
0
}
204
205
static isc_nmsocket_t *
206
proxyudp_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type,
207
0
      isc_sockaddr_t *addr, const bool is_server) {
208
0
  isc_nmsocket_t *sock;
209
0
  INSIST(type == isc_nm_proxyudpsocket ||
210
0
         type == isc_nm_proxyudplistener);
211
212
0
  sock = isc_mempool_get(worker->nmsocket_pool);
213
0
  isc__nmsocket_init(sock, worker, type, addr, NULL);
214
0
  sock->result = ISC_R_UNSET;
215
0
  if (type == isc_nm_proxyudpsocket) {
216
0
    sock->read_timeout = isc_nm_getinitialtimeout();
217
0
    sock->client = !is_server;
218
0
    sock->connecting = !is_server;
219
0
    if (!is_server) {
220
0
      isc_buffer_allocate(worker->mctx,
221
0
              &sock->proxy.proxy2.outbuf,
222
0
              ISC_NM_PROXY2_DEFAULT_BUFFER_SIZE);
223
0
    }
224
0
  } else if (type == isc_nm_proxyudplistener) {
225
0
    size_t nworkers = isc_loopmgr_nloops();
226
0
    sock->proxy.udp_server_socks_num = nworkers;
227
0
    sock->proxy.udp_server_socks = isc_mem_cget(
228
0
      worker->mctx, nworkers, sizeof(isc_nmsocket_t *));
229
0
  }
230
231
0
  return sock;
232
0
}
233
234
static void
235
proxyudp_read_cb(isc_nmhandle_t *handle, isc_result_t result,
236
0
     isc_region_t *region, void *cbarg) {
237
0
  isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
238
0
  isc_nmsocket_t *proxysock = NULL;
239
240
0
  REQUIRE(VALID_NMSOCK(sock));
241
0
  REQUIRE(VALID_NMHANDLE(handle));
242
243
0
  if (sock->client) {
244
0
    proxysock = sock;
245
0
  } else {
246
0
    INSIST(sock->type == isc_nm_proxyudplistener);
247
0
    proxysock = sock->proxy.udp_server_socks[handle->sock->tid];
248
0
    if (proxysock->outerhandle == NULL) {
249
0
      isc_nmhandle_attach(handle, &proxysock->outerhandle);
250
0
    }
251
252
0
    proxysock->iface = isc_nmhandle_localaddr(handle);
253
0
    proxysock->peer = isc_nmhandle_peeraddr(handle);
254
0
  }
255
256
0
  INSIST(proxysock->tid == isc_tid());
257
258
0
  if (result != ISC_R_SUCCESS) {
259
0
    if (!proxysock->client) {
260
0
      goto failed;
261
0
    }
262
263
0
    if (result != ISC_R_TIMEDOUT) {
264
0
      goto failed;
265
0
    }
266
0
  }
267
268
0
  if (isc__nm_closing(proxysock->worker)) {
269
0
    result = ISC_R_SHUTTINGDOWN;
270
0
    goto failed;
271
0
  } else if (proxyudp_closing(proxysock)) {
272
0
    result = ISC_R_CANCELED;
273
0
    goto failed;
274
0
  }
275
276
  /* Handle initial PROXY header data */
277
0
  if (!proxysock->client) {
278
0
    isc_nmhandle_t *proxyhandle = NULL;
279
0
    proxysock->reading = false;
280
0
    proxyhandle = isc__nmhandle_get(proxysock, &proxysock->peer,
281
0
            &proxysock->iface);
282
0
    isc_nmhandle_attach(handle, &proxyhandle->proxy_udphandle);
283
0
    (void)isc_proxy2_header_handle_directly(
284
0
      region, proxyudp_on_header_data_cb, proxyhandle);
285
0
    isc_nmhandle_detach(&proxyhandle);
286
0
  } else {
287
0
    isc_nm_recv_cb_t recv_cb = NULL;
288
0
    void *recv_cbarg = NULL;
289
290
0
    recv_cb = proxysock->recv_cb;
291
0
    recv_cbarg = proxysock->recv_cbarg;
292
293
0
    if (result != ISC_R_TIMEDOUT) {
294
0
      proxysock->reading = false;
295
0
      proxyudp_stop_reading(proxysock);
296
0
    }
297
0
    recv_cb(proxysock->statichandle, result, region, recv_cbarg);
298
299
0
    if (result == ISC_R_TIMEDOUT &&
300
0
        !isc__nmsocket_timer_running(proxysock))
301
302
0
    {
303
0
      isc__nmsocket_clearcb(proxysock);
304
0
      goto failed;
305
0
    }
306
0
  }
307
308
0
  proxyudp_try_close_unused(proxysock);
309
310
0
  return;
311
312
0
failed:
313
0
  isc__nm_proxyudp_failed_read_cb(proxysock, result, false);
314
0
  return;
315
0
}
316
317
isc_result_t
318
isc_nm_listenproxyudp(uint32_t workers, isc_sockaddr_t *iface,
319
          isc_nm_recv_cb_t cb, void *cbarg,
320
0
          isc_nmsocket_t **sockp) {
321
0
  isc_result_t result;
322
0
  isc_nmsocket_t *listener = NULL;
323
0
  isc__networker_t *worker = isc__networker_current();
324
325
0
  REQUIRE(isc_tid() == 0);
326
0
  REQUIRE(sockp != NULL && *sockp == NULL);
327
328
0
  if (isc__nm_closing(worker)) {
329
0
    return ISC_R_SHUTTINGDOWN;
330
0
  }
331
332
0
  listener = proxyudp_sock_new(worker, isc_nm_proxyudplistener, iface,
333
0
             true);
334
0
  listener->recv_cb = cb;
335
0
  listener->recv_cbarg = cbarg;
336
337
0
  for (size_t i = 0; i < listener->proxy.udp_server_socks_num; i++) {
338
0
    listener->proxy.udp_server_socks[i] =
339
0
      proxyudp_sock_new(isc__networker_get(i),
340
0
            isc_nm_proxyudpsocket, iface, true);
341
342
0
    listener->proxy.udp_server_socks[i]->recv_cb =
343
0
      listener->recv_cb;
344
345
0
    listener->proxy.udp_server_socks[i]->recv_cbarg =
346
0
      listener->recv_cbarg;
347
348
0
    isc__nmsocket_attach(
349
0
      listener,
350
0
      &listener->proxy.udp_server_socks[i]->listener);
351
0
  }
352
353
0
  result = isc_nm_listenudp(workers, iface, proxyudp_read_cb, listener,
354
0
          &listener->outer);
355
356
0
  if (result == ISC_R_SUCCESS) {
357
0
    listener->active = true;
358
0
    listener->result = result;
359
0
    listener->nchildren = listener->outer->nchildren;
360
0
    *sockp = listener;
361
0
  } else {
362
0
    for (size_t i = 0; i < listener->proxy.udp_server_socks_num;
363
0
         i++)
364
0
    {
365
0
      stop_proxyudp_child(
366
0
        listener->proxy.udp_server_socks[i]);
367
0
    }
368
0
    listener->closed = true;
369
0
    isc__nmsocket_detach(&listener);
370
0
  }
371
372
0
  return result;
373
0
}
374
375
static void
376
proxyudp_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
377
0
       isc_result_t result) {
378
0
  sock->connecting = false;
379
0
  if (sock->connect_cb == NULL) {
380
0
    return;
381
0
  }
382
383
0
  sock->connect_cb(handle, result, sock->connect_cbarg);
384
0
  if (result != ISC_R_SUCCESS) {
385
0
    isc__nmsocket_clearcb(handle->sock);
386
0
  } else {
387
0
    sock->connected = true;
388
0
  }
389
0
}
390
391
static void
392
0
proxyudp_try_close_unused(isc_nmsocket_t *sock) {
393
  /* try to close unused socket */
394
0
  if (sock->statichandle == NULL && sock->proxy.nsending == 0) {
395
0
    if (sock->client) {
396
0
      isc__nmsocket_prep_destroy(sock);
397
0
    } else if (sock->outerhandle) {
398
0
      isc_nmhandle_detach(&sock->outerhandle);
399
0
    }
400
0
  }
401
0
}
402
403
static void
404
0
proxyudp_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
405
0
  isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
406
0
  isc_nmhandle_t *proxyhandle = NULL;
407
408
0
  REQUIRE(VALID_NMSOCK(sock));
409
410
0
  sock->tid = isc_tid();
411
412
0
  if (result != ISC_R_SUCCESS) {
413
0
    goto error;
414
0
  }
415
416
0
  INSIST(VALID_NMHANDLE(handle));
417
418
0
  sock->iface = isc_nmhandle_localaddr(handle);
419
0
  sock->peer = isc_nmhandle_peeraddr(handle);
420
0
  isc_nmhandle_attach(handle, &sock->outerhandle);
421
0
  handle->sock->proxy.sock = sock;
422
0
  sock->active = true;
423
0
  sock->connected = true;
424
0
  sock->connecting = false;
425
426
0
  proxyhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
427
0
  proxyudp_call_connect_cb(sock, proxyhandle, ISC_R_SUCCESS);
428
0
  isc_nmhandle_detach(&proxyhandle);
429
430
0
  proxyudp_try_close_unused(sock);
431
432
0
  isc__nmsocket_detach(&handle->sock->proxy.sock);
433
434
0
  return;
435
0
error:
436
0
  proxyhandle = isc__nmhandle_get(sock, NULL, NULL);
437
0
  sock->closed = true;
438
0
  proxyudp_call_connect_cb(sock, proxyhandle, result);
439
0
  isc_nmhandle_detach(&proxyhandle);
440
0
  isc__nmsocket_detach(&sock);
441
0
}
442
443
void
444
isc_nm_proxyudpconnect(isc_sockaddr_t *local, isc_sockaddr_t *peer,
445
           isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
446
0
           isc_nm_proxyheader_info_t *proxy_info) {
447
0
  isc_result_t result = ISC_R_FAILURE;
448
0
  isc_nmsocket_t *nsock = NULL;
449
0
  isc__networker_t *worker = isc__networker_current();
450
451
0
  if (isc__nm_closing(worker)) {
452
0
    cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
453
0
    return;
454
0
  }
455
456
0
  nsock = proxyudp_sock_new(worker, isc_nm_proxyudpsocket, local, false);
457
0
  nsock->connect_cb = cb;
458
0
  nsock->connect_cbarg = cbarg;
459
0
  nsock->read_timeout = timeout;
460
0
  nsock->connecting = true;
461
462
0
  if (proxy_info == NULL) {
463
0
    result = isc_proxy2_make_header(nsock->proxy.proxy2.outbuf,
464
0
            ISC_PROXY2_CMD_LOCAL, 0, NULL,
465
0
            NULL, NULL);
466
0
  } else if (proxy_info->complete) {
467
0
    isc_buffer_putmem(nsock->proxy.proxy2.outbuf,
468
0
          proxy_info->complete_header.base,
469
0
          proxy_info->complete_header.length);
470
0
    result = ISC_R_SUCCESS;
471
0
  } else if (!proxy_info->complete) {
472
0
    result = isc_proxy2_make_header(
473
0
      nsock->proxy.proxy2.outbuf, ISC_PROXY2_CMD_PROXY,
474
0
      SOCK_DGRAM, &proxy_info->proxy_info.src_addr,
475
0
      &proxy_info->proxy_info.dst_addr,
476
0
      &proxy_info->proxy_info.tlv_data);
477
0
  }
478
0
  RUNTIME_CHECK(result == ISC_R_SUCCESS);
479
480
0
  isc_nm_udpconnect(local, peer, proxyudp_connect_cb, nsock, timeout);
481
0
}
482
483
/*
484
 * Asynchronous 'udpstop' call handler: stop listening on a UDP socket.
485
 */
486
static void
487
0
stop_proxyudp_child_job(void *arg) {
488
0
  isc_nmsocket_t *listener = NULL;
489
0
  isc_nmsocket_t *sock = arg;
490
0
  isc_tid_t tid = 0;
491
492
0
  if (sock == NULL) {
493
0
    return;
494
0
  }
495
496
0
  INSIST(VALID_NMSOCK(sock));
497
0
  INSIST(sock->tid == isc_tid());
498
499
0
  listener = sock->listener;
500
0
  sock->listener = NULL;
501
502
0
  INSIST(VALID_NMSOCK(listener));
503
0
  INSIST(listener->type == isc_nm_proxyudplistener);
504
505
0
  if (sock->outerhandle != NULL) {
506
0
    proxyudp_stop_reading(sock);
507
0
    isc_nmhandle_detach(&sock->outerhandle);
508
0
  }
509
510
0
  tid = sock->tid;
511
0
  isc__nmsocket_prep_destroy(sock);
512
0
  isc__nmsocket_detach(&listener->proxy.udp_server_socks[tid]);
513
0
  isc__nmsocket_detach(&listener);
514
0
}
515
516
static void
517
0
stop_proxyudp_child(isc_nmsocket_t *sock) {
518
0
  REQUIRE(VALID_NMSOCK(sock));
519
520
0
  if (sock->tid == 0) {
521
0
    stop_proxyudp_child_job(sock);
522
0
  } else {
523
0
    isc_async_run(sock->worker->loop, stop_proxyudp_child_job,
524
0
            sock);
525
0
  }
526
0
}
527
528
void
529
0
isc__nm_proxyudp_stoplistening(isc_nmsocket_t *listener) {
530
0
  REQUIRE(VALID_NMSOCK(listener));
531
0
  REQUIRE(listener->type == isc_nm_proxyudplistener);
532
0
  REQUIRE(listener->proxy.sock == NULL);
533
534
0
  isc__nmsocket_stop(listener);
535
536
0
  listener->active = false;
537
538
0
  for (size_t i = 1; i < listener->proxy.udp_server_socks_num; i++) {
539
0
    stop_proxyudp_child(listener->proxy.udp_server_socks[i]);
540
0
  }
541
542
0
  stop_proxyudp_child(listener->proxy.udp_server_socks[0]);
543
0
}
544
545
static void
546
0
proxyudp_clear_proxy_header_data(isc_nmsocket_t *sock) {
547
0
  if (sock->client && sock->proxy.proxy2.outbuf != NULL) {
548
0
    isc_buffer_free(&sock->proxy.proxy2.outbuf);
549
0
  }
550
0
}
551
552
void
553
0
isc__nm_proxyudp_cleanup_data(isc_nmsocket_t *sock) {
554
0
  switch (sock->type) {
555
0
  case isc_nm_proxyudpsocket:
556
0
    if (sock->proxy.send_req != NULL) {
557
0
      proxyudp_put_send_req(sock->worker->mctx,
558
0
                sock->proxy.send_req, true);
559
0
    }
560
561
0
    proxyudp_clear_proxy_header_data(sock);
562
0
    break;
563
0
  case isc_nm_proxyudplistener:
564
0
    isc_mem_cput(sock->worker->mctx, sock->proxy.udp_server_socks,
565
0
           sock->proxy.udp_server_socks_num,
566
0
           sizeof(isc_nmsocket_t *));
567
0
    break;
568
0
  case isc_nm_udpsocket:
569
0
    INSIST(sock->proxy.sock == NULL);
570
0
    break;
571
0
  default:
572
0
    break;
573
0
  };
574
0
}
575
576
void
577
0
isc__nmhandle_proxyudp_cleartimeout(isc_nmhandle_t *handle) {
578
0
  isc_nmsocket_t *sock = NULL;
579
580
0
  REQUIRE(VALID_NMHANDLE(handle));
581
0
  REQUIRE(VALID_NMSOCK(handle->sock));
582
0
  REQUIRE(handle->sock->type == isc_nm_proxyudpsocket);
583
584
0
  sock = handle->sock;
585
0
  if (sock->outerhandle != NULL) {
586
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
587
0
    isc_nmhandle_cleartimeout(sock->outerhandle);
588
0
  }
589
0
}
590
591
void
592
0
isc__nmhandle_proxyudp_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
593
0
  isc_nmsocket_t *sock = NULL;
594
595
0
  REQUIRE(VALID_NMHANDLE(handle));
596
0
  REQUIRE(VALID_NMSOCK(handle->sock));
597
0
  REQUIRE(handle->sock->type == isc_nm_proxyudpsocket);
598
599
0
  sock = handle->sock;
600
0
  if (sock->outerhandle != NULL) {
601
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
602
0
    isc_nmhandle_settimeout(sock->outerhandle, timeout);
603
0
  }
604
0
}
605
606
void
607
isc__nmhandle_proxyudp_setwritetimeout(isc_nmhandle_t *handle,
608
0
               uint64_t write_timeout) {
609
0
  isc_nmsocket_t *sock = NULL;
610
611
0
  REQUIRE(VALID_NMHANDLE(handle));
612
0
  REQUIRE(VALID_NMSOCK(handle->sock));
613
0
  REQUIRE(handle->sock->type == isc_nm_proxyudpsocket);
614
615
0
  sock = handle->sock;
616
0
  if (sock->outerhandle != NULL) {
617
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
618
619
0
    isc_nmhandle_setwritetimeout(sock->outerhandle, write_timeout);
620
0
  }
621
0
}
622
623
bool
624
0
isc__nmsocket_proxyudp_timer_running(isc_nmsocket_t *sock) {
625
0
  REQUIRE(VALID_NMSOCK(sock));
626
0
  REQUIRE(sock->type == isc_nm_proxyudpsocket);
627
628
0
  if (sock->outerhandle != NULL) {
629
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
630
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
631
0
    return isc__nmsocket_timer_running(sock->outerhandle->sock);
632
0
  }
633
634
0
  return false;
635
0
}
636
637
void
638
0
isc__nmsocket_proxyudp_timer_restart(isc_nmsocket_t *sock) {
639
0
  REQUIRE(VALID_NMSOCK(sock));
640
0
  REQUIRE(sock->type == isc_nm_proxyudpsocket);
641
642
0
  if (sock->outerhandle != NULL) {
643
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
644
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
645
0
    isc__nmsocket_timer_restart(sock->outerhandle->sock);
646
0
  }
647
0
}
648
649
void
650
0
isc__nmsocket_proxyudp_timer_stop(isc_nmsocket_t *sock) {
651
0
  REQUIRE(VALID_NMSOCK(sock));
652
0
  REQUIRE(sock->type == isc_nm_proxyudpsocket);
653
654
0
  if (sock->outerhandle != NULL) {
655
0
    INSIST(VALID_NMHANDLE(sock->outerhandle));
656
0
    REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
657
0
    isc__nmsocket_timer_stop(sock->outerhandle->sock);
658
0
  }
659
0
}
660
661
void
662
0
isc__nm_proxyudp_close(isc_nmsocket_t *sock) {
663
0
  REQUIRE(VALID_NMSOCK(sock));
664
0
  REQUIRE(sock->type == isc_nm_proxyudpsocket);
665
0
  REQUIRE(sock->tid == isc_tid());
666
667
0
  sock->closing = true;
668
669
  /*
670
   * At this point we're certain that there are no
671
   * external references, we can close everything.
672
   */
673
0
  proxyudp_stop_reading(sock);
674
0
  sock->reading = false;
675
0
  if (sock->outerhandle != NULL) {
676
0
    isc_nmhandle_close(sock->outerhandle);
677
0
    isc_nmhandle_detach(&sock->outerhandle);
678
0
  }
679
680
0
  if (sock->proxy.sock != NULL) {
681
0
    isc__nmsocket_detach(&sock->proxy.sock);
682
0
  }
683
684
  /* Further cleanup performed in isc__nm_proxyudp_cleanup_data() */
685
0
  sock->closed = true;
686
0
  sock->active = false;
687
0
}
688
689
void
690
isc__nm_proxyudp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb,
691
0
          void *cbarg) {
692
0
  isc_nmsocket_t *sock = NULL;
693
0
  REQUIRE(VALID_NMHANDLE(handle));
694
0
  sock = handle->sock;
695
0
  REQUIRE(VALID_NMSOCK(sock));
696
0
  REQUIRE(sock->type == isc_nm_proxyudpsocket);
697
0
  REQUIRE(sock->recv_handle == NULL);
698
0
  REQUIRE(sock->tid == isc_tid());
699
700
0
  sock->recv_cb = cb;
701
0
  sock->recv_cbarg = cbarg;
702
0
  sock->reading = true;
703
704
0
  if (isc__nm_closing(sock->worker)) {
705
0
    isc__nm_proxyudp_failed_read_cb(sock, ISC_R_SHUTTINGDOWN,
706
0
            false);
707
0
    return;
708
0
  } else if (proxyudp_closing(sock)) {
709
0
    isc__nm_proxyudp_failed_read_cb(sock, ISC_R_CANCELED, true);
710
0
    return;
711
0
  }
712
713
0
  isc_nm_read(sock->outerhandle, proxyudp_read_cb, sock);
714
0
}
715
716
static proxyudp_send_req_t *
717
proxyudp_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
718
          isc_nmhandle_t *proxyhandle, isc_region_t *client_data,
719
0
          isc_nm_cb_t cb, void *cbarg) {
720
0
  proxyudp_send_req_t *send_req = NULL;
721
722
0
  if (sock->proxy.send_req != NULL) {
723
    /*
724
     * We have a previously allocated object - let's use that.
725
     * That should help reducing stress on the memory allocator.
726
     */
727
0
    send_req = (proxyudp_send_req_t *)sock->proxy.send_req;
728
0
    sock->proxy.send_req = NULL;
729
0
  } else {
730
    /* Allocate a new object. */
731
0
    send_req = isc_mem_get(mctx, sizeof(*send_req));
732
0
    *send_req = (proxyudp_send_req_t){ 0 };
733
0
  }
734
735
  /* Initialise the send request object */
736
0
  send_req->cb = cb;
737
0
  send_req->cbarg = cbarg;
738
0
  isc_nmhandle_attach(proxyhandle, &send_req->proxyhandle);
739
740
0
  if (client_data != NULL) {
741
0
    isc_region_t header_region = { 0 };
742
0
    INSIST(sock->client);
743
0
    INSIST(sock->proxy.proxy2.outbuf != NULL);
744
745
0
    isc_buffer_usedregion(sock->proxy.proxy2.outbuf,
746
0
              &header_region);
747
748
0
    INSIST(header_region.length > 0);
749
750
    /* allocate the buffer if it has not been allocated yet */
751
0
    if (send_req->outbuf == NULL) {
752
0
      isc_buffer_allocate(mctx, &send_req->outbuf,
753
0
              client_data->length +
754
0
                header_region.length);
755
0
    }
756
757
0
    isc_buffer_putmem(send_req->outbuf, header_region.base,
758
0
          header_region.length);
759
0
    isc_buffer_putmem(send_req->outbuf, client_data->base,
760
0
          client_data->length);
761
0
  }
762
763
0
  sock->proxy.nsending++;
764
765
0
  return send_req;
766
0
}
767
768
static void
769
proxyudp_put_send_req(isc_mem_t *mctx, proxyudp_send_req_t *send_req,
770
0
          const bool force_destroy) {
771
0
  if (send_req->outbuf != NULL) {
772
    /* clear the buffer to reuse it further */
773
0
    isc_buffer_clear(send_req->outbuf);
774
0
  }
775
  /*
776
   * Attempt to put the object for reuse later if we are not
777
   * wrapping up.
778
   */
779
0
  if (!force_destroy) {
780
0
    isc_nmsocket_t *sock = send_req->proxyhandle->sock;
781
0
    sock->proxy.nsending--;
782
0
    isc_nmhandle_detach(&send_req->proxyhandle);
783
0
    if (sock->proxy.send_req == NULL) {
784
0
      sock->proxy.send_req = send_req;
785
      /*
786
       * An object has been recycled,
787
       * if not - we are going to destroy it.
788
       */
789
0
      return;
790
0
    }
791
0
  } else {
792
0
    if (send_req->outbuf != NULL) {
793
0
      isc_buffer_free(&send_req->outbuf);
794
0
    }
795
0
  }
796
797
0
  isc_mem_put(mctx, send_req, sizeof(*send_req));
798
0
}
799
800
static void
801
0
proxyudp_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
802
0
  proxyudp_send_req_t *send_req = (proxyudp_send_req_t *)cbarg;
803
0
  isc_mem_t *mctx;
804
0
  isc_nm_cb_t cb;
805
0
  void *send_cbarg;
806
0
  isc_nmhandle_t *proxyhandle = NULL;
807
0
  isc_nmsocket_t *sock = NULL;
808
809
0
  REQUIRE(VALID_NMHANDLE(handle));
810
0
  REQUIRE(VALID_NMHANDLE(send_req->proxyhandle));
811
0
  REQUIRE(VALID_NMSOCK(send_req->proxyhandle->sock));
812
0
  REQUIRE(send_req->proxyhandle->sock->tid == isc_tid());
813
814
0
  mctx = send_req->proxyhandle->sock->worker->mctx;
815
0
  cb = send_req->cb;
816
0
  send_cbarg = send_req->cbarg;
817
818
0
  isc_nmhandle_attach(send_req->proxyhandle, &proxyhandle);
819
0
  isc__nmsocket_attach(proxyhandle->sock, &sock);
820
821
  /* try to keep the send request object for reuse */
822
0
  proxyudp_put_send_req(mctx, send_req, false);
823
0
  cb(proxyhandle, result, send_cbarg);
824
0
  isc_nmhandle_detach(&proxyhandle);
825
826
  /*
827
   * Try to close the client socket when we do not need it
828
   * anymore. In the case of server socket - detach the underlying
829
   * (UDP) handle when the socket is not being used anymore.
830
   */
831
0
  proxyudp_try_close_unused(sock);
832
0
  isc__nmsocket_detach(&sock);
833
0
}
834
835
void
836
isc__nm_proxyudp_send(isc_nmhandle_t *handle, isc_region_t *region,
837
0
          isc_nm_cb_t cb, void *cbarg) {
838
0
  isc_nmsocket_t *sock = NULL;
839
0
  proxyudp_send_req_t *send_req = NULL;
840
0
  isc_result_t result = ISC_R_SUCCESS;
841
842
0
  REQUIRE(VALID_NMHANDLE(handle));
843
0
  REQUIRE(VALID_NMSOCK(handle->sock));
844
845
0
  sock = handle->sock;
846
847
0
  REQUIRE(sock->type == isc_nm_proxyudpsocket);
848
849
0
  if (isc__nm_closing(sock->worker)) {
850
0
    result = ISC_R_SHUTTINGDOWN;
851
0
  } else if (proxyudp_closing(sock)) {
852
0
    result = ISC_R_CANCELED;
853
0
  }
854
855
0
  if (result != ISC_R_SUCCESS) {
856
0
    isc__nm_uvreq_t *uvreq = isc__nm_uvreq_get(sock);
857
0
    isc_nmhandle_attach(handle, &uvreq->handle);
858
0
    uvreq->cb.send = cb;
859
0
    uvreq->cbarg = cbarg;
860
861
0
    isc__nm_failed_send_cb(sock, uvreq, result, true);
862
0
    return;
863
0
  }
864
865
0
  send_req = proxyudp_get_send_req(sock->worker->mctx, sock, handle,
866
0
           sock->client ? region : NULL, cb,
867
0
           cbarg);
868
0
  if (sock->client) {
869
0
    isc_region_t send_data = { 0 };
870
0
    isc_buffer_usedregion(send_req->outbuf, &send_data);
871
0
    isc_nm_send(sock->outerhandle, &send_data, proxyudp_send_cb,
872
0
          send_req);
873
0
  } else {
874
0
    isc_nm_send(handle->proxy_udphandle, region, proxyudp_send_cb,
875
0
          send_req);
876
0
  }
877
0
}