PROXY Stream transport
This commit adds a new stream-based transport with an interface compatible with TCP. The transport is built on top of TCP transport and the new PROXYv2 handling code. Despite being built on top of TCP, it can be easily extended to work on top of any TCP-like stream-based transport. The intention of having this transport is to add PROXYv2 support into all existing stream-based DNS transport (DNS over TCP, DNS over TLS, DNS over HTTP) by making the work on top of this new transport. The idea behind the transport is simple after accepting the connection or connecting to a remote server it enters PROXYv2 handling mode: that is, it either attempts to read (when accepting the connection) or send (when establishing a connection) a PROXYv2 header. After that it works like a mere wrapper on top of the underlying stream-based transport (TCP).
This commit is contained in:
@@ -106,6 +106,7 @@ libisc_la_SOURCES = \
|
||||
$(libisc_la_HEADERS) \
|
||||
netmgr/netmgr-int.h \
|
||||
netmgr/netmgr.c \
|
||||
netmgr/proxystream.c \
|
||||
netmgr/socket.c \
|
||||
netmgr/streamdns.c \
|
||||
netmgr/tcp.c \
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <isc/refcount.h>
|
||||
#include <isc/region.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/sockaddr.h>
|
||||
#include <isc/tls.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
@@ -87,6 +88,21 @@ typedef void (*isc_nm_opaquecb_t)(void *arg);
|
||||
* callbacks.
|
||||
*/
|
||||
|
||||
typedef struct isc_nm_proxyheader_info {
|
||||
bool complete;
|
||||
union {
|
||||
isc_region_t complete_header; /* complete header data */
|
||||
struct {
|
||||
isc_sockaddr_t src_addr;
|
||||
isc_sockaddr_t dst_addr;
|
||||
isc_region_t tlv_data;
|
||||
} proxy_info; /* information to put into the new header */
|
||||
};
|
||||
} isc_nm_proxyheader_info_t;
|
||||
/*%<
|
||||
* Information to put into the PROXYv2 header when establishing a connection.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_netmgr_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t **netgmrp);
|
||||
/*%<
|
||||
@@ -229,6 +245,29 @@ isc_nmhandle_localaddr(isc_nmhandle_t *handle);
|
||||
* Return the local address for the given handle.
|
||||
*/
|
||||
|
||||
isc_sockaddr_t
|
||||
isc_nmhandle_real_peeraddr(isc_nmhandle_t *handle);
|
||||
/*%<
|
||||
* Return the real (as seen by the OS) peer address for the given
|
||||
* handle even when PROXY protocol is used.
|
||||
*
|
||||
* NOTE: This function is intended mostly for a) implementing PROXYv2
|
||||
* access control facilities and b) logging. Using it for anything
|
||||
* else WILL break PROXYv2 support. Please consider using
|
||||
* 'isc_nmhandle_peeraddr()' instead.
|
||||
*/
|
||||
isc_sockaddr_t
|
||||
isc_nmhandle_real_localaddr(isc_nmhandle_t *handle);
|
||||
/*%<
|
||||
* Return the real (as seen by the OS) local address for the given
|
||||
* handle even when PROXY protocol is used.
|
||||
*
|
||||
* NOTE: This function is intended mostly for a) implementing PROXYv2
|
||||
* access control facilities and b) logging. Using it for anything
|
||||
* else WILL break PROXYv2 support. Please consider using
|
||||
* 'isc_nmhandle_localaddr()' instead.
|
||||
*/
|
||||
|
||||
isc_nm_t *
|
||||
isc_nmhandle_netmgr(isc_nmhandle_t *handle);
|
||||
/*%<
|
||||
@@ -391,6 +430,68 @@ isc_nm_listenstreamdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
* 'quota' is passed to isc_nm_listentcp() when opening the raw TCP socket.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_nm_listenproxystream(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
|
||||
int backlog, isc_quota_t *quota,
|
||||
isc_nmsocket_t **sockp);
|
||||
/*%<
|
||||
* Start listening for data preceded by a PROXYv2 header over the
|
||||
* TCP on interface 'iface', using net manager 'mgr'.
|
||||
*
|
||||
* On success, 'sockp' will be updated to contain a new listening TCP
|
||||
* socket.
|
||||
*
|
||||
* When connection is accepted on the socket, 'accept_cb' will be called with
|
||||
* 'accept_cbarg' as its argument. The callback is expected to start a read.
|
||||
*
|
||||
* If 'quota' is not NULL, then the socket is attached to the specified
|
||||
* quota. This allows us to enforce TCP client quota limits.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_proxystreamconnect(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);
|
||||
/*%<
|
||||
* Create a TCP socket using netmgr 'mgr', bind it to the address
|
||||
* 'local', and connect it to the address 'peer'. Right after the
|
||||
* connection has been established, send PROXYv2 header using the
|
||||
* information provided via the 'proxy_info' to the remote peer. Then
|
||||
* the connection is considered established.
|
||||
*
|
||||
* If 'proxy_info' is omitted, then a LOCAL PROXYv2 header is sent.
|
||||
*
|
||||
* When the connection is established or has timed out, call 'cb' with
|
||||
* argument 'cbarg'.
|
||||
*
|
||||
* 'timeout' specifies the timeout interval in milliseconds.
|
||||
*
|
||||
* The connected socket can only be accessed via the handle passed to
|
||||
* 'cb'.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_proxyheader_info_init(isc_nm_proxyheader_info_t *restrict info,
|
||||
isc_sockaddr_t *restrict src_addr,
|
||||
isc_sockaddr_t *restrict dst_addr,
|
||||
isc_region_t *restrict tlv_data);
|
||||
/*%<
|
||||
* Initialize a 'isc_nm_proxyheader_info_t' object with user
|
||||
* provided addresses and a TLVs blob, that can be omitted (the rest
|
||||
* of the data is REQUIRE()d).
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_proxyheader_info_init_complete(isc_nm_proxyheader_info_t *restrict info,
|
||||
isc_region_t *restrict header_data);
|
||||
/*%<
|
||||
* Initialize a 'isc_nm_proxyheader_info_t' with user provided data
|
||||
* blob (e.g. a pre-rendered PROXYv2 header for forwarding or
|
||||
* testing).
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
|
||||
uint32_t keepalive, uint32_t advertised);
|
||||
@@ -499,6 +600,19 @@ isc_nm_is_http_handle(isc_nmhandle_t *handle);
|
||||
* 'isc_nm_httpsocket'.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc_nm_is_proxy_unspec(isc_nmhandle_t *handle);
|
||||
/*%<
|
||||
* Returns 'true' iff 'handle' is associated with a peer who send
|
||||
* a PROXYv2 header with unsupported address type.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc_nm_is_proxy_handle(isc_nmhandle_t *handle);
|
||||
/*%< Returns 'true' iff 'handle' is associated is with a PROXYv2
|
||||
* connection.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_nm_listentls(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface,
|
||||
isc_nm_accept_cb_t accept_cb, void *accept_cbarg, int backlog,
|
||||
|
||||
@@ -98,13 +98,15 @@ typedef enum isc_nmsocket_type {
|
||||
isc_nm_tlssocket = 1 << 3,
|
||||
isc_nm_httpsocket = 1 << 4,
|
||||
isc_nm_streamdnssocket = 1 << 5,
|
||||
isc_nm_proxystreamsocket = 1 << 6,
|
||||
isc_nm_maxsocket,
|
||||
|
||||
isc_nm_udplistener, /* Aggregate of nm_udpsocks */
|
||||
isc_nm_tcplistener,
|
||||
isc_nm_tlslistener,
|
||||
isc_nm_httplistener,
|
||||
isc_nm_streamdnslistener
|
||||
isc_nm_streamdnslistener,
|
||||
isc_nm_proxystreamlistener
|
||||
} isc_nmsocket_type;
|
||||
|
||||
typedef isc_nmsocket_type isc_nmsocket_type_t;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <isc/magic.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/netmgr.h>
|
||||
#include <isc/proxy2.h>
|
||||
#include <isc/quota.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/refcount.h>
|
||||
@@ -111,6 +112,9 @@ STATIC_ASSERT(ISC_NETMGR_TCP_RECVBUF_SIZE <= ISC_NETMGR_RECVBUF_SIZE,
|
||||
#define ISC_NM_NMHANDLES_MAX 64
|
||||
#define ISC_NM_UVREQS_MAX 64
|
||||
|
||||
/*% ISC_PROXY2_MIN_AF_UNIX_SIZE is the largest type when TLVs are not used */
|
||||
#define ISC_NM_PROXY2_DEFAULT_BUFFER_SIZE (ISC_PROXY2_MIN_AF_UNIX_SIZE)
|
||||
|
||||
/*
|
||||
* Define ISC_NETMGR_TRACE to activate tracing of handles and sockets.
|
||||
* This will impair performance but enables us to quickly determine,
|
||||
@@ -244,6 +248,7 @@ struct isc_nmhandle {
|
||||
|
||||
isc_sockaddr_t peer;
|
||||
isc_sockaddr_t local;
|
||||
bool proxy_is_unspec;
|
||||
isc_nm_opaquecb_t doreset; /* reset extra callback, external */
|
||||
isc_nm_opaquecb_t dofree; /* free extra callback, external */
|
||||
#if ISC_NETMGR_TRACE
|
||||
@@ -536,6 +541,20 @@ struct isc_nmsocket {
|
||||
bool dot_alpn_negotiated;
|
||||
const char *tls_verify_error;
|
||||
} streamdns;
|
||||
|
||||
struct {
|
||||
isc_nmsocket_t *sock;
|
||||
bool reading;
|
||||
size_t nsending;
|
||||
void *send_req;
|
||||
union {
|
||||
isc_proxy2_handler_t *handler; /* server */
|
||||
isc_buffer_t *outbuf; /* client */
|
||||
} proxy2;
|
||||
bool header_processed;
|
||||
bool extra_processed; /* data arrived past header processed */
|
||||
} proxy;
|
||||
|
||||
/*%
|
||||
* pquota is a non-attached pointer to the TCP client quota, stored in
|
||||
* listening sockets.
|
||||
@@ -1136,6 +1155,71 @@ void
|
||||
isc__nm_streamdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
|
||||
bool async);
|
||||
|
||||
bool
|
||||
isc__nm_valid_proxy_addresses(const isc_sockaddr_t *src,
|
||||
const isc_sockaddr_t *dst);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
|
||||
bool async);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_stoplistening(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_cleanup_data(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_cleartimeout(isc_nmhandle_t *handle);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_keepalive(isc_nmhandle_t *handle, bool value);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_setwritetimeout(isc_nmhandle_t *handle,
|
||||
uint64_t write_timeout);
|
||||
|
||||
void
|
||||
isc__nmsocket_proxystream_reset(isc_nmsocket_t *sock);
|
||||
|
||||
bool
|
||||
isc__nmsocket_proxystream_timer_running(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmsocket_proxystream_timer_restart(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmsocket_proxystream_timer_stop(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nmhandle_proxystream_set_manual_timer(isc_nmhandle_t *handle,
|
||||
const bool manual);
|
||||
|
||||
isc_result_t
|
||||
isc__nmhandle_proxystream_set_tcp_nodelay(isc_nmhandle_t *handle,
|
||||
const bool value);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_read_stop(isc_nmhandle_t *handle);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_close(isc_nmsocket_t *sock);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb,
|
||||
void *cbarg);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_send(isc_nmhandle_t *handle, isc_region_t *region,
|
||||
isc_nm_cb_t cb, void *cbarg);
|
||||
|
||||
void
|
||||
isc__nm_proxystream_senddns(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);
|
||||
/*%<
|
||||
@@ -1323,6 +1407,14 @@ void
|
||||
isc__nmhandle_log(const isc_nmhandle_t *handle, int level, const char *fmt, ...)
|
||||
ISC_FORMAT_PRINTF(3, 4);
|
||||
|
||||
void
|
||||
isc__nm_received_proxy_header_log(isc_nmhandle_t *handle,
|
||||
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);
|
||||
|
||||
void
|
||||
isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual);
|
||||
/*
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <isc/loop.h>
|
||||
#include <isc/magic.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/netaddr.h>
|
||||
#include <isc/netmgr.h>
|
||||
#include <isc/quota.h>
|
||||
#include <isc/random.h>
|
||||
@@ -319,6 +320,10 @@ isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nmhandle_streamdns_setwritetimeout(handle, write_timeout);
|
||||
break;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_setwritetimeout(handle,
|
||||
write_timeout);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -469,6 +474,7 @@ nmsocket_cleanup(void *arg) {
|
||||
isc__nm_http_cleanup_data(sock);
|
||||
#endif
|
||||
isc__nm_streamdns_cleanup_data(sock);
|
||||
isc__nm_proxystream_cleanup_data(sock);
|
||||
|
||||
if (sock->barriers_initialised) {
|
||||
isc_barrier_destroy(&sock->listen_barrier);
|
||||
@@ -601,6 +607,9 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
|
||||
isc__nm_http_close(sock);
|
||||
return;
|
||||
#endif
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_close(sock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -645,7 +654,8 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) {
|
||||
(*sockp)->type == isc_nm_tcplistener ||
|
||||
(*sockp)->type == isc_nm_streamdnslistener ||
|
||||
(*sockp)->type == isc_nm_tlslistener ||
|
||||
(*sockp)->type == isc_nm_httplistener);
|
||||
(*sockp)->type == isc_nm_httplistener ||
|
||||
(*sockp)->type == isc_nm_proxystreamlistener);
|
||||
|
||||
isc__nmsocket_detach(sockp);
|
||||
}
|
||||
@@ -844,6 +854,7 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t const *peer,
|
||||
FALLTHROUGH;
|
||||
case isc_nm_tcpsocket:
|
||||
case isc_nm_tlssocket:
|
||||
case isc_nm_proxystreamsocket:
|
||||
INSIST(sock->statichandle == NULL);
|
||||
|
||||
/*
|
||||
@@ -875,7 +886,8 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
|
||||
return (handle->sock->type == isc_nm_tcpsocket ||
|
||||
handle->sock->type == isc_nm_tlssocket ||
|
||||
handle->sock->type == isc_nm_httpsocket ||
|
||||
handle->sock->type == isc_nm_streamdnssocket);
|
||||
handle->sock->type == isc_nm_streamdnssocket ||
|
||||
handle->sock->type == isc_nm_proxystreamsocket);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1030,6 +1042,9 @@ isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nm_streamdns_failed_read_cb(sock, result, async);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_failed_read_cb(sock, result, async);
|
||||
return;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1133,6 +1148,9 @@ isc__nmsocket_timer_restart(isc_nmsocket_t *sock) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nmsocket_streamdns_timer_restart(sock);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmsocket_proxystream_timer_restart(sock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1176,6 +1194,8 @@ isc__nmsocket_timer_running(isc_nmsocket_t *sock) {
|
||||
return (isc__nmsocket_tls_timer_running(sock));
|
||||
case isc_nm_streamdnssocket:
|
||||
return (isc__nmsocket_streamdns_timer_running(sock));
|
||||
case isc_nm_proxystreamsocket:
|
||||
return (isc__nmsocket_proxystream_timer_running(sock));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1207,6 +1227,9 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nmsocket_streamdns_timer_stop(sock);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmsocket_proxystream_timer_stop(sock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1228,6 +1251,7 @@ isc___nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr FLARG) {
|
||||
switch (sock->type) {
|
||||
case isc_nm_tcpsocket:
|
||||
case isc_nm_tlssocket:
|
||||
case isc_nm_proxystreamsocket:
|
||||
#if ISC_NETMGR_TRACE
|
||||
isc_nmhandle__attach(sock->statichandle,
|
||||
&req->handle FLARG_PASS);
|
||||
@@ -1380,6 +1404,9 @@ isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nmhandle_streamdns_cleartimeout(handle);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_cleartimeout(handle);
|
||||
return;
|
||||
default:
|
||||
handle->sock->read_timeout = 0;
|
||||
|
||||
@@ -1406,6 +1433,9 @@ isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nmhandle_streamdns_settimeout(handle, timeout);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_settimeout(handle, timeout);
|
||||
return;
|
||||
default:
|
||||
handle->sock->read_timeout = timeout;
|
||||
isc__nmsocket_timer_restart(handle->sock);
|
||||
@@ -1446,6 +1476,9 @@ isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) {
|
||||
isc__nmhandle_http_keepalive(handle, value);
|
||||
break;
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_keepalive(handle, value);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* For any other protocol, this is a no-op.
|
||||
@@ -1559,6 +1592,9 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
|
||||
isc__nm_http_send(handle, region, cb, cbarg);
|
||||
break;
|
||||
#endif
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_send(handle, region, cb, cbarg);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1576,6 +1612,9 @@ isc__nm_senddns(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
|
||||
case isc_nm_tlssocket:
|
||||
isc__nm_tls_senddns(handle, region, cb, cbarg);
|
||||
break;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_senddns(handle, region, cb, cbarg);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1603,6 +1642,9 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
|
||||
isc__nm_http_read(handle, cb, cbarg);
|
||||
break;
|
||||
#endif
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_read(handle, cb, cbarg);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1654,6 +1696,9 @@ isc_nm_read_stop(isc_nmhandle_t *handle) {
|
||||
case isc_nm_tlssocket:
|
||||
isc__nm_tls_read_stop(handle);
|
||||
break;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nm_proxystream_read_stop(handle);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1690,6 +1735,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) {
|
||||
isc__nm_http_stoplistening(sock);
|
||||
break;
|
||||
#endif
|
||||
case isc_nm_proxystreamlistener:
|
||||
isc__nm_proxystream_stoplistening(sock);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1702,7 +1750,8 @@ isc__nmsocket_stop(isc_nmsocket_t *listener) {
|
||||
REQUIRE(listener->tid == 0);
|
||||
REQUIRE(listener->type == isc_nm_httplistener ||
|
||||
listener->type == isc_nm_tlslistener ||
|
||||
listener->type == isc_nm_streamdnslistener);
|
||||
listener->type == isc_nm_streamdnslistener ||
|
||||
listener->type == isc_nm_proxystreamlistener);
|
||||
REQUIRE(!listener->closing);
|
||||
|
||||
listener->closing = true;
|
||||
@@ -1833,6 +1882,9 @@ isc__nmsocket_reset(isc_nmsocket_t *sock) {
|
||||
case isc_nm_streamdnssocket:
|
||||
isc__nmsocket_streamdns_reset(sock);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmsocket_proxystream_reset(sock);
|
||||
return;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -2042,6 +2094,7 @@ isc_nm_bad_request(isc_nmhandle_t *handle) {
|
||||
case isc_nm_tcpsocket:
|
||||
case isc_nm_streamdnssocket:
|
||||
case isc_nm_tlssocket:
|
||||
case isc_nm_proxystreamsocket:
|
||||
REQUIRE(sock->parent == NULL);
|
||||
isc__nmsocket_reset(sock);
|
||||
return;
|
||||
@@ -2085,6 +2138,162 @@ isc_nm_is_http_handle(isc_nmhandle_t *handle) {
|
||||
return (handle->sock->type == isc_nm_httpsocket);
|
||||
}
|
||||
|
||||
static isc_nmhandle_t *
|
||||
get_proxy_handle(isc_nmhandle_t *handle) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
|
||||
sock = handle->sock;
|
||||
|
||||
switch (sock->type) {
|
||||
case isc_nm_proxystreamsocket:
|
||||
return (handle);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (sock->outerhandle == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (get_proxy_handle(sock->outerhandle));
|
||||
}
|
||||
|
||||
bool
|
||||
isc_nm_is_proxy_handle(isc_nmhandle_t *handle) {
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
|
||||
return (get_proxy_handle(handle) != NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
isc_nm_is_proxy_unspec(isc_nmhandle_t *handle) {
|
||||
isc_nmhandle_t *proxyhandle;
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
REQUIRE(VALID_NMSOCK(handle->sock));
|
||||
|
||||
if (handle->sock->client) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
proxyhandle = get_proxy_handle(handle);
|
||||
if (proxyhandle == NULL) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (proxyhandle->proxy_is_unspec);
|
||||
}
|
||||
|
||||
isc_sockaddr_t
|
||||
isc_nmhandle_real_peeraddr(isc_nmhandle_t *handle) {
|
||||
isc_sockaddr_t addr = { 0 };
|
||||
isc_nmhandle_t *proxyhandle;
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
|
||||
proxyhandle = get_proxy_handle(handle);
|
||||
if (proxyhandle == NULL) {
|
||||
return (isc_nmhandle_peeraddr(handle));
|
||||
}
|
||||
|
||||
INSIST(VALID_NMSOCK(proxyhandle->sock));
|
||||
|
||||
if (isc_nmhandle_is_stream(proxyhandle)) {
|
||||
addr = isc_nmhandle_peeraddr(proxyhandle->sock->outerhandle);
|
||||
} else {
|
||||
/* TODO: PROXY over UDP */
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return (addr);
|
||||
}
|
||||
|
||||
isc_sockaddr_t
|
||||
isc_nmhandle_real_localaddr(isc_nmhandle_t *handle) {
|
||||
isc_sockaddr_t addr = { 0 };
|
||||
isc_nmhandle_t *proxyhandle;
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
|
||||
proxyhandle = get_proxy_handle(handle);
|
||||
if (proxyhandle == NULL) {
|
||||
return (isc_nmhandle_localaddr(handle));
|
||||
}
|
||||
|
||||
INSIST(VALID_NMSOCK(proxyhandle->sock));
|
||||
|
||||
if (isc_nmhandle_is_stream(proxyhandle)) {
|
||||
addr = isc_nmhandle_localaddr(proxyhandle->sock->outerhandle);
|
||||
} else {
|
||||
/* TODO: PROXY over UDP */
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return (addr);
|
||||
}
|
||||
|
||||
bool
|
||||
isc__nm_valid_proxy_addresses(const isc_sockaddr_t *src,
|
||||
const isc_sockaddr_t *dst) {
|
||||
struct in_addr inv4 = { 0 };
|
||||
struct in6_addr inv6 = { 0 };
|
||||
isc_netaddr_t zerov4 = { 0 }, zerov6 = { 0 };
|
||||
isc_netaddr_t src_addr = { 0 }, dst_addr = { 0 };
|
||||
|
||||
if (src == NULL || dst == NULL) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*
|
||||
* We should not allow using 0 in source addresses as well, but we
|
||||
* have a precedent of a tool that issues port 0 in the source
|
||||
* addresses (kdig).
|
||||
*/
|
||||
if (isc_sockaddr_getport(dst) == 0) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Anybody using zeroes in source or destination addresses is not
|
||||
* a friend. Considering that most of the upper level code is
|
||||
* written with consideration that bot source and destination
|
||||
* addresses are returned by the OS and should be valid, we should
|
||||
* discard so suspicious addresses. Also, keep in mind that both
|
||||
* "0.0.0.0" and "::" match all interfaces when using as listener
|
||||
* addresses.
|
||||
*/
|
||||
isc_netaddr_fromin(&zerov4, &inv4);
|
||||
isc_netaddr_fromin6(&zerov6, &inv6);
|
||||
|
||||
isc_netaddr_fromsockaddr(&src_addr, src);
|
||||
isc_netaddr_fromsockaddr(&dst_addr, dst);
|
||||
|
||||
INSIST(isc_sockaddr_pf(src) == isc_sockaddr_pf(dst));
|
||||
|
||||
switch (isc_sockaddr_pf(src)) {
|
||||
case AF_INET:
|
||||
if (isc_netaddr_equal(&src_addr, &zerov4)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (isc_netaddr_equal(&dst_addr, &zerov4)) {
|
||||
return (false);
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (isc_netaddr_equal(&src_addr, &zerov6)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (isc_netaddr_equal(&dst_addr, &zerov6)) {
|
||||
return (false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
@@ -2110,6 +2319,7 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
|
||||
break;
|
||||
case isc_nm_tcpsocket:
|
||||
case isc_nm_tlssocket:
|
||||
case isc_nm_proxystreamsocket:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -2157,6 +2367,10 @@ isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
|
||||
case isc_nm_tlssocket:
|
||||
return (isc__nm_tls_verify_tls_peer_result_string(handle));
|
||||
break;
|
||||
case isc_nm_proxystreamsocket:
|
||||
return (isc__nm_proxystream_verify_tls_peer_result_string(
|
||||
handle));
|
||||
break;
|
||||
#if HAVE_LIBNGHTTP2
|
||||
case isc_nm_httpsocket:
|
||||
return (isc__nm_http_verify_tls_peer_result_string(handle));
|
||||
@@ -2351,6 +2565,96 @@ isc__nmhandle_log(const isc_nmhandle_t *handle, int level, const char *fmt,
|
||||
isc__nmsocket_log(handle->sock, level, "handle %p: %s", handle, msgbuf);
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_received_proxy_header_log(isc_nmhandle_t *handle,
|
||||
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 int log_level = ISC_LOG_DEBUG(1);
|
||||
isc_sockaddr_t real_local, real_peer;
|
||||
char real_local_fmt[ISC_SOCKADDR_FORMATSIZE] = { 0 };
|
||||
char real_peer_fmt[ISC_SOCKADDR_FORMATSIZE] = { 0 };
|
||||
char common_msg[512] = { 0 };
|
||||
const char *proto = NULL;
|
||||
const char *real_addresses_msg =
|
||||
"real source and destination addresses are used";
|
||||
|
||||
if (!isc_log_wouldlog(isc_lctx, log_level)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isc_nmhandle_is_stream(handle)) {
|
||||
proto = isc_nm_has_encryption(handle) ? "TLS" : "TCP";
|
||||
} else {
|
||||
proto = "UDP";
|
||||
}
|
||||
|
||||
real_local = isc_nmhandle_real_localaddr(handle);
|
||||
real_peer = isc_nmhandle_real_peeraddr(handle);
|
||||
|
||||
isc_sockaddr_format(&real_local, real_local_fmt,
|
||||
sizeof(real_local_fmt));
|
||||
|
||||
isc_sockaddr_format(&real_peer, real_peer_fmt, sizeof(real_peer_fmt));
|
||||
|
||||
(void)snprintf(common_msg, sizeof(common_msg),
|
||||
"Received a PROXYv2 header from %s on %s over %s",
|
||||
real_peer_fmt, real_local_fmt, proto);
|
||||
|
||||
if (cmd == ISC_PROXY2_CMD_LOCAL) {
|
||||
isc_log_write(isc_lctx, ISC_LOGCATEGORY_DEFAULT,
|
||||
ISC_LOGMODULE_NETMGR, log_level,
|
||||
"%s: command: LOCAL (%s)", common_msg,
|
||||
real_addresses_msg);
|
||||
return;
|
||||
} else if (cmd == ISC_PROXY2_CMD_PROXY) {
|
||||
const char *tlvs_msg = tlvs == NULL ? "no" : "yes";
|
||||
const char *socktype_name = NULL;
|
||||
const char *src_addr_msg = "(none)", *dst_addr_msg = "(none)";
|
||||
char src_addr_fmt[ISC_SOCKADDR_FORMATSIZE] = { 0 };
|
||||
char dst_addr_fmt[ISC_SOCKADDR_FORMATSIZE] = { 0 };
|
||||
|
||||
switch (socktype) {
|
||||
case 0:
|
||||
isc_log_write(isc_lctx, ISC_LOGCATEGORY_DEFAULT,
|
||||
ISC_LOGMODULE_NETMGR, log_level,
|
||||
"%s: command: PROXY (unspecified address "
|
||||
"and socket type, %s)",
|
||||
common_msg, real_addresses_msg);
|
||||
return;
|
||||
case SOCK_STREAM:
|
||||
socktype_name = "SOCK_STREAM";
|
||||
break;
|
||||
case SOCK_DGRAM:
|
||||
socktype_name = "SOCK_DGRAM";
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (src_addr) {
|
||||
isc_sockaddr_format(src_addr, src_addr_fmt,
|
||||
sizeof(src_addr_fmt));
|
||||
src_addr_msg = src_addr_fmt;
|
||||
}
|
||||
|
||||
if (dst_addr) {
|
||||
isc_sockaddr_format(dst_addr, dst_addr_fmt,
|
||||
sizeof(dst_addr_fmt));
|
||||
dst_addr_msg = dst_addr_fmt;
|
||||
}
|
||||
|
||||
isc_log_write(isc_lctx, ISC_LOGCATEGORY_DEFAULT,
|
||||
ISC_LOGMODULE_NETMGR, log_level,
|
||||
"%s: command: PROXY, socket type: %s, source: "
|
||||
"%s, destination: %s, TLVs: %s",
|
||||
common_msg, socktype_name, src_addr_msg,
|
||||
dst_addr_msg, tlvs_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
|
||||
REQUIRE(VALID_NMHANDLE(handle));
|
||||
@@ -2365,6 +2669,9 @@ isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
|
||||
case isc_nm_tlssocket:
|
||||
isc__nmhandle_tls_set_manual_timer(handle, manual);
|
||||
return;
|
||||
case isc_nm_proxystreamsocket:
|
||||
isc__nmhandle_proxystream_set_manual_timer(handle, manual);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
@@ -2409,6 +2716,10 @@ isc_nmhandle_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value) {
|
||||
case isc_nm_tlssocket:
|
||||
result = isc__nmhandle_tls_set_tcp_nodelay(handle, value);
|
||||
break;
|
||||
case isc_nm_proxystreamsocket:
|
||||
result = isc__nmhandle_proxystream_set_tcp_nodelay(handle,
|
||||
value);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -2423,6 +2734,36 @@ isc_nmsocket_getaddr(isc_nmsocket_t *sock) {
|
||||
return (sock->iface);
|
||||
}
|
||||
|
||||
void
|
||||
isc_nm_proxyheader_info_init(isc_nm_proxyheader_info_t *restrict info,
|
||||
isc_sockaddr_t *restrict src_addr,
|
||||
isc_sockaddr_t *restrict dst_addr,
|
||||
isc_region_t *restrict tlv_data) {
|
||||
REQUIRE(info != NULL);
|
||||
REQUIRE(src_addr != NULL);
|
||||
REQUIRE(dst_addr != NULL);
|
||||
REQUIRE(tlv_data == NULL ||
|
||||
(tlv_data->length > 0 && tlv_data->base != NULL));
|
||||
|
||||
*info = (isc_nm_proxyheader_info_t){ .proxy_info.src_addr = *src_addr,
|
||||
.proxy_info.dst_addr = *dst_addr };
|
||||
if (tlv_data != NULL) {
|
||||
info->proxy_info.tlv_data = *tlv_data;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_nm_proxyheader_info_init_complete(isc_nm_proxyheader_info_t *restrict info,
|
||||
isc_region_t *restrict header_data) {
|
||||
REQUIRE(info != NULL);
|
||||
REQUIRE(header_data != NULL);
|
||||
REQUIRE(header_data->base != NULL &&
|
||||
header_data->length >= ISC_PROXY2_HEADER_SIZE);
|
||||
|
||||
*info = (isc_nm_proxyheader_info_t){ .complete = true,
|
||||
.complete_header = *header_data };
|
||||
}
|
||||
|
||||
#if ISC_NETMGR_TRACE
|
||||
/*
|
||||
* Dump all active sockets in netmgr. We output to stderr
|
||||
@@ -2452,6 +2793,10 @@ nmsocket_type_totext(isc_nmsocket_type type) {
|
||||
return ("isc_nm_streamdnslistener");
|
||||
case isc_nm_streamdnssocket:
|
||||
return ("isc_nm_streamdnssocket");
|
||||
case isc_nm_proxystreamlistener:
|
||||
return ("isc_nm_proxystreamlistener");
|
||||
case isc_nm_proxystreamsocket:
|
||||
return ("isc_nm_proxystreamsocket");
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
1089
lib/isc/netmgr/proxystream.c
Normal file
1089
lib/isc/netmgr/proxystream.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user