Browse Source

Merge pull request #479 from WaxieSDR/master

Add support for reverse proxy connections
Mészáros Mihály 5 years ago
parent
commit
db68c76195

+ 4 - 0
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.

+ 8 - 0
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, 

+ 6 - 0
man/man1/turnserver.1

@@ -481,6 +481,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.

+ 1 - 0
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:

+ 10 - 2
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	<port>		Alternative listening port for TLS and DTLS,\n"
 " 						the default is \"TLS/DTLS port plus one\".\n"
+" --tcp-proxy-port		<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		<ip>		Listener IP address of relay server. Multiple listeners can be specified.\n"
 " --aux-server			<ip:port>	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' },
@@ -1267,6 +1271,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;

+ 2 - 0
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;

+ 4 - 4
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<turn_params.aux_servers_list.size; i++) {
 
@@ -1494,15 +1494,15 @@ static void setup_tcp_listener_servers(ioa_engine_handle e, struct relay_server
 
 		/* TCP: */
 		if(!turn_params.no_tcp) {
-			tcp_services[index] = create_tls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.listener_port, turn_params.verbose, e, send_socket_to_general_relay, relay_server);
+			tcp_services[index] = create_tls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.tcp_use_proxy?turn_params.tcp_proxy_port:turn_params.listener_port, turn_params.verbose, e, send_socket_to_general_relay, relay_server);
 			if(turn_params.rfc5780)
-				tcp_services[index+1] = create_tls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_listener_port(), turn_params.verbose, e, send_socket_to_general_relay, relay_server);
+				tcp_services[index+1] = turn_params.tcp_use_proxy?NULL:create_tls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_listener_port(), turn_params.verbose, e, send_socket_to_general_relay, relay_server);
 		} else {
 			tcp_services[index] = NULL;
 			if(turn_params.rfc5780)
 				tcp_services[index+1] = NULL;
 		}
-		if(!turn_params.no_tls && (turn_params.no_tcp || (turn_params.listener_port != turn_params.tls_listener_port))) {
+		if(!turn_params.no_tls && !turn_params.tcp_use_proxy && (turn_params.no_tcp || (turn_params.listener_port != turn_params.tls_listener_port))) {
 			tls_services[index] = create_tls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], turn_params.tls_listener_port, turn_params.verbose, e, send_socket_to_general_relay, relay_server);
 			if(turn_params.rfc5780)
 				tls_services[index+1] = create_tls_listener_server(turn_params.listener_ifname, turn_params.listener.addrs[i], get_alt_tls_listener_port(), turn_params.verbose, e, send_socket_to_general_relay, relay_server);

+ 100 - 20
src/apps/relay/ns_ioalib_engine_impl.c

@@ -2157,6 +2157,67 @@ static TURN_TLS_TYPE check_tentative_tls(ioa_socket_raw fd)
 }
 #endif
 
+static ssize_t socket_parse_proxy_v2(ioa_socket_handle s, uint8_t *buf, size_t len)
+{
+	if(len < 16){
+		return 0 ;
+	}
+
+	/* Check for proxy-v2 magic field */
+	char magic[] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
+	if(memcmp(magic, buf, sizeof(magic))){
+		return -1;
+	}
+
+	/* Check version */
+	uint8_t version = buf[12] >> 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;
+						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;
+						}
+						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,

+ 3 - 1
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;

+ 1 - 0
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
 };