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