/* $NetBSD: proxyudp.c,v 1.2 2025/01/26 16:25:43 christos Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MP1 was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ #include #include "netmgr-int.h" typedef struct proxyudp_send_req { isc_nm_cb_t cb; /* send callback */ void *cbarg; /* send callback argument */ isc_nmhandle_t *proxyhandle; /* socket handle */ isc_buffer_t *outbuf; /* PROXY header followed by data (client only) */ } proxyudp_send_req_t; static bool proxyudp_closing(isc_nmsocket_t *sock); static void proxyudp_stop_reading(isc_nmsocket_t *sock); static void proxyudp_on_header_data_cb(const isc_result_t result, const isc_proxy2_command_t cmd, const int socktype, const isc_sockaddr_t *restrict src_addr, const isc_sockaddr_t *restrict dst_addr, const isc_region_t *restrict tlvs, const isc_region_t *restrict extra, void *cbarg); static isc_nmsocket_t * proxyudp_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type, isc_sockaddr_t *addr, const bool is_server); static void proxyudp_read_cb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region, void *cbarg); static void proxyudp_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle, isc_result_t result); static void proxyudp_try_close_unused(isc_nmsocket_t *sock); static void proxyudp_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg); static void stop_proxyudp_child_job(void *arg); static void stop_proxyudp_child(isc_nmsocket_t *sock); static void proxyudp_clear_proxy_header_data(isc_nmsocket_t *sock); static proxyudp_send_req_t * proxyudp_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock, isc_nmhandle_t *proxyhandle, isc_region_t *client_data, isc_nm_cb_t cb, void *cbarg); static void proxyudp_put_send_req(isc_mem_t *mctx, proxyudp_send_req_t *send_req, const bool force_destroy); static void proxyudp_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg); static bool proxyudp_closing(isc_nmsocket_t *sock) { return isc__nmsocket_closing(sock) || (sock->client && sock->outerhandle == NULL) || (sock->outerhandle != NULL && isc__nmsocket_closing(sock->outerhandle->sock)); } static void proxyudp_stop_reading(isc_nmsocket_t *sock) { isc__nmsocket_timer_stop(sock); if (sock->outerhandle != NULL) { isc__nm_stop_reading(sock->outerhandle->sock); } } void isc__nm_proxyudp_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result, const bool async) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(result != ISC_R_SUCCESS); REQUIRE(sock->tid == isc_tid()); /* * For UDP server socket, we don't have child socket via * "accept", so we: * - we continue to read * - we don't clear the callbacks * - we don't destroy it (only stoplistening could do that) */ if (sock->client) { proxyudp_stop_reading(sock); } if (sock->reading) { sock->reading = false; if (sock->recv_cb != NULL) { isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL); isc__nm_readcb(sock, req, result, async); } } if (sock->client) { isc__nmsocket_clearcb(sock); isc__nmsocket_prep_destroy(sock); } } static void proxyudp_on_header_data_cb(const isc_result_t result, const isc_proxy2_command_t cmd, const int socktype, const isc_sockaddr_t *restrict src_addr, const isc_sockaddr_t *restrict dst_addr, const isc_region_t *restrict tlvs, const isc_region_t *restrict extra, void *cbarg) { isc_nmhandle_t *proxyhandle = (isc_nmhandle_t *)cbarg; isc_nmsocket_t *proxysock = proxyhandle->sock; if (result != ISC_R_SUCCESS) { isc__nm_proxyudp_failed_read_cb(proxysock, result, false); return; } else if (extra == NULL) { /* a PROXYv2 header with no data is unexpected */ goto unexpected; } /* Process header data */ if (cmd == ISC_PROXY2_CMD_LOCAL) { proxyhandle->proxy_is_unspec = true; } else if (cmd == ISC_PROXY2_CMD_PROXY) { switch (socktype) { case 0: /* * Treat unsupported addresses (aka AF_UNSPEC) * as LOCAL. */ proxyhandle->proxy_is_unspec = true; break; case SOCK_STREAM: /* * In some cases proxies can do protocol conversion. In * this case, the original request might have arrived * over TCP-based transport and, thus, the PROXYv2 * header can contain SOCK_STREAM, while for UDP one * would expect SOCK_DGRAM. That might be unexpected, * but, as the main idea behind PROXYv2 is to carry the * original endpoint information to back-ends, that is * fine. */ case SOCK_DGRAM: INSIST(isc_sockaddr_pf(src_addr) == isc_sockaddr_pf(dst_addr)); /* We will treat AF_UNIX as unspec */ if (isc_sockaddr_pf(src_addr) == AF_UNIX) { proxyhandle->proxy_is_unspec = true; } else { if (!isc__nm_valid_proxy_addresses(src_addr, dst_addr)) { goto unexpected; } } break; default: goto unexpected; } } if (!proxyhandle->proxy_is_unspec) { INSIST(src_addr != NULL); INSIST(dst_addr != NULL); proxyhandle->local = *dst_addr; proxyhandle->peer = *src_addr; } isc__nm_received_proxy_header_log(proxyhandle, cmd, socktype, src_addr, dst_addr, tlvs); proxysock->recv_cb(proxyhandle, result, (isc_region_t *)extra, proxysock->recv_cbarg); return; unexpected: isc__nm_proxyudp_failed_read_cb(proxysock, ISC_R_UNEXPECTED, false); } static isc_nmsocket_t * proxyudp_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type, isc_sockaddr_t *addr, const bool is_server) { isc_nmsocket_t *sock; INSIST(type == isc_nm_proxyudpsocket || type == isc_nm_proxyudplistener); sock = isc_mempool_get(worker->nmsocket_pool); isc__nmsocket_init(sock, worker, type, addr, NULL); sock->result = ISC_R_UNSET; if (type == isc_nm_proxyudpsocket) { uint32_t initial = 0; isc_nm_gettimeouts(worker->netmgr, &initial, NULL, NULL, NULL); sock->read_timeout = initial; sock->client = !is_server; sock->connecting = !is_server; if (!is_server) { isc_buffer_allocate(worker->mctx, &sock->proxy.proxy2.outbuf, ISC_NM_PROXY2_DEFAULT_BUFFER_SIZE); } } else if (type == isc_nm_proxyudplistener) { size_t nworkers = worker->netmgr->nloops; sock->proxy.udp_server_socks_num = nworkers; sock->proxy.udp_server_socks = isc_mem_cget( worker->mctx, nworkers, sizeof(isc_nmsocket_t *)); } return sock; } static void proxyudp_read_cb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region, void *cbarg) { isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; isc_nmsocket_t *proxysock = NULL; REQUIRE(VALID_NMSOCK(sock)); REQUIRE(VALID_NMHANDLE(handle)); if (sock->client) { proxysock = sock; } else { INSIST(sock->type == isc_nm_proxyudplistener); proxysock = sock->proxy.udp_server_socks[handle->sock->tid]; if (proxysock->outerhandle == NULL) { isc_nmhandle_attach(handle, &proxysock->outerhandle); } proxysock->iface = isc_nmhandle_localaddr(handle); proxysock->peer = isc_nmhandle_peeraddr(handle); } INSIST(proxysock->tid == isc_tid()); if (result != ISC_R_SUCCESS) { if (!proxysock->client) { goto failed; } if (result != ISC_R_TIMEDOUT) { goto failed; } } if (isc__nm_closing(proxysock->worker)) { result = ISC_R_SHUTTINGDOWN; goto failed; } else if (proxyudp_closing(proxysock)) { result = ISC_R_CANCELED; goto failed; } /* Handle initial PROXY header data */ if (!proxysock->client) { isc_nmhandle_t *proxyhandle = NULL; proxysock->reading = false; proxyhandle = isc__nmhandle_get(proxysock, &proxysock->peer, &proxysock->iface); isc_nmhandle_attach(handle, &proxyhandle->proxy_udphandle); (void)isc_proxy2_header_handle_directly( region, proxyudp_on_header_data_cb, proxyhandle); isc_nmhandle_detach(&proxyhandle); } else { isc_nm_recv_cb_t recv_cb = NULL; void *recv_cbarg = NULL; recv_cb = proxysock->recv_cb; recv_cbarg = proxysock->recv_cbarg; if (result != ISC_R_TIMEDOUT) { proxysock->reading = false; proxyudp_stop_reading(proxysock); } recv_cb(proxysock->statichandle, result, region, recv_cbarg); if (result == ISC_R_TIMEDOUT && !isc__nmsocket_timer_running(proxysock)) { isc__nmsocket_clearcb(proxysock); goto failed; } } proxyudp_try_close_unused(proxysock); return; failed: isc__nm_proxyudp_failed_read_cb(proxysock, result, false); return; } isc_result_t isc_nm_listenproxyudp(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb, void *cbarg, isc_nmsocket_t **sockp) { isc_result_t result; isc_nmsocket_t *listener = NULL; isc__networker_t *worker = &mgr->workers[isc_tid()]; REQUIRE(VALID_NM(mgr)); REQUIRE(isc_tid() == 0); REQUIRE(sockp != NULL && *sockp == NULL); if (isc__nm_closing(worker)) { return ISC_R_SHUTTINGDOWN; } listener = proxyudp_sock_new(worker, isc_nm_proxyudplistener, iface, true); listener->recv_cb = cb; listener->recv_cbarg = cbarg; for (size_t i = 0; i < listener->proxy.udp_server_socks_num; i++) { listener->proxy.udp_server_socks[i] = proxyudp_sock_new( &mgr->workers[i], isc_nm_proxyudpsocket, iface, true); listener->proxy.udp_server_socks[i]->recv_cb = listener->recv_cb; listener->proxy.udp_server_socks[i]->recv_cbarg = listener->recv_cbarg; isc__nmsocket_attach( listener, &listener->proxy.udp_server_socks[i]->listener); } result = isc_nm_listenudp(mgr, workers, iface, proxyudp_read_cb, listener, &listener->outer); if (result == ISC_R_SUCCESS) { listener->active = true; listener->result = result; listener->nchildren = listener->outer->nchildren; *sockp = listener; } else { for (size_t i = 0; i < listener->proxy.udp_server_socks_num; i++) { stop_proxyudp_child( listener->proxy.udp_server_socks[i]); } listener->closed = true; isc__nmsocket_detach(&listener); } return result; } static void proxyudp_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle, isc_result_t result) { sock->connecting = false; if (sock->connect_cb == NULL) { return; } sock->connect_cb(handle, result, sock->connect_cbarg); if (result != ISC_R_SUCCESS) { isc__nmsocket_clearcb(handle->sock); } else { sock->connected = true; } } static void proxyudp_try_close_unused(isc_nmsocket_t *sock) { /* try to close unused socket */ if (sock->statichandle == NULL && sock->proxy.nsending == 0) { if (sock->client) { isc__nmsocket_prep_destroy(sock); } else if (sock->outerhandle) { isc_nmhandle_detach(&sock->outerhandle); } } } static void proxyudp_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; isc_nmhandle_t *proxyhandle = NULL; REQUIRE(VALID_NMSOCK(sock)); sock->tid = isc_tid(); if (result != ISC_R_SUCCESS) { goto error; } INSIST(VALID_NMHANDLE(handle)); sock->iface = isc_nmhandle_localaddr(handle); sock->peer = isc_nmhandle_peeraddr(handle); isc_nmhandle_attach(handle, &sock->outerhandle); handle->sock->proxy.sock = sock; sock->active = true; sock->connected = true; sock->connecting = false; proxyhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); proxyudp_call_connect_cb(sock, proxyhandle, ISC_R_SUCCESS); isc_nmhandle_detach(&proxyhandle); proxyudp_try_close_unused(sock); isc__nmsocket_detach(&handle->sock->proxy.sock); return; error: proxyhandle = isc__nmhandle_get(sock, NULL, NULL); sock->closed = true; proxyudp_call_connect_cb(sock, proxyhandle, result); isc_nmhandle_detach(&proxyhandle); isc__nmsocket_detach(&sock); } void isc_nm_proxyudpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, unsigned int timeout, isc_nm_proxyheader_info_t *proxy_info) { isc_result_t result = ISC_R_FAILURE; isc_nmsocket_t *nsock = NULL; isc__networker_t *worker = &mgr->workers[isc_tid()]; REQUIRE(VALID_NM(mgr)); if (isc__nm_closing(worker)) { cb(NULL, ISC_R_SHUTTINGDOWN, cbarg); return; } nsock = proxyudp_sock_new(worker, isc_nm_proxyudpsocket, local, false); nsock->connect_cb = cb; nsock->connect_cbarg = cbarg; nsock->read_timeout = timeout; nsock->connecting = true; if (proxy_info == NULL) { result = isc_proxy2_make_header(nsock->proxy.proxy2.outbuf, ISC_PROXY2_CMD_LOCAL, 0, NULL, NULL, NULL); } else if (proxy_info->complete) { isc_buffer_putmem(nsock->proxy.proxy2.outbuf, proxy_info->complete_header.base, proxy_info->complete_header.length); result = ISC_R_SUCCESS; } else if (!proxy_info->complete) { result = isc_proxy2_make_header( nsock->proxy.proxy2.outbuf, ISC_PROXY2_CMD_PROXY, SOCK_DGRAM, &proxy_info->proxy_info.src_addr, &proxy_info->proxy_info.dst_addr, &proxy_info->proxy_info.tlv_data); } RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_nm_udpconnect(mgr, local, peer, proxyudp_connect_cb, nsock, timeout); } /* * Asynchronous 'udpstop' call handler: stop listening on a UDP socket. */ static void stop_proxyudp_child_job(void *arg) { isc_nmsocket_t *listener = NULL; isc_nmsocket_t *sock = arg; uint32_t tid = 0; if (sock == NULL) { return; } INSIST(VALID_NMSOCK(sock)); INSIST(sock->tid == isc_tid()); listener = sock->listener; sock->listener = NULL; INSIST(VALID_NMSOCK(listener)); INSIST(listener->type == isc_nm_proxyudplistener); if (sock->outerhandle != NULL) { proxyudp_stop_reading(sock); isc_nmhandle_detach(&sock->outerhandle); } tid = sock->tid; isc__nmsocket_prep_destroy(sock); isc__nmsocket_detach(&listener->proxy.udp_server_socks[tid]); isc__nmsocket_detach(&listener); } static void stop_proxyudp_child(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); if (sock->tid == 0) { stop_proxyudp_child_job(sock); } else { isc_async_run(sock->worker->loop, stop_proxyudp_child_job, sock); } } void isc__nm_proxyudp_stoplistening(isc_nmsocket_t *listener) { REQUIRE(VALID_NMSOCK(listener)); REQUIRE(listener->type == isc_nm_proxyudplistener); REQUIRE(listener->proxy.sock == NULL); isc__nmsocket_stop(listener); listener->active = false; for (size_t i = 1; i < listener->proxy.udp_server_socks_num; i++) { stop_proxyudp_child(listener->proxy.udp_server_socks[i]); } stop_proxyudp_child(listener->proxy.udp_server_socks[0]); } static void proxyudp_clear_proxy_header_data(isc_nmsocket_t *sock) { if (sock->client && sock->proxy.proxy2.outbuf != NULL) { isc_buffer_free(&sock->proxy.proxy2.outbuf); } } void isc__nm_proxyudp_cleanup_data(isc_nmsocket_t *sock) { switch (sock->type) { case isc_nm_proxyudpsocket: if (sock->proxy.send_req != NULL) { proxyudp_put_send_req(sock->worker->mctx, sock->proxy.send_req, true); } proxyudp_clear_proxy_header_data(sock); break; case isc_nm_proxyudplistener: isc_mem_cput(sock->worker->mctx, sock->proxy.udp_server_socks, sock->proxy.udp_server_socks_num, sizeof(isc_nmsocket_t *)); break; case isc_nm_udpsocket: INSIST(sock->proxy.sock == NULL); break; default: break; }; } void isc__nmhandle_proxyudp_cleartimeout(isc_nmhandle_t *handle) { isc_nmsocket_t *sock = NULL; REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); REQUIRE(handle->sock->type == isc_nm_proxyudpsocket); sock = handle->sock; if (sock->outerhandle != NULL) { INSIST(VALID_NMHANDLE(sock->outerhandle)); isc_nmhandle_cleartimeout(sock->outerhandle); } } void isc__nmhandle_proxyudp_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { isc_nmsocket_t *sock = NULL; REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); REQUIRE(handle->sock->type == isc_nm_proxyudpsocket); sock = handle->sock; if (sock->outerhandle != NULL) { INSIST(VALID_NMHANDLE(sock->outerhandle)); isc_nmhandle_settimeout(sock->outerhandle, timeout); } } void isc__nmhandle_proxyudp_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) { isc_nmsocket_t *sock = NULL; REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); REQUIRE(handle->sock->type == isc_nm_proxyudpsocket); sock = handle->sock; if (sock->outerhandle != NULL) { INSIST(VALID_NMHANDLE(sock->outerhandle)); isc_nmhandle_setwritetimeout(sock->outerhandle, write_timeout); } } bool isc__nmsocket_proxyudp_timer_running(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_proxyudpsocket); if (sock->outerhandle != NULL) { INSIST(VALID_NMHANDLE(sock->outerhandle)); REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); return isc__nmsocket_timer_running(sock->outerhandle->sock); } return false; } void isc__nmsocket_proxyudp_timer_restart(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_proxyudpsocket); if (sock->outerhandle != NULL) { INSIST(VALID_NMHANDLE(sock->outerhandle)); REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); isc__nmsocket_timer_restart(sock->outerhandle->sock); } } void isc__nmsocket_proxyudp_timer_stop(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_proxyudpsocket); if (sock->outerhandle != NULL) { INSIST(VALID_NMHANDLE(sock->outerhandle)); REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); isc__nmsocket_timer_stop(sock->outerhandle->sock); } } void isc__nm_proxyudp_close(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_proxyudpsocket); REQUIRE(sock->tid == isc_tid()); sock->closing = true; /* * At this point we're certain that there are no * external references, we can close everything. */ proxyudp_stop_reading(sock); sock->reading = false; if (sock->outerhandle != NULL) { isc_nmhandle_close(sock->outerhandle); isc_nmhandle_detach(&sock->outerhandle); } if (sock->proxy.sock != NULL) { isc__nmsocket_detach(&sock->proxy.sock); } /* Further cleanup performed in isc__nm_proxyudp_cleanup_data() */ sock->closed = true; sock->active = false; } void isc__nm_proxyudp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { isc_nmsocket_t *sock = NULL; REQUIRE(VALID_NMHANDLE(handle)); sock = handle->sock; REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_proxyudpsocket); REQUIRE(sock->recv_handle == NULL); REQUIRE(sock->tid == isc_tid()); sock->recv_cb = cb; sock->recv_cbarg = cbarg; sock->reading = true; if (isc__nm_closing(sock->worker)) { isc__nm_proxyudp_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false); return; } else if (proxyudp_closing(sock)) { isc__nm_proxyudp_failed_read_cb(sock, ISC_R_CANCELED, true); return; } isc_nm_read(sock->outerhandle, proxyudp_read_cb, sock); } static proxyudp_send_req_t * proxyudp_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock, isc_nmhandle_t *proxyhandle, isc_region_t *client_data, isc_nm_cb_t cb, void *cbarg) { proxyudp_send_req_t *send_req = NULL; if (sock->proxy.send_req != NULL) { /* * We have a previously allocated object - let's use that. * That should help reducing stress on the memory allocator. */ send_req = (proxyudp_send_req_t *)sock->proxy.send_req; sock->proxy.send_req = NULL; } else { /* Allocate a new object. */ send_req = isc_mem_get(mctx, sizeof(*send_req)); *send_req = (proxyudp_send_req_t){ 0 }; } /* Initialise the send request object */ send_req->cb = cb; send_req->cbarg = cbarg; isc_nmhandle_attach(proxyhandle, &send_req->proxyhandle); if (client_data != NULL) { isc_region_t header_region = { 0 }; INSIST(sock->client); INSIST(sock->proxy.proxy2.outbuf != NULL); isc_buffer_usedregion(sock->proxy.proxy2.outbuf, &header_region); INSIST(header_region.length > 0); /* allocate the buffer if it has not been allocated yet */ if (send_req->outbuf == NULL) { isc_buffer_allocate(mctx, &send_req->outbuf, client_data->length + header_region.length); } isc_buffer_putmem(send_req->outbuf, header_region.base, header_region.length); isc_buffer_putmem(send_req->outbuf, client_data->base, client_data->length); } sock->proxy.nsending++; return send_req; } static void proxyudp_put_send_req(isc_mem_t *mctx, proxyudp_send_req_t *send_req, const bool force_destroy) { if (send_req->outbuf != NULL) { /* clear the buffer to reuse it further */ isc_buffer_clear(send_req->outbuf); } /* * Attempt to put the object for reuse later if we are not * wrapping up. */ if (!force_destroy) { isc_nmsocket_t *sock = send_req->proxyhandle->sock; sock->proxy.nsending--; isc_nmhandle_detach(&send_req->proxyhandle); if (sock->proxy.send_req == NULL) { sock->proxy.send_req = send_req; /* * An object has been recycled, * if not - we are going to destroy it. */ return; } } else { if (send_req->outbuf != NULL) { isc_buffer_free(&send_req->outbuf); } } isc_mem_put(mctx, send_req, sizeof(*send_req)); } static void proxyudp_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { proxyudp_send_req_t *send_req = (proxyudp_send_req_t *)cbarg; isc_mem_t *mctx; isc_nm_cb_t cb; void *send_cbarg; isc_nmhandle_t *proxyhandle = NULL; isc_nmsocket_t *sock = NULL; REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMHANDLE(send_req->proxyhandle)); REQUIRE(VALID_NMSOCK(send_req->proxyhandle->sock)); REQUIRE(send_req->proxyhandle->sock->tid == isc_tid()); mctx = send_req->proxyhandle->sock->worker->mctx; cb = send_req->cb; send_cbarg = send_req->cbarg; isc_nmhandle_attach(send_req->proxyhandle, &proxyhandle); isc__nmsocket_attach(proxyhandle->sock, &sock); /* try to keep the send request object for reuse */ proxyudp_put_send_req(mctx, send_req, false); cb(proxyhandle, result, send_cbarg); isc_nmhandle_detach(&proxyhandle); /* * Try to close the client socket when we do not need it * anymore. In the case of server socket - detach the underlying * (UDP) handle when the socket is not being used anymore. */ proxyudp_try_close_unused(sock); isc__nmsocket_detach(&sock); } void isc__nm_proxyudp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, void *cbarg) { isc_nmsocket_t *sock = NULL; proxyudp_send_req_t *send_req = NULL; isc_result_t result = ISC_R_SUCCESS; REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); sock = handle->sock; REQUIRE(sock->type == isc_nm_proxyudpsocket); if (isc__nm_closing(sock->worker)) { result = ISC_R_SHUTTINGDOWN; } else if (proxyudp_closing(sock)) { result = ISC_R_CANCELED; } if (result != ISC_R_SUCCESS) { isc__nm_uvreq_t *uvreq = isc__nm_uvreq_get(sock); isc_nmhandle_attach(handle, &uvreq->handle); uvreq->cb.send = cb; uvreq->cbarg = cbarg; isc__nm_failed_send_cb(sock, uvreq, result, true); return; } send_req = proxyudp_get_send_req(sock->worker->mctx, sock, handle, (sock->client ? region : NULL), cb, cbarg); if (sock->client) { isc_region_t send_data = { 0 }; isc_buffer_usedregion(send_req->outbuf, &send_data); isc_nm_send(sock->outerhandle, &send_data, proxyudp_send_cb, send_req); } else { isc_nm_send(handle->proxy_udphandle, region, proxyudp_send_cb, send_req); } }