2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-28 21:17:54 +00:00
bind/lib/isc/netmgr/proxyudp.c
Ondřej Surý 1032681af0 Convert the isc/tid.h to use own signed integer isc_tid_t type
Change the internal type used for isc_tid unit to isc_tid_t to hide the
specific integer type being used for the 'tid'.  Internally, the signed
integer type is being used.  This allows us to have negatively indexed
arrays that works both for threads with assigned tid and the threads
with unassigned tid.  This should be used only in specific situations.
2025-06-28 13:32:12 +02:00

882 lines
23 KiB
C

/*
* 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 <isc/netmgr.h>
#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) {
sock->read_timeout = isc_nm_getinitialtimeout(worker->netmgr);
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;
isc_tid_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);
}
}