mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 14:35:26 +00:00
PROXYv2 over UDP transport
This commit adds a new transport that supports PROXYv2 over UDP. It is built on top of PROXYv2 handling code (just like PROXY Stream). It works by processing and stripping the PROXYv2 headers at the beginning of a datagram (when accepting a datagram) or by placing a PROXYv2 header to the beginning of an outgoing datagram. The transport is built in such a way that incoming datagrams are being handled with minimal memory allocations and copying.
This commit is contained in:
@@ -107,6 +107,7 @@ libisc_la_SOURCES = \
|
||||
netmgr/netmgr-int.h \
|
||||
netmgr/netmgr.c \
|
||||
netmgr/proxystream.c \
|
||||
netmgr/proxyudp.c \
|
||||
netmgr/socket.c \
|
||||
netmgr/streamdns.c \
|
||||
netmgr/tcp.c \
|
||||
|
@@ -326,6 +326,26 @@ isc_nm_routeconnect(isc_nm_t *mgr, isc_nm_cb_t cb, void *cbarg);
|
||||
* are not supported.
|
||||
*/
|
||||
|
||||
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);
|
||||
/*%<
|
||||
* The same as `isc_nm_listenudp()`, but PROXYv2 headers are
|
||||
* expected at the beginning of the received datagrams.
|
||||
*/
|
||||
|
||||
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);
|
||||
/*%<
|
||||
* The same as `isc_nm_udpconnect()`, but PROXYv2 headers are added
|
||||
* at the beginning of each sent datagram. The PROXYv2 headers are
|
||||
* created using the data from the `proxy_info` object. If the
|
||||
* object is omitted, then LOCAL PROXYv2 headers are used.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_stoplistening(isc_nmsocket_t *sock);
|
||||
/*%<
|
||||
@@ -598,7 +618,7 @@ isc_nm_streamdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local,
|
||||
unsigned int timeout, isc_tlsctx_t *tlsctx,
|
||||
isc_tlsctx_client_session_cache_t *client_sess_cache,
|
||||
isc_nm_proxy_type_t proxy_type,
|
||||
isc_nm_proxyheader_info_t *proxy_info);
|
||||
isc_nm_proxyheader_info_t *proxy_info);
|
||||
/*%<
|
||||
* Establish a DNS client connection via a TCP or TLS connection, bound to
|
||||
* the address 'local' and connected to the address 'peer'.
|
||||
|
@@ -99,6 +99,7 @@ typedef enum isc_nmsocket_type {
|
||||
isc_nm_httpsocket = 1 << 4,
|
||||
isc_nm_streamdnssocket = 1 << 5,
|
||||
isc_nm_proxystreamsocket = 1 << 6,
|
||||
isc_nm_proxyudpsocket = 1 << 7,
|
||||
isc_nm_maxsocket,
|
||||
|
||||
isc_nm_udplistener, /* Aggregate of nm_udpsocks */
|
||||
@@ -106,7 +107,8 @@ typedef enum isc_nmsocket_type {
|
||||
isc_nm_tlslistener,
|
||||
isc_nm_httplistener,
|
||||
isc_nm_streamdnslistener,
|
||||
isc_nm_proxystreamlistener
|
||||
isc_nm_proxystreamlistener,
|
||||
isc_nm_proxyudplistener
|
||||
} isc_nmsocket_type;
|
||||
|
||||
typedef isc_nmsocket_type isc_nmsocket_type_t;
|
||||
|
@@ -249,6 +249,7 @@ struct isc_nmhandle {
|
||||
isc_sockaddr_t peer;
|
||||
isc_sockaddr_t local;
|
||||
bool proxy_is_unspec;
|
||||
struct isc_nmhandle *proxy_udphandle;
|
||||
isc_nm_opaquecb_t doreset; /* reset extra callback, external */
|
||||
isc_nm_opaquecb_t dofree; /* free extra callback, external */
|
||||
#if ISC_NETMGR_TRACE
|
||||
@@ -553,6 +554,8 @@ struct isc_nmsocket {
|
||||
} proxy2;
|
||||
bool header_processed;
|
||||
bool extra_processed; /* data arrived past header processed */
|
||||
isc_nmsocket_t **udp_server_socks; /* UDP sockets */
|
||||
size_t udp_server_socks_num;
|
||||
} proxy;
|
||||
|
||||
/*%
|
||||
@@ -1237,6 +1240,45 @@ isc__nmhandle_proxystream_get_selected_alpn(isc_nmhandle_t *handle,
|
||||
const unsigned char **alpn,
|
||||
unsigned int *alpnlen);
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result,
|
||||
const bool async);
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_stoplistening(isc_nmsocket_t *listener);
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_cleanup_data(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxyudp_cleartimeout(isc_nmhandle_t *handle);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxyudp_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxyudp_setwritetimeout(isc_nmhandle_t *handle,
|
||||
uint64_t write_timeout);
|
||||
|
||||
bool
|
||||
isc__nmsocket_proxyudp_timer_running(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmsocket_proxyudp_timer_restart(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmsocket_proxyudp_timer_stop(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_close(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_send(isc_nmhandle_t *handle, isc_region_t *region,
|
||||
isc_nm_cb_t cb, void *cbarg);
|
||||
|
||||
void
|
||||
isc__nm_incstats(isc_nmsocket_t *sock, isc__nm_statid_t id);
|
||||
/*%<
|
||||
|
@@ -324,6 +324,9 @@ isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) {
|
||||
isc__nmhandle_proxystream_setwritetimeout(handle,
|
||||
write_timeout);
|
||||
break;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nmhandle_proxyudp_setwritetimeout(handle, write_timeout);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -475,6 +478,7 @@ nmsocket_cleanup(void *arg) {
|
||||
#endif
|
||||
isc__nm_streamdns_cleanup_data(sock);
|
||||
isc__nm_proxystream_cleanup_data(sock);
|
||||
isc__nm_proxyudp_cleanup_data(sock);
|
||||
|
||||
if (sock->barriers_initialised) {
|
||||
isc_barrier_destroy(&sock->listen_barrier);
|
||||
@@ -610,6 +614,9 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_close(sock);
|
||||
return;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nm_proxyudp_close(sock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -655,7 +662,8 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) {
|
||||
(*sockp)->type == isc_nm_streamdnslistener ||
|
||||
(*sockp)->type == isc_nm_tlslistener ||
|
||||
(*sockp)->type == isc_nm_httplistener ||
|
||||
(*sockp)->type == isc_nm_proxystreamlistener);
|
||||
(*sockp)->type == isc_nm_proxystreamlistener ||
|
||||
(*sockp)->type == isc_nm_proxyudplistener);
|
||||
|
||||
isc__nmsocket_detach(sockp);
|
||||
}
|
||||
@@ -848,6 +856,7 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t const *peer,
|
||||
|
||||
switch (sock->type) {
|
||||
case isc_nm_udpsocket:
|
||||
case isc_nm_proxyudpsocket:
|
||||
if (!sock->client) {
|
||||
break;
|
||||
}
|
||||
@@ -951,6 +960,10 @@ nmhandle_destroy(isc_nmhandle_t *handle) {
|
||||
sock->statichandle = NULL;
|
||||
}
|
||||
|
||||
if (handle->proxy_udphandle != NULL) {
|
||||
isc_nmhandle_detach(&handle->proxy_udphandle);
|
||||
}
|
||||
|
||||
ISC_LIST_UNLINK(sock->active_handles, handle, active_link);
|
||||
|
||||
if (sock->closehandle_cb == NULL) {
|
||||
@@ -1045,6 +1058,9 @@ isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_failed_read_cb(sock, result, async);
|
||||
return;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nm_proxyudp_failed_read_cb(sock, result, async);
|
||||
return;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1151,6 +1167,9 @@ isc__nmsocket_timer_restart(isc_nmsocket_t *sock) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmsocket_proxystream_timer_restart(sock);
|
||||
return;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nmsocket_proxyudp_timer_restart(sock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1196,6 +1215,8 @@ isc__nmsocket_timer_running(isc_nmsocket_t *sock) {
|
||||
return (isc__nmsocket_streamdns_timer_running(sock));
|
||||
case isc_nm_proxystreamsocket:
|
||||
return (isc__nmsocket_proxystream_timer_running(sock));
|
||||
case isc_nm_proxyudpsocket:
|
||||
return (isc__nmsocket_proxyudp_timer_running(sock));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1230,6 +1251,9 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmsocket_proxystream_timer_stop(sock);
|
||||
return;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nmsocket_proxyudp_timer_stop(sock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1407,6 +1431,9 @@ isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_cleartimeout(handle);
|
||||
return;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nmhandle_proxyudp_cleartimeout(handle);
|
||||
return;
|
||||
default:
|
||||
handle->sock->read_timeout = 0;
|
||||
|
||||
@@ -1436,6 +1463,9 @@ isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_settimeout(handle, timeout);
|
||||
return;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nmhandle_proxyudp_settimeout(handle, timeout);
|
||||
return;
|
||||
default:
|
||||
handle->sock->read_timeout = timeout;
|
||||
isc__nmsocket_timer_restart(handle->sock);
|
||||
@@ -1595,6 +1625,9 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_send(handle, region, cb, cbarg);
|
||||
break;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nm_proxyudp_send(handle, region, cb, cbarg);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1645,6 +1678,9 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_read(handle, cb, cbarg);
|
||||
break;
|
||||
case isc_nm_proxyudpsocket:
|
||||
isc__nm_proxyudp_read(handle, cb, cbarg);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1662,6 +1698,7 @@ cancelread_cb(void *arg) {
|
||||
|
||||
switch (handle->sock->type) {
|
||||
case isc_nm_udpsocket:
|
||||
case isc_nm_proxyudpsocket:
|
||||
case isc_nm_streamdnssocket:
|
||||
case isc_nm_httpsocket:
|
||||
isc__nm_failed_read_cb(handle->sock, ISC_R_CANCELED, false);
|
||||
@@ -1738,6 +1775,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) {
|
||||
case isc_nm_proxystreamlistener:
|
||||
isc__nm_proxystream_stoplistening(sock);
|
||||
break;
|
||||
case isc_nm_proxyudplistener:
|
||||
isc__nm_proxyudp_stoplistening(sock);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1751,7 +1791,8 @@ isc__nmsocket_stop(isc_nmsocket_t *listener) {
|
||||
REQUIRE(listener->type == isc_nm_httplistener ||
|
||||
listener->type == isc_nm_tlslistener ||
|
||||
listener->type == isc_nm_streamdnslistener ||
|
||||
listener->type == isc_nm_proxystreamlistener);
|
||||
listener->type == isc_nm_proxystreamlistener ||
|
||||
listener->type == isc_nm_proxyudplistener);
|
||||
REQUIRE(!listener->closing);
|
||||
|
||||
listener->closing = true;
|
||||
@@ -2090,6 +2131,7 @@ isc_nm_bad_request(isc_nmhandle_t *handle) {
|
||||
|
||||
switch (sock->type) {
|
||||
case isc_nm_udpsocket:
|
||||
case isc_nm_proxyudpsocket:
|
||||
return;
|
||||
case isc_nm_tcpsocket:
|
||||
case isc_nm_streamdnssocket:
|
||||
@@ -2146,6 +2188,7 @@ get_proxy_handle(isc_nmhandle_t *handle) {
|
||||
|
||||
switch (sock->type) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
case isc_nm_proxyudpsocket:
|
||||
return (handle);
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
case isc_nm_httpsocket:
|
||||
@@ -2205,8 +2248,8 @@ isc_nmhandle_real_peeraddr(isc_nmhandle_t *handle) {
|
||||
if (isc_nmhandle_is_stream(proxyhandle)) {
|
||||
addr = isc_nmhandle_peeraddr(proxyhandle->sock->outerhandle);
|
||||
} else {
|
||||
/* TODO: PROXY over UDP */
|
||||
UNREACHABLE();
|
||||
INSIST(proxyhandle->sock->type == isc_nm_proxyudpsocket);
|
||||
addr = isc_nmhandle_peeraddr(proxyhandle->proxy_udphandle);
|
||||
}
|
||||
|
||||
return (addr);
|
||||
@@ -2228,8 +2271,8 @@ isc_nmhandle_real_localaddr(isc_nmhandle_t *handle) {
|
||||
if (isc_nmhandle_is_stream(proxyhandle)) {
|
||||
addr = isc_nmhandle_localaddr(proxyhandle->sock->outerhandle);
|
||||
} else {
|
||||
/* TODO: PROXY over UDP */
|
||||
UNREACHABLE();
|
||||
INSIST(proxyhandle->sock->type == isc_nm_proxyudpsocket);
|
||||
addr = isc_nmhandle_localaddr(proxyhandle->proxy_udphandle);
|
||||
}
|
||||
|
||||
return (addr);
|
||||
@@ -2319,6 +2362,7 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
|
||||
break;
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
case isc_nm_udpsocket:
|
||||
case isc_nm_proxyudpsocket:
|
||||
case isc_nm_streamdnssocket:
|
||||
return;
|
||||
break;
|
||||
@@ -2811,6 +2855,10 @@ nmsocket_type_totext(isc_nmsocket_type type) {
|
||||
return ("isc_nm_proxystreamlistener");
|
||||
case isc_nm_proxystreamsocket:
|
||||
return ("isc_nm_proxystreamsocket");
|
||||
case isc_nm_proxyudplistener:
|
||||
return ("isc_nm_proxyudplistener");
|
||||
case isc_nm_proxyudpsocket:
|
||||
return ("isc_nm_proxyudpsocket");
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
883
lib/isc/netmgr/proxyudp.c
Normal file
883
lib/isc/netmgr/proxyudp.c
Normal file
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
* 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_mem_get(worker->mctx, sizeof(*sock));
|
||||
isc__nmsocket_init(sock, worker, type, addr, NULL);
|
||||
sock->result = ISC_R_UNSET;
|
||||
if (type == isc_nm_proxyudpsocket) {
|
||||
uint32_t initial = 0;
|
||||
isc_nm_gettimeouts(worker->netmgr, &initial, NULL, NULL, NULL);
|
||||
sock->read_timeout = initial;
|
||||
sock->client = !is_server;
|
||||
sock->connecting = !is_server;
|
||||
if (!is_server) {
|
||||
isc_buffer_allocate(worker->mctx,
|
||||
&sock->proxy.proxy2.outbuf,
|
||||
ISC_NM_PROXY2_DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
} else if (type == isc_nm_proxyudplistener) {
|
||||
size_t nworkers = worker->netmgr->nloops;
|
||||
sock->proxy.udp_server_socks_num = nworkers;
|
||||
sock->proxy.udp_server_socks = isc_mem_cget(
|
||||
worker->mctx, nworkers, sizeof(isc_nmsocket_t *));
|
||||
}
|
||||
|
||||
return (sock);
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_read_cb(isc_nmhandle_t *handle, isc_result_t result,
|
||||
isc_region_t *region, void *cbarg) {
|
||||
isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
|
||||
isc_nmsocket_t *proxysock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
|
||||
if (sock->client) {
|
||||
proxysock = sock;
|
||||
} else {
|
||||
INSIST(sock->type == isc_nm_proxyudplistener);
|
||||
proxysock = sock->proxy.udp_server_socks[handle->sock->tid];
|
||||
if (proxysock->outerhandle == NULL) {
|
||||
isc_nmhandle_attach(handle, &proxysock->outerhandle);
|
||||
}
|
||||
|
||||
proxysock->iface = isc_nmhandle_localaddr(handle);
|
||||
proxysock->peer = isc_nmhandle_peeraddr(handle);
|
||||
}
|
||||
|
||||
INSIST(proxysock->tid == isc_tid());
|
||||
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (!proxysock->client) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (result != ISC_R_TIMEDOUT) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (isc__nm_closing(proxysock->worker)) {
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
goto failed;
|
||||
} else if (proxyudp_closing(proxysock)) {
|
||||
result = ISC_R_CANCELED;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Handle initial PROXY header data */
|
||||
if (!proxysock->client) {
|
||||
isc_nmhandle_t *proxyhandle = NULL;
|
||||
proxysock->reading = false;
|
||||
proxyhandle = isc__nmhandle_get(proxysock, &proxysock->peer,
|
||||
&proxysock->iface);
|
||||
isc_nmhandle_attach(handle, &proxyhandle->proxy_udphandle);
|
||||
(void)isc_proxy2_header_handle_directly(
|
||||
region, proxyudp_on_header_data_cb, proxyhandle);
|
||||
isc_nmhandle_detach(&proxyhandle);
|
||||
} else {
|
||||
isc_nm_recv_cb_t recv_cb = NULL;
|
||||
void *recv_cbarg = NULL;
|
||||
|
||||
recv_cb = proxysock->recv_cb;
|
||||
recv_cbarg = proxysock->recv_cbarg;
|
||||
|
||||
if (result != ISC_R_TIMEDOUT) {
|
||||
proxysock->reading = false;
|
||||
proxyudp_stop_reading(proxysock);
|
||||
}
|
||||
recv_cb(proxysock->statichandle, result, region, recv_cbarg);
|
||||
|
||||
if (result == ISC_R_TIMEDOUT &&
|
||||
!isc__nmsocket_timer_running(proxysock))
|
||||
|
||||
{
|
||||
isc__nmsocket_clearcb(proxysock);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
proxyudp_try_close_unused(proxysock);
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
isc__nm_proxyudp_failed_read_cb(proxysock, result, false);
|
||||
return;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_nm_listenproxyudp(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
isc_nm_recv_cb_t cb, void *cbarg,
|
||||
isc_nmsocket_t **sockp) {
|
||||
isc_result_t result;
|
||||
isc_nmsocket_t *listener = NULL;
|
||||
isc__networker_t *worker = &mgr->workers[isc_tid()];
|
||||
|
||||
REQUIRE(VALID_NM(mgr));
|
||||
REQUIRE(isc_tid() == 0);
|
||||
REQUIRE(sockp != NULL && *sockp == NULL);
|
||||
|
||||
if (isc__nm_closing(worker)) {
|
||||
return (ISC_R_SHUTTINGDOWN);
|
||||
}
|
||||
|
||||
listener = proxyudp_sock_new(worker, isc_nm_proxyudplistener, iface,
|
||||
true);
|
||||
listener->recv_cb = cb;
|
||||
listener->recv_cbarg = cbarg;
|
||||
|
||||
for (size_t i = 0; i < listener->proxy.udp_server_socks_num; i++) {
|
||||
listener->proxy.udp_server_socks[i] = proxyudp_sock_new(
|
||||
&mgr->workers[i], isc_nm_proxyudpsocket, iface, true);
|
||||
|
||||
listener->proxy.udp_server_socks[i]->recv_cb =
|
||||
listener->recv_cb;
|
||||
|
||||
listener->proxy.udp_server_socks[i]->recv_cbarg =
|
||||
listener->recv_cbarg;
|
||||
|
||||
isc__nmsocket_attach(
|
||||
listener,
|
||||
&listener->proxy.udp_server_socks[i]->listener);
|
||||
}
|
||||
|
||||
result = isc_nm_listenudp(mgr, workers, iface, proxyudp_read_cb,
|
||||
listener, &listener->outer);
|
||||
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
listener->active = true;
|
||||
listener->result = result;
|
||||
listener->nchildren = listener->outer->nchildren;
|
||||
*sockp = listener;
|
||||
} else {
|
||||
for (size_t i = 0; i < listener->proxy.udp_server_socks_num;
|
||||
i++)
|
||||
{
|
||||
stop_proxyudp_child(
|
||||
listener->proxy.udp_server_socks[i]);
|
||||
}
|
||||
listener->closed = true;
|
||||
isc__nmsocket_detach(&listener);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
|
||||
isc_result_t result) {
|
||||
sock->connecting = false;
|
||||
if (sock->connect_cb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sock->connect_cb(handle, result, sock->connect_cbarg);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isc__nmsocket_clearcb(handle->sock);
|
||||
} else {
|
||||
sock->connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_try_close_unused(isc_nmsocket_t *sock) {
|
||||
/* try to close unused socket */
|
||||
if (sock->statichandle == NULL && sock->proxy.nsending == 0) {
|
||||
if (sock->client) {
|
||||
isc__nmsocket_prep_destroy(sock);
|
||||
} else if (sock->outerhandle) {
|
||||
isc_nmhandle_detach(&sock->outerhandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
|
||||
isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
|
||||
isc_nmhandle_t *proxyhandle = NULL;
|
||||
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
|
||||
sock->tid = isc_tid();
|
||||
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
INSIST(VALID_NMHANDLE(handle));
|
||||
|
||||
sock->iface = isc_nmhandle_localaddr(handle);
|
||||
sock->peer = isc_nmhandle_peeraddr(handle);
|
||||
isc_nmhandle_attach(handle, &sock->outerhandle);
|
||||
handle->sock->proxy.sock = sock;
|
||||
sock->active = true;
|
||||
sock->connected = true;
|
||||
sock->connecting = false;
|
||||
|
||||
proxyhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
|
||||
proxyudp_call_connect_cb(sock, proxyhandle, ISC_R_SUCCESS);
|
||||
isc_nmhandle_detach(&proxyhandle);
|
||||
|
||||
proxyudp_try_close_unused(sock);
|
||||
|
||||
isc__nmsocket_detach(&handle->sock->proxy.sock);
|
||||
|
||||
return;
|
||||
error:
|
||||
proxyhandle = isc__nmhandle_get(sock, NULL, NULL);
|
||||
sock->closed = true;
|
||||
proxyudp_call_connect_cb(sock, proxyhandle, result);
|
||||
isc_nmhandle_detach(&proxyhandle);
|
||||
isc__nmsocket_detach(&sock);
|
||||
}
|
||||
|
||||
void
|
||||
isc_nm_proxyudpconnect(isc_nm_t *mgr, isc_sockaddr_t *local,
|
||||
isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg,
|
||||
unsigned int timeout,
|
||||
isc_nm_proxyheader_info_t *proxy_info) {
|
||||
isc_result_t result = ISC_R_FAILURE;
|
||||
isc_nmsocket_t *nsock = NULL;
|
||||
isc__networker_t *worker = &mgr->workers[isc_tid()];
|
||||
|
||||
REQUIRE(VALID_NM(mgr));
|
||||
|
||||
if (isc__nm_closing(worker)) {
|
||||
cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
|
||||
return;
|
||||
}
|
||||
|
||||
nsock = proxyudp_sock_new(worker, isc_nm_proxyudpsocket, local, false);
|
||||
nsock->connect_cb = cb;
|
||||
nsock->connect_cbarg = cbarg;
|
||||
nsock->read_timeout = timeout;
|
||||
nsock->connecting = true;
|
||||
|
||||
if (proxy_info == NULL) {
|
||||
result = isc_proxy2_make_header(nsock->proxy.proxy2.outbuf,
|
||||
ISC_PROXY2_CMD_LOCAL, 0, NULL,
|
||||
NULL, NULL);
|
||||
} else if (proxy_info->complete) {
|
||||
isc_buffer_putmem(nsock->proxy.proxy2.outbuf,
|
||||
proxy_info->complete_header.base,
|
||||
proxy_info->complete_header.length);
|
||||
result = ISC_R_SUCCESS;
|
||||
} else if (!proxy_info->complete) {
|
||||
result = isc_proxy2_make_header(
|
||||
nsock->proxy.proxy2.outbuf, ISC_PROXY2_CMD_PROXY,
|
||||
SOCK_DGRAM, &proxy_info->proxy_info.src_addr,
|
||||
&proxy_info->proxy_info.dst_addr,
|
||||
&proxy_info->proxy_info.tlv_data);
|
||||
}
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
|
||||
isc_nm_udpconnect(mgr, local, peer, proxyudp_connect_cb, nsock,
|
||||
timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* Asynchronous 'udpstop' call handler: stop listening on a UDP socket.
|
||||
*/
|
||||
static void
|
||||
stop_proxyudp_child_job(void *arg) {
|
||||
isc_nmsocket_t *listener = NULL;
|
||||
isc_nmsocket_t *sock = arg;
|
||||
uint32_t tid = 0;
|
||||
|
||||
if (sock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
INSIST(VALID_NMSOCK(sock));
|
||||
INSIST(sock->tid == isc_tid());
|
||||
|
||||
listener = sock->listener;
|
||||
sock->listener = NULL;
|
||||
|
||||
INSIST(VALID_NMSOCK(listener));
|
||||
INSIST(listener->type == isc_nm_proxyudplistener);
|
||||
|
||||
if (sock->outerhandle != NULL) {
|
||||
proxyudp_stop_reading(sock);
|
||||
isc_nmhandle_detach(&sock->outerhandle);
|
||||
}
|
||||
|
||||
tid = sock->tid;
|
||||
isc__nmsocket_prep_destroy(sock);
|
||||
isc__nmsocket_detach(&listener->proxy.udp_server_socks[tid]);
|
||||
isc__nmsocket_detach(&listener);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_proxyudp_child(isc_nmsocket_t *sock) {
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
|
||||
if (sock->tid == 0) {
|
||||
stop_proxyudp_child_job(sock);
|
||||
} else {
|
||||
isc_async_run(sock->worker->loop, stop_proxyudp_child_job,
|
||||
sock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_stoplistening(isc_nmsocket_t *listener) {
|
||||
REQUIRE(VALID_NMSOCK(listener));
|
||||
REQUIRE(listener->type == isc_nm_proxyudplistener);
|
||||
REQUIRE(listener->proxy.sock == NULL);
|
||||
|
||||
isc__nmsocket_stop(listener);
|
||||
|
||||
listener->active = false;
|
||||
|
||||
for (size_t i = 1; i < listener->proxy.udp_server_socks_num; i++) {
|
||||
stop_proxyudp_child(listener->proxy.udp_server_socks[i]);
|
||||
}
|
||||
|
||||
stop_proxyudp_child(listener->proxy.udp_server_socks[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_clear_proxy_header_data(isc_nmsocket_t *sock) {
|
||||
if (sock->client && sock->proxy.proxy2.outbuf != NULL) {
|
||||
isc_buffer_free(&sock->proxy.proxy2.outbuf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_cleanup_data(isc_nmsocket_t *sock) {
|
||||
switch (sock->type) {
|
||||
case isc_nm_proxyudpsocket:
|
||||
if (sock->proxy.send_req != NULL) {
|
||||
proxyudp_put_send_req(sock->worker->mctx,
|
||||
sock->proxy.send_req, true);
|
||||
}
|
||||
|
||||
proxyudp_clear_proxy_header_data(sock);
|
||||
break;
|
||||
case isc_nm_proxyudplistener:
|
||||
isc_mem_cput(sock->worker->mctx, sock->proxy.udp_server_socks,
|
||||
sock->proxy.udp_server_socks_num,
|
||||
sizeof(isc_nmsocket_t *));
|
||||
break;
|
||||
case isc_nm_udpsocket:
|
||||
INSIST(sock->proxy.sock == NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmhandle_proxyudp_cleartimeout(isc_nmhandle_t *handle) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
REQUIRE(handle->sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
sock = handle->sock;
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
isc_nmhandle_cleartimeout(sock->outerhandle);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmhandle_proxyudp_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
REQUIRE(handle->sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
sock = handle->sock;
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
isc_nmhandle_settimeout(sock->outerhandle, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmhandle_proxyudp_setwritetimeout(isc_nmhandle_t *handle,
|
||||
uint64_t write_timeout) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
REQUIRE(handle->sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
sock = handle->sock;
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
|
||||
isc_nmhandle_setwritetimeout(sock->outerhandle, write_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
isc__nmsocket_proxyudp_timer_running(isc_nmsocket_t *sock) {
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
|
||||
return (isc__nmsocket_timer_running(sock->outerhandle->sock));
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmsocket_proxyudp_timer_restart(isc_nmsocket_t *sock) {
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
|
||||
isc__nmsocket_timer_restart(sock->outerhandle->sock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmsocket_proxyudp_timer_stop(isc_nmsocket_t *sock) {
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
if (sock->outerhandle != NULL) {
|
||||
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
||||
REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
|
||||
isc__nmsocket_timer_stop(sock->outerhandle->sock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_close(isc_nmsocket_t *sock) {
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_proxyudpsocket);
|
||||
REQUIRE(sock->tid == isc_tid());
|
||||
|
||||
sock->closing = true;
|
||||
|
||||
/*
|
||||
* At this point we're certain that there are no
|
||||
* external references, we can close everything.
|
||||
*/
|
||||
proxyudp_stop_reading(sock);
|
||||
sock->reading = false;
|
||||
if (sock->outerhandle != NULL) {
|
||||
isc_nmhandle_close(sock->outerhandle);
|
||||
isc_nmhandle_detach(&sock->outerhandle);
|
||||
}
|
||||
|
||||
if (sock->proxy.sock != NULL) {
|
||||
isc__nmsocket_detach(&sock->proxy.sock);
|
||||
}
|
||||
|
||||
/* Further cleanup performed in isc__nm_proxyudp_cleanup_data() */
|
||||
sock->closed = true;
|
||||
sock->active = false;
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb,
|
||||
void *cbarg) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
sock = handle->sock;
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_proxyudpsocket);
|
||||
REQUIRE(sock->recv_handle == NULL);
|
||||
REQUIRE(sock->tid == isc_tid());
|
||||
|
||||
sock->recv_cb = cb;
|
||||
sock->recv_cbarg = cbarg;
|
||||
sock->reading = true;
|
||||
|
||||
if (isc__nm_closing(sock->worker)) {
|
||||
isc__nm_proxyudp_failed_read_cb(sock, ISC_R_SHUTTINGDOWN,
|
||||
false);
|
||||
return;
|
||||
} else if (proxyudp_closing(sock)) {
|
||||
isc__nm_proxyudp_failed_read_cb(sock, ISC_R_CANCELED, true);
|
||||
return;
|
||||
}
|
||||
|
||||
isc_nm_read(sock->outerhandle, proxyudp_read_cb, sock);
|
||||
}
|
||||
|
||||
static proxyudp_send_req_t *
|
||||
proxyudp_get_send_req(isc_mem_t *mctx, isc_nmsocket_t *sock,
|
||||
isc_nmhandle_t *proxyhandle, isc_region_t *client_data,
|
||||
isc_nm_cb_t cb, void *cbarg) {
|
||||
proxyudp_send_req_t *send_req = NULL;
|
||||
|
||||
if (sock->proxy.send_req != NULL) {
|
||||
/*
|
||||
* We have a previously allocated object - let's use that.
|
||||
* That should help reducing stress on the memory allocator.
|
||||
*/
|
||||
send_req = (proxyudp_send_req_t *)sock->proxy.send_req;
|
||||
sock->proxy.send_req = NULL;
|
||||
} else {
|
||||
/* Allocate a new object. */
|
||||
send_req = isc_mem_get(mctx, sizeof(*send_req));
|
||||
*send_req = (proxyudp_send_req_t){ 0 };
|
||||
}
|
||||
|
||||
/* Initialise the send request object */
|
||||
send_req->cb = cb;
|
||||
send_req->cbarg = cbarg;
|
||||
isc_nmhandle_attach(proxyhandle, &send_req->proxyhandle);
|
||||
|
||||
if (client_data != NULL) {
|
||||
isc_region_t header_region = { 0 };
|
||||
INSIST(sock->client);
|
||||
INSIST(sock->proxy.proxy2.outbuf != NULL);
|
||||
|
||||
isc_buffer_usedregion(sock->proxy.proxy2.outbuf,
|
||||
&header_region);
|
||||
|
||||
INSIST(header_region.length > 0);
|
||||
|
||||
/* allocate the buffer if it has not been allocated yet */
|
||||
if (send_req->outbuf == NULL) {
|
||||
isc_buffer_allocate(mctx, &send_req->outbuf,
|
||||
client_data->length +
|
||||
header_region.length);
|
||||
}
|
||||
|
||||
isc_buffer_putmem(send_req->outbuf, header_region.base,
|
||||
header_region.length);
|
||||
isc_buffer_putmem(send_req->outbuf, client_data->base,
|
||||
client_data->length);
|
||||
}
|
||||
|
||||
sock->proxy.nsending++;
|
||||
|
||||
return (send_req);
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_put_send_req(isc_mem_t *mctx, proxyudp_send_req_t *send_req,
|
||||
const bool force_destroy) {
|
||||
if (send_req->outbuf != NULL) {
|
||||
/* clear the buffer to reuse it further */
|
||||
isc_buffer_clear(send_req->outbuf);
|
||||
}
|
||||
/*
|
||||
* Attempt to put the object for reuse later if we are not
|
||||
* wrapping up.
|
||||
*/
|
||||
if (!force_destroy) {
|
||||
isc_nmsocket_t *sock = send_req->proxyhandle->sock;
|
||||
sock->proxy.nsending--;
|
||||
isc_nmhandle_detach(&send_req->proxyhandle);
|
||||
if (sock->proxy.send_req == NULL) {
|
||||
sock->proxy.send_req = send_req;
|
||||
/*
|
||||
* An object has been recycled,
|
||||
* if not - we are going to destroy it.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (send_req->outbuf != NULL) {
|
||||
isc_buffer_free(&send_req->outbuf);
|
||||
}
|
||||
}
|
||||
|
||||
isc_mem_put(mctx, send_req, sizeof(*send_req));
|
||||
}
|
||||
|
||||
static void
|
||||
proxyudp_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
|
||||
proxyudp_send_req_t *send_req = (proxyudp_send_req_t *)cbarg;
|
||||
isc_mem_t *mctx;
|
||||
isc_nm_cb_t cb;
|
||||
void *send_cbarg;
|
||||
isc_nmhandle_t *proxyhandle = NULL;
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMHANDLE(send_req->proxyhandle));
|
||||
REQUIRE(VALID_NMSOCK(send_req->proxyhandle->sock));
|
||||
REQUIRE(send_req->proxyhandle->sock->tid == isc_tid());
|
||||
|
||||
mctx = send_req->proxyhandle->sock->worker->mctx;
|
||||
cb = send_req->cb;
|
||||
send_cbarg = send_req->cbarg;
|
||||
|
||||
isc_nmhandle_attach(send_req->proxyhandle, &proxyhandle);
|
||||
isc__nmsocket_attach(proxyhandle->sock, &sock);
|
||||
|
||||
/* try to keep the send request object for reuse */
|
||||
proxyudp_put_send_req(mctx, send_req, false);
|
||||
cb(proxyhandle, result, send_cbarg);
|
||||
isc_nmhandle_detach(&proxyhandle);
|
||||
|
||||
/*
|
||||
* Try to close the client socket when we do not need it
|
||||
* anymore. In the case of server socket - detach the underlying
|
||||
* (UDP) handle when the socket is not being used anymore.
|
||||
*/
|
||||
proxyudp_try_close_unused(sock);
|
||||
isc__nmsocket_detach(&sock);
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_proxyudp_send(isc_nmhandle_t *handle, isc_region_t *region,
|
||||
isc_nm_cb_t cb, void *cbarg) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
proxyudp_send_req_t *send_req = NULL;
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
|
||||
sock = handle->sock;
|
||||
|
||||
REQUIRE(sock->type == isc_nm_proxyudpsocket);
|
||||
|
||||
if (isc__nm_closing(sock->worker)) {
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
} else if (proxyudp_closing(sock)) {
|
||||
result = ISC_R_CANCELED;
|
||||
}
|
||||
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isc__nm_uvreq_t *uvreq = isc__nm_uvreq_get(sock);
|
||||
isc_nmhandle_attach(handle, &uvreq->handle);
|
||||
uvreq->cb.send = cb;
|
||||
uvreq->cbarg = cbarg;
|
||||
|
||||
isc__nm_failed_send_cb(sock, uvreq, result, true);
|
||||
return;
|
||||
}
|
||||
|
||||
send_req = proxyudp_get_send_req(sock->worker->mctx, sock, handle,
|
||||
(sock->client ? region : NULL), cb,
|
||||
cbarg);
|
||||
if (sock->client) {
|
||||
isc_region_t send_data = { 0 };
|
||||
isc_buffer_usedregion(send_req->outbuf, &send_data);
|
||||
isc_nm_send(sock->outerhandle, &send_data, proxyudp_send_cb,
|
||||
send_req);
|
||||
} else {
|
||||
isc_nm_send(handle->proxy_udphandle, region, proxyudp_send_cb,
|
||||
send_req);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user