From ace903fd070c32a8c84e370775f718dc8038b5e9 Mon Sep 17 00:00:00 2001 From: Bertold Van den Bergh Date: Wed, 18 Dec 2019 23:41:33 +0100 Subject: [PATCH] Add support for loadbalanced TCP connections (haproxy protocol v2) --- README.turnserver | 4 + examples/etc/turnserver.conf | 8 ++ man/man1/turnserver.1 | 8 +- src/apps/common/apputils.c | 1 + src/apps/relay/mainrelay.c | 12 ++- src/apps/relay/mainrelay.h | 2 + src/apps/relay/netengine.c | 8 +- src/apps/relay/ns_ioalib_engine_impl.c | 124 ++++++++++++++++++++----- src/apps/relay/tls_listener.c | 4 +- src/server/ns_turn_ioalib.h | 1 + 10 files changed, 142 insertions(+), 30 deletions(-) diff --git a/README.turnserver b/README.turnserver index 4f2b28d0..21ef455d 100644 --- a/README.turnserver +++ b/README.turnserver @@ -321,6 +321,10 @@ Options with values: --alt-tls-listening-port Alternative listening port for TLS and DTLS protocols. Default (or zero) value means "TLS listening port plus one". + +--tcp-proxy-port Support connections from TCP loadbalancer on this port. The loadbalancer + should use the binary proxy protocol. + (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) --aux-server Auxiliary STUN/TURN server listening endpoint. Aux servers have almost full TURN and STUN functionality. diff --git a/examples/etc/turnserver.conf b/examples/etc/turnserver.conf index d8189f53..0855be8b 100644 --- a/examples/etc/turnserver.conf +++ b/examples/etc/turnserver.conf @@ -44,6 +44,14 @@ # Default (or zero) value means "TLS listening port plus one". # #alt-tls-listening-port=0 + +# Some network setups will require using a TCP reverse proxy in front +# of the STUN server. If the proxy port option is set a single listener +# is started on the given port that accepts connections using the +# haproxy proxy protocol v2. +# (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) +# +#tcp-proxy-port=5555 # Listener IP address of relay server. Multiple listeners can be specified. # If no IP(s) specified in the config file or in the command line options, diff --git a/man/man1/turnserver.1 b/man/man1/turnserver.1 index 4cbac6d3..8d59028c 100644 --- a/man/man1/turnserver.1 +++ b/man/man1/turnserver.1 @@ -1,5 +1,5 @@ .\" Text automatically generated by txt2man -.TH TURN 1 "29 January 2019" "" "" +.TH TURN 1 "12 February 2020" "" "" .SH GENERAL INFORMATION The \fBTURN Server\fP project contains the source code of a TURN server and TURN client @@ -483,6 +483,12 @@ Alternative listening port for TLS and DTLS protocols. Default (or zero) value means "TLS listening port plus one". .TP .B +\fB\-\-tcp\-proxy\-port\fP +Support connections from TCP loadbalancer on this port. The loadbalancer +should use the binary proxy protocol. +(https://www.haproxy.org/download/1.8/doc/proxy\-protocol.txt) +.TP +.B \fB\-\-aux\-server\fP Auxiliary STUN/TURN server listening endpoint. Aux servers have almost full TURN and STUN functionality. diff --git a/src/apps/common/apputils.c b/src/apps/common/apputils.c index 2dbb923e..13c1f9b2 100644 --- a/src/apps/common/apputils.c +++ b/src/apps/common/apputils.c @@ -439,6 +439,7 @@ int set_raw_socket_tos(evutil_socket_t fd, int family, int tos) int is_stream_socket(int st) { switch(st) { case TCP_SOCKET: + case TCP_SOCKET_PROXY: case TLS_SOCKET: case TENTATIVE_TCP_SOCKET: case SCTP_SOCKET: diff --git a/src/apps/relay/mainrelay.c b/src/apps/relay/mainrelay.c index 6710da6d..c5941ef6 100644 --- a/src/apps/relay/mainrelay.c +++ b/src/apps/relay/mainrelay.c @@ -110,8 +110,8 @@ NULL, PTHREAD_MUTEX_INITIALIZER, //////////////// Common params //////////////////// TURN_VERBOSE_NONE,0,0,0,0, "/var/run/turnserver.pid", -DEFAULT_STUN_PORT,DEFAULT_STUN_TLS_PORT,0,0,1, -0,0,0,0, +DEFAULT_STUN_PORT,DEFAULT_STUN_TLS_PORT,0,0,0,1, +0,0,0,0,0, "", "",0, { @@ -402,6 +402,8 @@ static char Usage[] = "Usage: turnserver [options]\n" " or in old RFC 3489 sense, default is \"listening port plus one\").\n" " --alt-tls-listening-port Alternative listening port for TLS and DTLS,\n" " the default is \"TLS/DTLS port plus one\".\n" +" --tcp-proxy-port Support connections from TCP loadbalancer on this port. The loadbalancer should\n" +" use the binary proxy protocol (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)\n" " -L, --listening-ip Listener IP address of relay server. Multiple listeners can be specified.\n" " --aux-server Auxiliary STUN/TURN server listening endpoint.\n" " Auxiliary servers do not have alternative ports and\n" @@ -713,6 +715,7 @@ static char AdminUsage[] = "Usage: turnadmin [command] [options]\n" enum EXTRA_OPTS { NO_UDP_OPT=256, NO_TCP_OPT, + TCP_PROXY_PORT_OPT, NO_TLS_OPT, NO_DTLS_OPT, NO_UDP_RELAY_OPT, @@ -805,6 +808,7 @@ static const struct myoption long_options[] = { { "tls-listening-port", required_argument, NULL, TLS_PORT_OPT }, { "alt-listening-port", required_argument, NULL, ALT_PORT_OPT }, { "alt-tls-listening-port", required_argument, NULL, ALT_TLS_PORT_OPT }, + { "tcp-proxy-port", required_argument, NULL, TCP_PROXY_PORT_OPT }, { "listening-ip", required_argument, NULL, 'L' }, { "relay-device", required_argument, NULL, 'i' }, { "relay-ip", required_argument, NULL, 'E' }, @@ -1266,6 +1270,10 @@ static void set_option(int c, char *value) case ALT_TLS_PORT_OPT: turn_params.alt_tls_listener_port = atoi(value); break; + case TCP_PROXY_PORT_OPT: + turn_params.tcp_proxy_port = atoi(value); + turn_params.tcp_use_proxy = 1; + break; case MIN_PORT_OPT: turn_params.min_port = atoi(value); break; diff --git a/src/apps/relay/mainrelay.h b/src/apps/relay/mainrelay.h index ea139846..779c09fd 100644 --- a/src/apps/relay/mainrelay.h +++ b/src/apps/relay/mainrelay.h @@ -226,10 +226,12 @@ typedef struct _turn_params_ { int tls_listener_port; int alt_listener_port; int alt_tls_listener_port; + int tcp_proxy_port; int rfc5780; int no_udp; int no_tcp; + int tcp_use_proxy; vint no_tcp_relay; vint no_udp_relay; diff --git a/src/apps/relay/netengine.c b/src/apps/relay/netengine.c index 1a69dcd8..82139c2c 100644 --- a/src/apps/relay/netengine.c +++ b/src/apps/relay/netengine.c @@ -1473,7 +1473,7 @@ static void setup_tcp_listener_servers(ioa_engine_handle e, struct relay_server /* Create listeners */ /* Aux TCP servers */ - if(!turn_params.no_tls || !turn_params.no_tcp) { + if(!turn_params.tcp_use_proxy && (!turn_params.no_tls || !turn_params.no_tcp)) { for(i=0; i> 4; + if(version != 2) return -1; + + /* Read data */ + uint8_t command = buf[12] & 0xF; + uint8_t family = buf[13] >> 4; + uint8_t proto = buf[13] & 0xF; + size_t plen = ((size_t)buf[14] << 8) | buf[15]; + + size_t tlen = 16 + plen; + if(len < tlen) return 0; + + /* A local connection is used by the proxy itself and does not carry a valid address */ + if(command == 0) return tlen; + + /* Accept only proxied TCP connections */ + if(command != 1 || proto != 1) return -1; + + /* Read the address */ + if(family == 1 && plen >= 12){ /* IPv4 */ + struct sockaddr_in remote, local; + remote.sin_family = local.sin_family = AF_INET; + memcpy(&remote.sin_addr.s_addr, &buf[16], 4); + memcpy(&local.sin_addr.s_addr, &buf[20], 4); + memcpy(&remote.sin_port, &buf[24], 2); + memcpy(&local.sin_port, &buf[26], 2); + + addr_cpy4(&(s->local_addr), &local); + addr_cpy4(&(s->remote_addr), &remote); + + }else if(family == 2 && plen >= 36){ /* IPv6 */ + struct sockaddr_in6 remote, local; + remote.sin6_family = local.sin6_family = AF_INET6; + memcpy(&remote.sin6_addr.s6_addr, &buf[16], 16); + memcpy(&local.sin6_addr.s6_addr, &buf[32], 16); + memcpy(&remote.sin6_port, &buf[48], 2); + memcpy(&local.sin6_port, &buf[50], 2); + + addr_cpy6(&(s->local_addr), &local); + addr_cpy6(&(s->remote_addr), &remote); + + }else{ + return -1; + } + + return tlen; +} + static int socket_input_worker(ioa_socket_handle s) { int len = 0; @@ -2372,39 +2433,57 @@ static int socket_input_worker(ioa_socket_handle s) struct evbuffer *inbuf = bufferevent_get_input(s->bev); if(inbuf) { ev_ssize_t blen = evbuffer_copyout(inbuf, buf_elem->buf.buf, STUN_BUFFER_SIZE); + if(blen>0) { int mlen = 0; if(blen>(ev_ssize_t)STUN_BUFFER_SIZE) blen=(ev_ssize_t)STUN_BUFFER_SIZE; - if(is_stream_socket(s->st) && ((s->sat == TCP_CLIENT_DATA_SOCKET)||(s->sat==TCP_RELAY_DATA_SOCKET))) { - mlen = blen; - } else { - mlen = stun_get_message_len_str(buf_elem->buf.buf, blen, 1, &app_msg_len); - } - - if(mlen>0 && mlen<=(int)blen) { - len = (int)bufferevent_read(s->bev, buf_elem->buf.buf, mlen); - if(len < 0) { - ret = -1; + if(s->st == TCP_SOCKET_PROXY){ + ssize_t tlen = socket_parse_proxy_v2(s, buf_elem->buf.buf, blen); + blen = 0; + if (tlen < 0){ s->tobeclosed = 1; s->broken = 1; - log_socket_event(s, "socket read failed, to be closed",1); - } else if((s->st == TLS_SOCKET)||(s->st == TLS_SCTP_SOCKET)) { -#if TLS_SUPPORTED - SSL *ctx = bufferevent_openssl_get_ssl(s->bev); - if(!ctx || SSL_get_shutdown(ctx)) { - ret = -1; - s->tobeclosed = 1; - } -#endif - } - if(ret != -1) { - ret = len; + ret = -1; + log_socket_event(s, "proxy protocol violated",1); + }else if(tlen > 0){ + bufferevent_read(s->bev, buf_elem->buf.buf, tlen); + + blen = evbuffer_copyout(inbuf, buf_elem->buf.buf, STUN_BUFFER_SIZE); + s->st = TCP_SOCKET; } } + if(blen){ + if(is_stream_socket(s->st) && ((s->sat == TCP_CLIENT_DATA_SOCKET)||(s->sat==TCP_RELAY_DATA_SOCKET))) { + mlen = blen; + } else { + mlen = stun_get_message_len_str(buf_elem->buf.buf, blen, 1, &app_msg_len); + } + + if(mlen>0 && mlen<=(int)blen) { + len = (int)bufferevent_read(s->bev, buf_elem->buf.buf, mlen); + if(len < 0) { + ret = -1; + s->tobeclosed = 1; + s->broken = 1; + log_socket_event(s, "socket read failed, to be closed",1); + } else if((s->st == TLS_SOCKET)||(s->st == TLS_SCTP_SOCKET)) { +#if TLS_SUPPORTED + SSL *ctx = bufferevent_openssl_get_ssl(s->bev); + if(!ctx || SSL_get_shutdown(ctx)) { + ret = -1; + s->tobeclosed = 1; + } +#endif + } + if(ret != -1) { + ret = len; + } + } + } } else if(blen<0) { s->tobeclosed = 1; s->broken = 1; @@ -3277,6 +3356,7 @@ int register_callback_on_ioa_socket(ioa_engine_handle e, ioa_socket_handle s, in break; case SCTP_SOCKET: case TCP_SOCKET: + case TCP_SOCKET_PROXY: if(s->bev) { if(!clean_preexisting) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, diff --git a/src/apps/relay/tls_listener.c b/src/apps/relay/tls_listener.c index a75c1875..689c2f66 100644 --- a/src/apps/relay/tls_listener.c +++ b/src/apps/relay/tls_listener.c @@ -82,7 +82,9 @@ static void server_input_handler(struct evconnlistener *l, evutil_socket_t fd, SOCKET_TYPE st = TENTATIVE_TCP_SOCKET; - if(turn_params.no_tls) + if(turn_params.tcp_use_proxy) + st = TCP_SOCKET_PROXY; + else if(turn_params.no_tls) st = TCP_SOCKET; else if(turn_params.no_tcp) st = TLS_SOCKET; diff --git a/src/server/ns_turn_ioalib.h b/src/server/ns_turn_ioalib.h index 074e618e..6737711d 100644 --- a/src/server/ns_turn_ioalib.h +++ b/src/server/ns_turn_ioalib.h @@ -90,6 +90,7 @@ enum _SOCKET_TYPE { SCTP_SOCKET=132, TLS_SCTP_SOCKET=133, DTLS_SOCKET=250, + TCP_SOCKET_PROXY=253, TENTATIVE_SCTP_SOCKET=254, TENTATIVE_TCP_SOCKET=255 };