Home | History | Annotate | Line # | Download | only in netmgr
      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