Home | History | Annotate | Line # | Download | only in netmgr
      1 /*	$NetBSD: proxystream.c,v 1.3 2025/05/21 14:48:05 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 MPL 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 /*
     21  * The idea behind the transport is simple after accepting the
     22  * connection or connecting to a remote server it enters PROXYv2
     23  * handling mode: that is, it either attempts to read (when accepting
     24  * the connection) or send (when establishing a connection) a PROXYv2
     25  * header. After that it works like a mere wrapper on top of the
     26  * underlying stream-based transport (TCP).
     27  */
     28 
     29 typedef struct proxystream_send_req {
     30 	isc_nm_cb_t cb;		     /* send callback */
     31 	void *cbarg;		     /* send callback argument */
     32 	isc_nmhandle_t *proxyhandle; /* PROXY Stream socket handle */
     33 } proxystream_send_req_t;
     34 
     35 static void
     36 proxystream_on_header_data_cb(const isc_result_t result,
     37 			      const isc_proxy2_command_t cmd,
     38 			      const int socktype,
     39 			      const isc_sockaddr_t *restrict src_addr,
     40 			      const isc_sockaddr_t *restrict dst_addr,
     41 			      const isc_region_t *restrict tlv_blob,
     42 			      const isc_region_t *restrict extra, void *cbarg);
     43 
     44 static isc_nmsocket_t *
     45 proxystream_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type,
     46 		     isc_sockaddr_t *addr, const bool is_server);
     47 
     48 static isc_result_t
     49 proxystream_accept_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
     50 
     51 static void
     52 proxystream_connect_cb(isc_nmhandle_t *handle, isc_result_t result,
     53 		       void *cbarg);
     54 
     55 static void
     56 proxystream_failed_read_cb_async(void *arg);
     57 
     58 static void
     59 proxystream_clear_proxy_header_data(isc_nmsocket_t *sock);
     60 
     61 static void
     62 proxystream_read_start(isc_nmsocket_t *sock);
     63 
     64 static void
     65 proxystream_read_stop(isc_nmsocket_t *sock);
     66 
     67 static void
     68 proxystream_try_close_unused(isc_nmsocket_t *sock);
     69 
     70 static void
     71 proxystream_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
     72 			    isc_result_t result);
     73 
     74 static bool
     75 proxystream_closing(isc_nmsocket_t *sock);
     76 
     77 static void
     78 proxystream_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result);
     79 
     80 static void
     81 proxystream_read_cb(isc_nmhandle_t *handle, isc_result_t result,
     82 		    isc_region_t *region, void *cbarg);
     83 
     84 static void
     85 proxystream_read_extra_cb(void *arg);
     86 
     87 static proxystream_send_req_t *
     88 proxystream_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
     89 			 isc_nmhandle_t *proxyhandle, isc_nm_cb_t cb,
     90 			 void *cbarg);
     91 
     92 static void
     93 proxystream_put_send_req(isc_mem_t *mctx, proxystream_send_req_t *send_req,
     94 			 const bool force_destroy);
     95 
     96 static void
     97 proxystream_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
     98 
     99 static void
    100 proxystream_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
    101 		 void *cbarg, const bool dnsmsg);
    102 
    103 static void
    104 proxystream_on_header_data_cb(const isc_result_t result,
    105 			      const isc_proxy2_command_t cmd,
    106 			      const int socktype,
    107 			      const isc_sockaddr_t *restrict src_addr,
    108 			      const isc_sockaddr_t *restrict dst_addr,
    109 			      const isc_region_t *restrict tlvs,
    110 			      const isc_region_t *restrict extra, void *cbarg) {
    111 	isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
    112 
    113 	switch (result) {
    114 	case ISC_R_SUCCESS: {
    115 		isc_nmhandle_t *proxyhandle = NULL;
    116 		isc_result_t accept_result = ISC_R_FAILURE;
    117 		bool call_accept = false;
    118 		bool is_unspec = false;
    119 
    120 		/*
    121 		 * After header has been processed - stop reading (thus,
    122 		 * stopping the timer) and disable manual timer control as in
    123 		 * the case of TCP it is disabled by default
    124 		 */
    125 		proxystream_read_stop(sock);
    126 		isc__nmsocket_timer_stop(sock);
    127 		isc__nmhandle_set_manual_timer(sock->outerhandle, false);
    128 
    129 		sock->proxy.header_processed = true;
    130 		if (extra == NULL) {
    131 			sock->proxy.extra_processed = true;
    132 		}
    133 
    134 		/* Process header data */
    135 		if (cmd == ISC_PROXY2_CMD_LOCAL) {
    136 			is_unspec = true;
    137 			call_accept = true;
    138 		} else if (cmd == ISC_PROXY2_CMD_PROXY) {
    139 			switch (socktype) {
    140 			case 0:
    141 				/*
    142 				 * Treat unsupported addresses (aka AF_UNSPEC)
    143 				 * as LOCAL.
    144 				 */
    145 				is_unspec = true;
    146 				call_accept = true;
    147 				break;
    148 			case SOCK_DGRAM:
    149 				/*
    150 				 * In some cases proxies can do protocol
    151 				 * conversion. In this case, the original
    152 				 * request might have arrived over UDP-based
    153 				 * transport and, thus, the PROXYv2 header can
    154 				 * contain SOCK_DGRAM, while for TCP one would
    155 				 * expect SOCK_STREAM. That might be unexpected,
    156 				 * but, as the main idea behind PROXYv2 is to
    157 				 * carry the original endpoint information to
    158 				 * back-ends, that is fine.
    159 				 *
    160 				 * At least "dnsdist" does that when redirecting
    161 				 * a UDP request to a TCP or TLS-only server.
    162 				 */
    163 			case SOCK_STREAM:
    164 				INSIST(isc_sockaddr_pf(src_addr) ==
    165 				       isc_sockaddr_pf(dst_addr));
    166 				/* We will treat AF_UNIX as unspec */
    167 				if (isc_sockaddr_pf(src_addr) == AF_UNIX) {
    168 					is_unspec = true;
    169 				}
    170 
    171 				if (!is_unspec &&
    172 				    !isc__nm_valid_proxy_addresses(src_addr,
    173 								   dst_addr))
    174 				{
    175 					break;
    176 				}
    177 
    178 				call_accept = true;
    179 				break;
    180 			default:
    181 				break;
    182 			}
    183 		}
    184 
    185 		if (call_accept) {
    186 			if (is_unspec) {
    187 				proxyhandle = isc__nmhandle_get(
    188 					sock, &sock->peer, &sock->iface);
    189 			} else {
    190 				INSIST(src_addr != NULL);
    191 				INSIST(dst_addr != NULL);
    192 				proxyhandle = isc__nmhandle_get(sock, src_addr,
    193 								dst_addr);
    194 			}
    195 			proxyhandle->proxy_is_unspec = is_unspec;
    196 			isc__nm_received_proxy_header_log(proxyhandle, cmd,
    197 							  socktype, src_addr,
    198 							  dst_addr, tlvs);
    199 			accept_result = sock->accept_cb(proxyhandle, result,
    200 							sock->accept_cbarg);
    201 			isc_nmhandle_detach(&proxyhandle);
    202 		}
    203 
    204 		if (accept_result != ISC_R_SUCCESS) {
    205 			isc__nmsocket_detach(&sock->listener);
    206 			isc_nmhandle_detach(&sock->outerhandle);
    207 			sock->closed = true;
    208 		}
    209 
    210 		sock->accepting = false;
    211 
    212 		proxystream_try_close_unused(sock);
    213 	} break;
    214 	case ISC_R_NOMORE:
    215 		/*
    216 		 * That is fine, wait for more data to complete the PROXY
    217 		 * header
    218 		 */
    219 		break;
    220 	default:
    221 		proxystream_failed_read_cb(sock, result);
    222 		break;
    223 	};
    224 }
    225 
    226 static void
    227 proxystream_handle_incoming_header_data(isc_nmsocket_t *sock,
    228 					isc_region_t *restrict data) {
    229 	isc_proxy2_handler_t *restrict handler = sock->proxy.proxy2.handler;
    230 
    231 	(void)isc_proxy2_handler_push(handler, data);
    232 	proxystream_try_close_unused(sock);
    233 }
    234 
    235 static isc_nmsocket_t *
    236 proxystream_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type,
    237 		     isc_sockaddr_t *addr, const bool is_server) {
    238 	isc_nmsocket_t *sock;
    239 	INSIST(type == isc_nm_proxystreamsocket ||
    240 	       type == isc_nm_proxystreamlistener);
    241 
    242 	sock = isc_mempool_get(worker->nmsocket_pool);
    243 	isc__nmsocket_init(sock, worker, type, addr, NULL);
    244 	sock->result = ISC_R_UNSET;
    245 	if (type == isc_nm_proxystreamsocket) {
    246 		uint32_t initial = 0;
    247 		isc_nm_gettimeouts(worker->netmgr, &initial, NULL, NULL, NULL);
    248 		sock->read_timeout = initial;
    249 		sock->client = !is_server;
    250 		sock->connecting = !is_server;
    251 		if (is_server) {
    252 			/*
    253 			 * Smallest TCP (over IPv6) segment size we required to
    254 			 * support. An adequate value for both IPv4 and IPv6.
    255 			 */
    256 			sock->proxy.proxy2.handler = isc_proxy2_handler_new(
    257 				worker->mctx, NM_MAXSEG,
    258 				proxystream_on_header_data_cb, sock);
    259 		} else {
    260 			isc_buffer_allocate(worker->mctx,
    261 					    &sock->proxy.proxy2.outbuf,
    262 					    ISC_NM_PROXY2_DEFAULT_BUFFER_SIZE);
    263 		}
    264 	}
    265 
    266 	return sock;
    267 }
    268 
    269 static isc_result_t
    270 proxystream_accept_cb(isc_nmhandle_t *handle, isc_result_t result,
    271 		      void *cbarg) {
    272 	isc_nmsocket_t *listensock = (isc_nmsocket_t *)cbarg;
    273 	isc_nmsocket_t *nsock = NULL;
    274 	isc_sockaddr_t iface;
    275 
    276 	if (result != ISC_R_SUCCESS) {
    277 		return result;
    278 	}
    279 
    280 	INSIST(VALID_NMHANDLE(handle));
    281 	INSIST(VALID_NMSOCK(handle->sock));
    282 	INSIST(VALID_NMSOCK(listensock));
    283 	INSIST(listensock->type == isc_nm_proxystreamlistener);
    284 
    285 	if (isc__nm_closing(handle->sock->worker)) {
    286 		return ISC_R_SHUTTINGDOWN;
    287 	} else if (isc__nmsocket_closing(handle->sock)) {
    288 		return ISC_R_CANCELED;
    289 	}
    290 
    291 	iface = isc_nmhandle_localaddr(handle);
    292 	nsock = proxystream_sock_new(handle->sock->worker,
    293 				     isc_nm_proxystreamsocket, &iface, true);
    294 	INSIST(listensock->accept_cb != NULL);
    295 	nsock->accept_cb = listensock->accept_cb;
    296 	nsock->accept_cbarg = listensock->accept_cbarg;
    297 
    298 	nsock->peer = isc_nmhandle_peeraddr(handle);
    299 	nsock->tid = isc_tid();
    300 	nsock->accepting = true;
    301 	nsock->active = true;
    302 
    303 	isc__nmsocket_attach(listensock, &nsock->listener);
    304 	isc_nmhandle_attach(handle, &nsock->outerhandle);
    305 	handle->sock->proxy.sock = nsock;
    306 
    307 	/*
    308 	 * We need to control the timer manually as we do *not* want it to
    309 	 * be reset on partial header data reads.
    310 	 */
    311 	isc__nmhandle_set_manual_timer(nsock->outerhandle, true);
    312 	isc__nmsocket_timer_restart(nsock);
    313 
    314 	proxystream_read_start(nsock);
    315 
    316 	return ISC_R_SUCCESS;
    317 }
    318 
    319 isc_result_t
    320 isc_nm_listenproxystream(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
    321 			 isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
    322 			 int backlog, isc_quota_t *quota, isc_tlsctx_t *tlsctx,
    323 			 isc_nmsocket_t **sockp) {
    324 	isc_result_t result;
    325 	isc_nmsocket_t *listener = NULL;
    326 	isc__networker_t *worker = &mgr->workers[isc_tid()];
    327 
    328 	REQUIRE(VALID_NM(mgr));
    329 	REQUIRE(isc_tid() == 0);
    330 	REQUIRE(sockp != NULL && *sockp == NULL);
    331 
    332 	if (isc__nm_closing(worker)) {
    333 		return ISC_R_SHUTTINGDOWN;
    334 	}
    335 
    336 	listener = proxystream_sock_new(worker, isc_nm_proxystreamlistener,
    337 					iface, true);
    338 	listener->accept_cb = accept_cb;
    339 	listener->accept_cbarg = accept_cbarg;
    340 
    341 	if (tlsctx == NULL) {
    342 		result = isc_nm_listentcp(mgr, workers, iface,
    343 					  proxystream_accept_cb, listener,
    344 					  backlog, quota, &listener->outer);
    345 	} else {
    346 		result = isc_nm_listentls(
    347 			mgr, workers, iface, proxystream_accept_cb, listener,
    348 			backlog, quota, tlsctx, false, &listener->outer);
    349 	}
    350 
    351 	if (result != ISC_R_SUCCESS) {
    352 		listener->closed = true;
    353 		isc__nmsocket_detach(&listener);
    354 		return result;
    355 	}
    356 
    357 	listener->active = true;
    358 	listener->result = result;
    359 	listener->nchildren = listener->outer->nchildren;
    360 
    361 	*sockp = listener;
    362 
    363 	return result;
    364 }
    365 
    366 static void
    367 proxystream_try_close_unused(isc_nmsocket_t *sock) {
    368 	/* try to close unused socket */
    369 	if (sock->statichandle == NULL && sock->proxy.nsending == 0) {
    370 		isc__nmsocket_prep_destroy(sock);
    371 	}
    372 }
    373 
    374 static void
    375 proxystream_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
    376 			    isc_result_t result) {
    377 	sock->connecting = false;
    378 	if (sock->connect_cb == NULL) {
    379 		return;
    380 	}
    381 
    382 	if (result == ISC_R_SUCCESS) {
    383 		sock->connected = true;
    384 	}
    385 
    386 	sock->connect_cb(handle, result, sock->connect_cbarg);
    387 	if (result != ISC_R_SUCCESS) {
    388 		isc__nmsocket_clearcb(handle->sock);
    389 	}
    390 }
    391 
    392 static void
    393 proxystream_send_header_cb(isc_nmhandle_t *transphandle, isc_result_t result,
    394 			   void *cbarg) {
    395 	isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
    396 	isc_nmhandle_t *proxyhandle = NULL;
    397 
    398 	REQUIRE(VALID_NMHANDLE(transphandle));
    399 	REQUIRE(VALID_NMSOCK(sock));
    400 
    401 	sock->proxy.nsending--;
    402 	sock->proxy.header_processed = true;
    403 
    404 	if (isc__nm_closing(transphandle->sock->worker)) {
    405 		result = ISC_R_SHUTTINGDOWN;
    406 	}
    407 
    408 	proxyhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
    409 	proxystream_call_connect_cb(sock, proxyhandle, result);
    410 	isc_nmhandle_detach(&proxyhandle);
    411 
    412 	proxystream_try_close_unused(sock);
    413 }
    414 
    415 static void
    416 proxystream_connect_cb(isc_nmhandle_t *handle, isc_result_t result,
    417 		       void *cbarg) {
    418 	isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
    419 	isc_nmhandle_t *proxyhandle = NULL;
    420 	isc_region_t header = { 0 };
    421 
    422 	REQUIRE(VALID_NMSOCK(sock));
    423 
    424 	sock->tid = isc_tid();
    425 
    426 	if (result != ISC_R_SUCCESS) {
    427 		goto error;
    428 	}
    429 
    430 	INSIST(VALID_NMHANDLE(handle));
    431 
    432 	sock->iface = isc_nmhandle_localaddr(handle);
    433 	sock->peer = isc_nmhandle_peeraddr(handle);
    434 	if (isc__nm_closing(handle->sock->worker)) {
    435 		result = ISC_R_SHUTTINGDOWN;
    436 		goto error;
    437 	} else if (isc__nmsocket_closing(handle->sock)) {
    438 		result = ISC_R_CANCELED;
    439 		goto error;
    440 	}
    441 
    442 	isc_nmhandle_attach(handle, &sock->outerhandle);
    443 	handle->sock->proxy.sock = sock;
    444 	sock->active = true;
    445 
    446 	isc_buffer_usedregion(sock->proxy.proxy2.outbuf, &header);
    447 	sock->proxy.nsending++;
    448 	isc_nm_send(handle, &header, proxystream_send_header_cb, sock);
    449 
    450 	proxystream_try_close_unused(sock);
    451 
    452 	return;
    453 error:
    454 	proxyhandle = isc__nmhandle_get(sock, NULL, NULL);
    455 	sock->closed = true;
    456 	proxystream_call_connect_cb(sock, proxyhandle, result);
    457 	isc_nmhandle_detach(&proxyhandle);
    458 	isc__nmsocket_detach(&sock);
    459 }
    460 
    461 void
    462 isc_nm_proxystreamconnect(isc_nm_t *mgr, isc_sockaddr_t *local,
    463 			  isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg,
    464 			  unsigned int timeout, isc_tlsctx_t *tlsctx,
    465 			  const char *sni_hostname,
    466 			  isc_tlsctx_client_session_cache_t *client_sess_cache,
    467 			  isc_nm_proxyheader_info_t *proxy_info) {
    468 	isc_result_t result = ISC_R_FAILURE;
    469 	isc_nmsocket_t *nsock = NULL;
    470 	isc__networker_t *worker = &mgr->workers[isc_tid()];
    471 
    472 	REQUIRE(VALID_NM(mgr));
    473 
    474 	if (isc__nm_closing(worker)) {
    475 		cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
    476 		return;
    477 	}
    478 
    479 	nsock = proxystream_sock_new(worker, isc_nm_proxystreamsocket, local,
    480 				     false);
    481 	nsock->connect_cb = cb;
    482 	nsock->connect_cbarg = cbarg;
    483 	nsock->connect_timeout = timeout;
    484 
    485 	if (proxy_info == NULL) {
    486 		result = isc_proxy2_make_header(nsock->proxy.proxy2.outbuf,
    487 						ISC_PROXY2_CMD_LOCAL, 0, NULL,
    488 						NULL, NULL);
    489 	} else if (proxy_info->complete) {
    490 		isc_buffer_putmem(nsock->proxy.proxy2.outbuf,
    491 				  proxy_info->complete_header.base,
    492 				  proxy_info->complete_header.length);
    493 		result = ISC_R_SUCCESS;
    494 	} else if (!proxy_info->complete) {
    495 		result = isc_proxy2_make_header(
    496 			nsock->proxy.proxy2.outbuf, ISC_PROXY2_CMD_PROXY,
    497 			SOCK_STREAM, &proxy_info->proxy_info.src_addr,
    498 			&proxy_info->proxy_info.dst_addr,
    499 			&proxy_info->proxy_info.tlv_data);
    500 	}
    501 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    502 
    503 	if (tlsctx == NULL) {
    504 		isc_nm_tcpconnect(mgr, local, peer, proxystream_connect_cb,
    505 				  nsock, nsock->connect_timeout);
    506 	} else {
    507 		isc_nm_tlsconnect(mgr, local, peer, proxystream_connect_cb,
    508 				  nsock, tlsctx, sni_hostname,
    509 				  client_sess_cache, nsock->connect_timeout,
    510 				  false, NULL);
    511 	}
    512 }
    513 
    514 static void
    515 proxystream_failed_read_cb_async(void *arg) {
    516 	isc__nm_uvreq_t *req = (isc__nm_uvreq_t *)arg;
    517 
    518 	proxystream_failed_read_cb(req->sock, req->result);
    519 	isc__nm_uvreq_put(&req);
    520 }
    521 
    522 void
    523 isc__nm_proxystream_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
    524 				   bool async) {
    525 	proxystream_read_stop(sock);
    526 
    527 	if (!async) {
    528 		proxystream_failed_read_cb(sock, result);
    529 	} else {
    530 		isc__nm_uvreq_t *req = isc__nm_uvreq_get(sock);
    531 		req->result = result;
    532 		req->cbarg = sock;
    533 		isc_job_run(sock->worker->loop, &req->job,
    534 			    proxystream_failed_read_cb_async, req);
    535 	}
    536 }
    537 
    538 void
    539 isc__nm_proxystream_stoplistening(isc_nmsocket_t *sock) {
    540 	REQUIRE(VALID_NMSOCK(sock));
    541 	REQUIRE(sock->type == isc_nm_proxystreamlistener);
    542 	REQUIRE(sock->proxy.sock == NULL);
    543 
    544 	isc__nmsocket_stop(sock);
    545 }
    546 
    547 static void
    548 proxystream_clear_proxy_header_data(isc_nmsocket_t *sock) {
    549 	if (!sock->client && sock->proxy.proxy2.handler != NULL) {
    550 		isc_proxy2_handler_free(&sock->proxy.proxy2.handler);
    551 	} else if (sock->client && sock->proxy.proxy2.outbuf != NULL) {
    552 		isc_buffer_free(&sock->proxy.proxy2.outbuf);
    553 	}
    554 }
    555 
    556 void
    557 isc__nm_proxystream_cleanup_data(isc_nmsocket_t *sock) {
    558 	switch (sock->type) {
    559 	case isc_nm_tcpsocket:
    560 	case isc_nm_tlssocket:
    561 		if (sock->proxy.sock != NULL) {
    562 			isc__nmsocket_detach(&sock->proxy.sock);
    563 		}
    564 		break;
    565 	case isc_nm_proxystreamsocket:
    566 		if (sock->proxy.send_req != NULL) {
    567 			proxystream_put_send_req(
    568 				sock->worker->mctx,
    569 				(proxystream_send_req_t *)sock->proxy.send_req,
    570 				true);
    571 		}
    572 
    573 		proxystream_clear_proxy_header_data(sock);
    574 		break;
    575 	default:
    576 		break;
    577 	};
    578 }
    579 
    580 void
    581 isc__nmhandle_proxystream_cleartimeout(isc_nmhandle_t *handle) {
    582 	isc_nmsocket_t *sock = NULL;
    583 
    584 	REQUIRE(VALID_NMHANDLE(handle));
    585 	REQUIRE(VALID_NMSOCK(handle->sock));
    586 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    587 
    588 	sock = handle->sock;
    589 	if (sock->outerhandle != NULL) {
    590 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    591 		isc_nmhandle_cleartimeout(sock->outerhandle);
    592 	}
    593 }
    594 
    595 void
    596 isc__nmhandle_proxystream_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
    597 	isc_nmsocket_t *sock = NULL;
    598 
    599 	REQUIRE(VALID_NMHANDLE(handle));
    600 	REQUIRE(VALID_NMSOCK(handle->sock));
    601 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    602 
    603 	sock = handle->sock;
    604 	if (sock->outerhandle != NULL) {
    605 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    606 		isc_nmhandle_settimeout(sock->outerhandle, timeout);
    607 	}
    608 }
    609 
    610 void
    611 isc__nmhandle_proxystream_keepalive(isc_nmhandle_t *handle, bool value) {
    612 	isc_nmsocket_t *sock = NULL;
    613 
    614 	REQUIRE(VALID_NMHANDLE(handle));
    615 	REQUIRE(VALID_NMSOCK(handle->sock));
    616 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    617 
    618 	sock = handle->sock;
    619 	if (sock->outerhandle != NULL) {
    620 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    621 
    622 		isc_nmhandle_keepalive(sock->outerhandle, value);
    623 	}
    624 }
    625 
    626 void
    627 isc__nmhandle_proxystream_setwritetimeout(isc_nmhandle_t *handle,
    628 					  uint64_t write_timeout) {
    629 	isc_nmsocket_t *sock = NULL;
    630 
    631 	REQUIRE(VALID_NMHANDLE(handle));
    632 	REQUIRE(VALID_NMSOCK(handle->sock));
    633 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    634 
    635 	sock = handle->sock;
    636 	if (sock->outerhandle != NULL) {
    637 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    638 
    639 		isc_nmhandle_setwritetimeout(sock->outerhandle, write_timeout);
    640 	}
    641 }
    642 
    643 void
    644 isc__nmsocket_proxystream_reset(isc_nmsocket_t *sock) {
    645 	REQUIRE(VALID_NMSOCK(sock));
    646 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
    647 
    648 	if (sock->outerhandle != NULL) {
    649 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    650 		REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
    651 		isc__nmsocket_reset(sock->outerhandle->sock);
    652 	}
    653 }
    654 
    655 bool
    656 isc__nmsocket_proxystream_timer_running(isc_nmsocket_t *sock) {
    657 	REQUIRE(VALID_NMSOCK(sock));
    658 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
    659 
    660 	if (sock->outerhandle != NULL) {
    661 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    662 		REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
    663 		return isc__nmsocket_timer_running(sock->outerhandle->sock);
    664 	}
    665 
    666 	return false;
    667 }
    668 
    669 void
    670 isc__nmsocket_proxystream_timer_restart(isc_nmsocket_t *sock) {
    671 	REQUIRE(VALID_NMSOCK(sock));
    672 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
    673 
    674 	if (sock->outerhandle != NULL) {
    675 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    676 		REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
    677 		isc__nmsocket_timer_restart(sock->outerhandle->sock);
    678 	}
    679 }
    680 
    681 void
    682 isc__nmsocket_proxystream_timer_stop(isc_nmsocket_t *sock) {
    683 	REQUIRE(VALID_NMSOCK(sock));
    684 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
    685 
    686 	if (sock->outerhandle != NULL) {
    687 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    688 		REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
    689 		isc__nmsocket_timer_stop(sock->outerhandle->sock);
    690 	}
    691 }
    692 
    693 void
    694 isc__nmhandle_proxystream_set_manual_timer(isc_nmhandle_t *handle,
    695 					   const bool manual) {
    696 	isc_nmsocket_t *sock = NULL;
    697 
    698 	REQUIRE(VALID_NMHANDLE(handle));
    699 	REQUIRE(VALID_NMSOCK(handle->sock));
    700 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    701 
    702 	sock = handle->sock;
    703 	if (sock->outerhandle != NULL) {
    704 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    705 
    706 		isc__nmhandle_set_manual_timer(sock->outerhandle, manual);
    707 	}
    708 }
    709 
    710 isc_result_t
    711 isc__nmhandle_proxystream_set_tcp_nodelay(isc_nmhandle_t *handle,
    712 					  const bool value) {
    713 	isc_nmsocket_t *sock = NULL;
    714 	isc_result_t result = ISC_R_FAILURE;
    715 
    716 	REQUIRE(VALID_NMHANDLE(handle));
    717 	REQUIRE(VALID_NMSOCK(handle->sock));
    718 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    719 
    720 	sock = handle->sock;
    721 	if (sock->outerhandle != NULL) {
    722 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    723 
    724 		result = isc_nmhandle_set_tcp_nodelay(sock->outerhandle, value);
    725 	}
    726 
    727 	return result;
    728 }
    729 
    730 static void
    731 proxystream_read_start(isc_nmsocket_t *sock) {
    732 	if (sock->proxy.reading == true) {
    733 		return;
    734 	}
    735 
    736 	sock->proxy.reading = true;
    737 	if (sock->outerhandle != NULL) {
    738 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    739 
    740 		isc_nm_read(sock->outerhandle, proxystream_read_cb, sock);
    741 	}
    742 }
    743 
    744 static void
    745 proxystream_read_stop(isc_nmsocket_t *sock) {
    746 	if (sock->proxy.reading == false) {
    747 		return;
    748 	}
    749 
    750 	sock->proxy.reading = false;
    751 	if (sock->outerhandle != NULL) {
    752 		INSIST(VALID_NMHANDLE(sock->outerhandle));
    753 
    754 		isc_nm_read_stop(sock->outerhandle);
    755 	}
    756 }
    757 
    758 void
    759 isc__nm_proxystream_read_stop(isc_nmhandle_t *handle) {
    760 	REQUIRE(VALID_NMHANDLE(handle));
    761 	REQUIRE(VALID_NMSOCK(handle->sock));
    762 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
    763 
    764 	handle->sock->reading = false;
    765 	proxystream_read_stop(handle->sock);
    766 }
    767 
    768 void
    769 isc__nm_proxystream_close(isc_nmsocket_t *sock) {
    770 	REQUIRE(VALID_NMSOCK(sock));
    771 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
    772 	REQUIRE(sock->tid == isc_tid());
    773 
    774 	sock->closing = true;
    775 
    776 	/*
    777 	 * At this point we're certain that there are no
    778 	 * external references, we can close everything.
    779 	 */
    780 	proxystream_read_stop(sock);
    781 	isc__nmsocket_timer_stop(sock);
    782 	if (sock->outerhandle != NULL) {
    783 		sock->reading = false;
    784 		isc_nm_read_stop(sock->outerhandle);
    785 		isc_nmhandle_close(sock->outerhandle);
    786 		isc_nmhandle_detach(&sock->outerhandle);
    787 	}
    788 
    789 	if (sock->listener != NULL) {
    790 		isc__nmsocket_detach(&sock->listener);
    791 	}
    792 
    793 	/* Further cleanup performed in isc__nm_proxystream_cleanup_data() */
    794 	sock->closed = true;
    795 	sock->active = false;
    796 }
    797 
    798 static bool
    799 proxystream_closing(isc_nmsocket_t *sock) {
    800 	return isc__nmsocket_closing(sock) || sock->outerhandle == NULL ||
    801 	       (sock->outerhandle != NULL &&
    802 		isc__nmsocket_closing(sock->outerhandle->sock));
    803 }
    804 
    805 static void
    806 proxystream_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result) {
    807 	REQUIRE(VALID_NMSOCK(sock));
    808 	REQUIRE(result != ISC_R_SUCCESS);
    809 
    810 	if (sock->client && sock->connect_cb != NULL && !sock->connected) {
    811 		isc_nmhandle_t *handle = NULL;
    812 		INSIST(sock->statichandle == NULL);
    813 		handle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
    814 		proxystream_call_connect_cb(sock, handle, result);
    815 		isc__nmsocket_clearcb(sock);
    816 		isc_nmhandle_detach(&handle);
    817 
    818 		isc__nmsocket_prep_destroy(sock);
    819 		return;
    820 	}
    821 
    822 	isc__nmsocket_timer_stop(sock);
    823 
    824 	if (sock->statichandle == NULL) {
    825 		isc__nmsocket_prep_destroy(sock);
    826 		return;
    827 	}
    828 
    829 	/* See isc__nmsocket_readtimeout_cb() */
    830 	if (sock->client && result == ISC_R_TIMEDOUT) {
    831 		if (sock->recv_cb != NULL) {
    832 			isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
    833 			isc__nm_readcb(sock, req, result, false);
    834 		}
    835 
    836 		if (isc__nmsocket_timer_running(sock)) {
    837 			/* Timer was restarted, bail-out */
    838 			return;
    839 		}
    840 
    841 		isc__nmsocket_clearcb(sock);
    842 
    843 		isc__nmsocket_prep_destroy(sock);
    844 		return;
    845 	}
    846 
    847 	if (sock->recv_cb != NULL) {
    848 		isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
    849 		isc__nmsocket_clearcb(sock);
    850 		isc__nm_readcb(sock, req, result, false);
    851 	}
    852 
    853 	isc__nmsocket_prep_destroy(sock);
    854 }
    855 
    856 static void
    857 proxystream_read_cb(isc_nmhandle_t *handle, isc_result_t result,
    858 		    isc_region_t *region, void *cbarg) {
    859 	isc_nmsocket_t *proxysock = (isc_nmsocket_t *)cbarg;
    860 
    861 	REQUIRE(VALID_NMSOCK(proxysock));
    862 	REQUIRE(VALID_NMHANDLE(handle));
    863 	REQUIRE(proxysock->tid == isc_tid());
    864 
    865 	if (result != ISC_R_SUCCESS) {
    866 		goto failed;
    867 
    868 	} else if (isc__nm_closing(proxysock->worker)) {
    869 		result = ISC_R_SHUTTINGDOWN;
    870 		goto failed;
    871 	} else if (isc__nmsocket_closing(handle->sock)) {
    872 		result = ISC_R_CANCELED;
    873 		goto failed;
    874 	}
    875 
    876 	/* Handle initial PROXY header data */
    877 	if (!proxysock->client && !proxysock->proxy.header_processed) {
    878 		proxystream_handle_incoming_header_data(proxysock, region);
    879 		return;
    880 	}
    881 
    882 	proxysock->recv_cb(proxysock->statichandle, ISC_R_SUCCESS, region,
    883 			   proxysock->recv_cbarg);
    884 
    885 	proxystream_try_close_unused(proxysock);
    886 
    887 	return;
    888 failed:
    889 	proxystream_failed_read_cb(proxysock, result);
    890 }
    891 
    892 static void
    893 proxystream_read_extra_cb(void *arg) {
    894 	isc_result_t result = ISC_R_SUCCESS;
    895 	isc__nm_uvreq_t *req = arg;
    896 	isc_region_t extra_data = { 0 }; /* data past PROXY header */
    897 
    898 	REQUIRE(VALID_UVREQ(req));
    899 
    900 	isc_nmsocket_t *sock = req->sock;
    901 
    902 	REQUIRE(VALID_NMSOCK(sock));
    903 	REQUIRE(sock->tid == isc_tid());
    904 
    905 	sock->proxy.extra_processed = true;
    906 
    907 	if (isc__nm_closing(sock->worker)) {
    908 		result = ISC_R_SHUTTINGDOWN;
    909 	} else if (proxystream_closing(sock)) {
    910 		result = ISC_R_CANCELED;
    911 	}
    912 
    913 	if (result == ISC_R_SUCCESS) {
    914 		extra_data.base = (uint8_t *)req->uvbuf.base;
    915 		extra_data.length = req->uvbuf.len;
    916 
    917 		INSIST(extra_data.length > 0);
    918 
    919 		req->cb.recv(req->handle, result, &extra_data, req->cbarg);
    920 
    921 		if (sock->reading) {
    922 			proxystream_read_start(sock);
    923 		}
    924 	} else {
    925 		isc__nm_proxystream_failed_read_cb(sock, result, false);
    926 	}
    927 
    928 	isc__nm_uvreq_put(&req);
    929 }
    930 
    931 void
    932 isc__nm_proxystream_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb,
    933 			 void *cbarg) {
    934 	isc_nmsocket_t *sock = NULL;
    935 	isc_region_t extra_data = { 0 }; /* data past PROXY header */
    936 
    937 	REQUIRE(VALID_NMHANDLE(handle));
    938 	sock = handle->sock;
    939 	REQUIRE(VALID_NMSOCK(sock));
    940 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
    941 	REQUIRE(sock->recv_handle == NULL);
    942 	REQUIRE(sock->tid == isc_tid());
    943 
    944 	sock->recv_cb = cb;
    945 	sock->recv_cbarg = cbarg;
    946 	sock->reading = true;
    947 
    948 	if (isc__nm_closing(sock->worker)) {
    949 		isc__nm_proxystream_failed_read_cb(sock, ISC_R_SHUTTINGDOWN,
    950 						   false);
    951 		return;
    952 	} else if (proxystream_closing(sock)) {
    953 		isc__nm_proxystream_failed_read_cb(sock, ISC_R_CANCELED, true);
    954 		return;
    955 	}
    956 
    957 	/* check if there is extra data on the server */
    958 	if (!sock->client && sock->proxy.header_processed &&
    959 	    !sock->proxy.extra_processed &&
    960 	    isc_proxy2_handler_extra(sock->proxy.proxy2.handler, &extra_data) >
    961 		    0)
    962 	{
    963 		isc__nm_uvreq_t *req = isc__nm_uvreq_get(sock);
    964 		isc_nmhandle_attach(handle, &req->handle);
    965 		req->cb.recv = sock->recv_cb;
    966 		req->cbarg = sock->recv_cbarg;
    967 
    968 		req->uvbuf.base = (char *)extra_data.base;
    969 		req->uvbuf.len = extra_data.length;
    970 
    971 		isc_job_run(sock->worker->loop, &req->job,
    972 			    proxystream_read_extra_cb, req);
    973 		return;
    974 	}
    975 
    976 	proxystream_read_start(sock);
    977 }
    978 
    979 static proxystream_send_req_t *
    980 proxystream_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
    981 			 isc_nmhandle_t *proxyhandle, isc_nm_cb_t cb,
    982 			 void *cbarg) {
    983 	proxystream_send_req_t *send_req = NULL;
    984 
    985 	if (sock->proxy.send_req != NULL) {
    986 		/*
    987 		 * We have a previously allocated object - let's use that.
    988 		 * That should help reducing stress on the memory allocator.
    989 		 */
    990 		send_req = (proxystream_send_req_t *)sock->proxy.send_req;
    991 		sock->proxy.send_req = NULL;
    992 	} else {
    993 		/* Allocate a new object. */
    994 		send_req = isc_mem_get(mctx, sizeof(*send_req));
    995 		*send_req = (proxystream_send_req_t){ 0 };
    996 	}
    997 
    998 	/* Initialise the send request object */
    999 	send_req->cb = cb;
   1000 	send_req->cbarg = cbarg;
   1001 	isc_nmhandle_attach(proxyhandle, &send_req->proxyhandle);
   1002 
   1003 	sock->proxy.nsending++;
   1004 
   1005 	return send_req;
   1006 }
   1007 
   1008 static void
   1009 proxystream_put_send_req(isc_mem_t *mctx, proxystream_send_req_t *send_req,
   1010 			 const bool force_destroy) {
   1011 	/*
   1012 	 * Attempt to put the object for reuse later if we are not
   1013 	 * wrapping up.
   1014 	 */
   1015 	if (!force_destroy) {
   1016 		isc_nmsocket_t *sock = send_req->proxyhandle->sock;
   1017 		sock->proxy.nsending--;
   1018 		isc_nmhandle_detach(&send_req->proxyhandle);
   1019 		if (sock->proxy.send_req == NULL) {
   1020 			sock->proxy.send_req = send_req;
   1021 			/*
   1022 			 * An object has been recycled,
   1023 			 * if not - we are going to destroy it.
   1024 			 */
   1025 			return;
   1026 		}
   1027 	}
   1028 
   1029 	isc_mem_put(mctx, send_req, sizeof(*send_req));
   1030 }
   1031 
   1032 static void
   1033 proxystream_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
   1034 	proxystream_send_req_t *send_req = (proxystream_send_req_t *)cbarg;
   1035 	isc_mem_t *mctx;
   1036 	isc_nm_cb_t cb;
   1037 	void *send_cbarg;
   1038 	isc_nmhandle_t *proxyhandle = NULL;
   1039 
   1040 	REQUIRE(VALID_NMHANDLE(handle));
   1041 	REQUIRE(VALID_NMHANDLE(send_req->proxyhandle));
   1042 	REQUIRE(VALID_NMSOCK(send_req->proxyhandle->sock));
   1043 	REQUIRE(send_req->proxyhandle->sock->tid == isc_tid());
   1044 
   1045 	mctx = send_req->proxyhandle->sock->worker->mctx;
   1046 	cb = send_req->cb;
   1047 	send_cbarg = send_req->cbarg;
   1048 
   1049 	isc_nmhandle_attach(send_req->proxyhandle, &proxyhandle);
   1050 	/* try to keep the send request object for reuse */
   1051 	proxystream_put_send_req(mctx, send_req, false);
   1052 	cb(proxyhandle, result, send_cbarg);
   1053 	proxystream_try_close_unused(proxyhandle->sock);
   1054 	isc_nmhandle_detach(&proxyhandle);
   1055 }
   1056 
   1057 static void
   1058 proxystream_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
   1059 		 void *cbarg, const bool dnsmsg) {
   1060 	isc_nmsocket_t *sock = NULL;
   1061 	proxystream_send_req_t *send_req = NULL;
   1062 	isc_result_t result = ISC_R_SUCCESS;
   1063 	bool fail_async = true;
   1064 
   1065 	REQUIRE(VALID_NMHANDLE(handle));
   1066 	REQUIRE(VALID_NMSOCK(handle->sock));
   1067 
   1068 	sock = handle->sock;
   1069 
   1070 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
   1071 
   1072 	if (isc__nm_closing(sock->worker)) {
   1073 		result = ISC_R_SHUTTINGDOWN;
   1074 		fail_async = false;
   1075 	} else if (proxystream_closing(sock)) {
   1076 		result = ISC_R_CANCELED;
   1077 		fail_async = true;
   1078 	}
   1079 
   1080 	if (result != ISC_R_SUCCESS) {
   1081 		isc__nm_uvreq_t *uvreq = isc__nm_uvreq_get(sock);
   1082 		isc_nmhandle_attach(handle, &uvreq->handle);
   1083 		uvreq->cb.send = cb;
   1084 		uvreq->cbarg = cbarg;
   1085 
   1086 		isc__nm_failed_send_cb(sock, uvreq, result, fail_async);
   1087 		return;
   1088 	}
   1089 
   1090 	send_req = proxystream_get_send_req(sock->worker->mctx, sock, handle,
   1091 					    cb, cbarg);
   1092 	if (dnsmsg) {
   1093 		isc__nm_senddns(sock->outerhandle, region, proxystream_send_cb,
   1094 				send_req);
   1095 	} else {
   1096 		isc_nm_send(sock->outerhandle, region, proxystream_send_cb,
   1097 			    send_req);
   1098 	}
   1099 }
   1100 
   1101 void
   1102 isc__nm_proxystream_send(isc_nmhandle_t *handle, isc_region_t *region,
   1103 			 isc_nm_cb_t cb, void *cbarg) {
   1104 	proxystream_send(handle, region, cb, cbarg, false);
   1105 }
   1106 
   1107 void
   1108 isc__nm_proxystream_senddns(isc_nmhandle_t *handle, isc_region_t *region,
   1109 			    isc_nm_cb_t cb, void *cbarg) {
   1110 	proxystream_send(handle, region, cb, cbarg, true);
   1111 }
   1112 
   1113 void
   1114 isc__nm_proxystream_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
   1115 	REQUIRE(VALID_NMSOCK(listener));
   1116 	REQUIRE(listener->type == isc_nm_proxystreamlistener);
   1117 
   1118 	if (listener->outer != NULL) {
   1119 		INSIST(VALID_NMSOCK(listener->outer));
   1120 		isc_nmsocket_set_tlsctx(listener->outer, tlsctx);
   1121 	}
   1122 }
   1123 
   1124 bool
   1125 isc__nm_proxystream_has_encryption(const isc_nmhandle_t *handle) {
   1126 	isc_nmsocket_t *sock = NULL;
   1127 
   1128 	REQUIRE(VALID_NMHANDLE(handle));
   1129 	REQUIRE(VALID_NMSOCK(handle->sock));
   1130 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
   1131 
   1132 	sock = handle->sock;
   1133 	if (sock->outerhandle != NULL) {
   1134 		INSIST(VALID_NMHANDLE(sock->outerhandle));
   1135 		return isc_nm_has_encryption(sock->outerhandle);
   1136 	}
   1137 
   1138 	return false;
   1139 }
   1140 
   1141 const char *
   1142 isc__nm_proxystream_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
   1143 	isc_nmsocket_t *sock = NULL;
   1144 
   1145 	REQUIRE(VALID_NMHANDLE(handle));
   1146 	REQUIRE(VALID_NMSOCK(handle->sock));
   1147 	REQUIRE(handle->sock->type == isc_nm_proxystreamsocket);
   1148 
   1149 	sock = handle->sock;
   1150 	if (sock->outerhandle != NULL) {
   1151 		INSIST(VALID_NMHANDLE(sock->outerhandle));
   1152 		return isc_nm_verify_tls_peer_result_string(sock->outerhandle);
   1153 	}
   1154 
   1155 	return NULL;
   1156 }
   1157 
   1158 void
   1159 isc__nmhandle_proxystream_get_selected_alpn(isc_nmhandle_t *handle,
   1160 					    const unsigned char **alpn,
   1161 					    unsigned int *alpnlen) {
   1162 	isc_nmsocket_t *sock;
   1163 
   1164 	REQUIRE(VALID_NMHANDLE(handle));
   1165 	sock = handle->sock;
   1166 	REQUIRE(VALID_NMSOCK(sock));
   1167 	REQUIRE(sock->type == isc_nm_proxystreamsocket);
   1168 	REQUIRE(sock->tid == isc_tid());
   1169 
   1170 	isc__nmhandle_get_selected_alpn(sock->outerhandle, alpn, alpnlen);
   1171 }
   1172