Browse Source

Merge pull request #31 from paullouisageneau/turn-ipv6

TURN server URL and IPv6 support
Paul-Louis Ageneau 5 years ago
parent
commit
06369f1f14
6 changed files with 120 additions and 60 deletions
  1. 12 7
      include/rtc/configuration.hpp
  2. 57 16
      src/configuration.cpp
  3. 41 29
      src/icetransport.cpp
  4. 4 4
      test/main.cpp
  5. 3 2
      test/p2p/answerer.cpp
  6. 3 2
      test/p2p/offerer.cpp

+ 12 - 7
include/rtc/configuration.hpp

@@ -28,15 +28,20 @@ namespace rtc {
 
 struct IceServer {
 	enum class Type { Stun, Turn };
-
-	// Don' Change It! It should be same order as enum NiceRelayType
 	enum class RelayType { TurnUdp, TurnTcp, TurnTls };
 
-	IceServer(const string &host_);
-	IceServer(const string &hostname_, uint16_t port_);
-	IceServer(const string &hostname_, const string &service_);
-	IceServer(const string &hostname_, const string &service_, string username_, string password_,
-	          RelayType relayType_);
+	// Any type
+	IceServer(const string &url);
+
+	// STUN
+	IceServer(string hostname_, uint16_t port_);
+	IceServer(string hostname_, string service_);
+
+	// TURN
+	IceServer(string hostname_, uint16_t port, string username_, string password_,
+	          RelayType relayType_ = RelayType::TurnUdp);
+	IceServer(string hostname_, string service_, string username_, string password_,
+	          RelayType relayType_ = RelayType::TurnUdp);
 
 	string hostname;
 	string service;

+ 57 - 16
src/configuration.cpp

@@ -18,29 +18,70 @@
 
 #include "configuration.hpp"
 
+#include <regex>
+
 namespace rtc {
 
-using std::to_string;
+IceServer::IceServer(const string &url) {
+	// Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
+	static const char *rs =
+	    R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
+	static const std::regex r(rs, std::regex::extended);
+
+	std::smatch m;
+	if (!std::regex_match(url, m, r) || m[10].length() == 0)
+		throw std::invalid_argument("Invalid ICE server URL: " + url);
+
+	std::vector<std::optional<string>> opt(m.size());
+	std::transform(m.begin(), m.end(), opt.begin(), [](const auto &sm) {
+		return sm.length() > 0 ? std::make_optional(string(sm)) : nullopt;
+	});
+
+	string scheme = opt[2].value_or("stun");
+	if (scheme == "stun" || scheme == "STUN")
+		type = Type::Stun;
+	else if (scheme == "turn" || scheme == "TURN")
+		type = Type::Turn;
+	else if (scheme == "turns" || scheme == "TURNS")
+		type = Type::Turn;
+	else
+		throw std::invalid_argument("Unknown ICE server protocol: " + scheme);
 
-IceServer::IceServer(const string &host) : type(Type::Stun) {
-	if (size_t pos = host.rfind(':'); pos != string::npos) {
-		hostname = host.substr(0, pos);
-		service = host.substr(pos + 1);
-	} else {
-		hostname = host;
-		service = "3478"; // STUN UDP port
+	relayType = RelayType::TurnUdp;
+	if (auto &query = opt[15]) {
+		if (query->find("transport=udp") != string::npos)
+			relayType = RelayType::TurnUdp;
+		if (query->find("transport=tcp") != string::npos)
+			relayType = RelayType::TurnTcp;
+		if (query->find("transport=tls") != string::npos)
+			relayType = RelayType::TurnTls;
 	}
+
+	username = opt[6].value_or("");
+	password = opt[8].value_or("");
+	hostname = opt[10].value();
+	service = opt[12].value_or(relayType == RelayType::TurnTls ? "5349" : "3478");
+
+	while (!hostname.empty() && hostname.front() == '[')
+		hostname.erase(hostname.begin());
+	while (!hostname.empty() && hostname.back() == ']')
+		hostname.pop_back();
 }
 
-IceServer::IceServer(const string &hostname_, uint16_t port_)
-    : IceServer(hostname_, to_string(port_)) {}
+IceServer::IceServer(string hostname_, uint16_t port_)
+    : IceServer(std::move(hostname_), std::to_string(port_)) {}
+
+IceServer::IceServer(string hostname_, string service_)
+    : hostname(std::move(hostname_)), service(std::move(service_)), type(Type::Stun) {}
 
-IceServer::IceServer(const string &hostname_, const string &service_)
-    : hostname(hostname_), service(service_), type(Type::Stun) {}
+IceServer::IceServer(string hostname_, uint16_t port_, string username_, string password_,
+                     RelayType relayType_)
+    : IceServer(hostname_, std::to_string(port_), std::move(username_), std::move(password_),
+                relayType_) {}
 
-IceServer::IceServer(const string &hostname_, const string &service_, string username_,
-                     string password_, RelayType relayType_)
-    : hostname(hostname_), service(service_), type(Type::Turn), username(username_),
-      password(password_), relayType(relayType_) {}
+IceServer::IceServer(string hostname_, string service_, string username_, string password_,
+                     RelayType relayType_)
+    : hostname(std::move(hostname_)), service(std::move(service_)), type(Type::Turn),
+      username(std::move(username_)), password(std::move(password_)), relayType(relayType_) {}
 
 } // namespace rtc

+ 41 - 29
src/icetransport.cpp

@@ -17,6 +17,7 @@
  */
 
 #include "icetransport.hpp"
+#include "configuration.hpp"
 
 #include <netdb.h>
 #include <netinet/in.h>
@@ -74,15 +75,17 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
 	g_object_set(G_OBJECT(mNiceAgent.get()), "upnp", FALSE, nullptr);
 	g_object_set(G_OBJECT(mNiceAgent.get()), "upnp-timeout", 200, nullptr);
 
+	// Randomize order
 	std::vector<IceServer> servers = config.iceServers;
 	unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
 	std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
 
+	// Add one STUN server
 	bool success = false;
 	for (auto &server : servers) {
 		if (server.hostname.empty())
 			continue;
-		if (server.type == IceServer::Type::Turn)
+		if (server.type != IceServer::Type::Stun)
 			continue;
 		if (server.service.empty())
 			server.service = "3478"; // STUN UDP port
@@ -101,7 +104,6 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
 				char nodebuffer[MAX_NUMERICNODE_LEN];
 				char servbuffer[MAX_NUMERICSERV_LEN];
 				if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
-
 				                servbuffer, MAX_NUMERICNODE_LEN,
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
 					g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr);
@@ -118,35 +120,17 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
 			break;
 	}
 
-	g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed",
-	                 G_CALLBACK(StateChangeCallback), this);
-	g_signal_connect(G_OBJECT(mNiceAgent.get()), "new-candidate-full",
-	                 G_CALLBACK(CandidateCallback), this);
-	g_signal_connect(G_OBJECT(mNiceAgent.get()), "candidate-gathering-done",
-	                 G_CALLBACK(GatheringDoneCallback), this);
-
-	mStreamId = nice_agent_add_stream(mNiceAgent.get(), 1);
-	if (!mStreamId)
-		throw std::runtime_error("Failed to add a stream");
-
-	nice_agent_set_stream_name(mNiceAgent.get(), mStreamId, "application");
-	nice_agent_set_port_range(mNiceAgent.get(), mStreamId, 1, config.portRangeBegin,
-	                          config.portRangeEnd);
-
-	nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(mMainLoop.get()),
-	                       RecvCallback, this);
-
-	// Add TURN Servers
+	// Add TURN servers
 	for (auto &server : servers) {
 		if (server.hostname.empty())
 			continue;
-		if (server.type == IceServer::Type::Stun)
+		if (server.type != IceServer::Type::Turn)
 			continue;
 		if (server.service.empty())
-			server.service = "3478"; // TURN UDP port
+			server.service = server.relayType == IceServer::RelayType::TurnTls ? "5349" : "3478";
 
 		struct addrinfo hints = {};
-		hints.ai_family = AF_INET; // IPv4
+		hints.ai_family = AF_UNSPEC;
 		hints.ai_socktype =
 		    server.relayType == IceServer::RelayType::TurnUdp ? SOCK_DGRAM : SOCK_STREAM;
 		hints.ai_protocol =
@@ -157,24 +141,52 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
 			continue;
 
 		for (auto p = result; p; p = p->ai_next) {
-			if (p->ai_family == AF_INET) {
+			if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
 				char nodebuffer[MAX_NUMERICNODE_LEN];
 				char servbuffer[MAX_NUMERICSERV_LEN];
 				if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
-
 				                servbuffer, MAX_NUMERICNODE_LEN,
 				                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
+
+					NiceRelayType niceRelayType;
+					switch (server.relayType) {
+					case IceServer::RelayType::TurnTcp:
+						niceRelayType = NICE_RELAY_TYPE_TURN_TCP;
+						break;
+					case IceServer::RelayType::TurnTls:
+						niceRelayType = NICE_RELAY_TYPE_TURN_TLS;
+						break;
+					default:
+						niceRelayType = NICE_RELAY_TYPE_TURN_UDP;
+						break;
+					}
 					nice_agent_set_relay_info(mNiceAgent.get(), mStreamId, 1, nodebuffer,
 					                          std::stoul(servbuffer), server.username.c_str(),
-					                          server.password.c_str(),
-					                          static_cast<NiceRelayType>(server.relayType));
-					break;
+					                          server.password.c_str(), niceRelayType);
 				}
 			}
 		}
 
 		freeaddrinfo(result);
 	}
+
+	g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed",
+	                 G_CALLBACK(StateChangeCallback), this);
+	g_signal_connect(G_OBJECT(mNiceAgent.get()), "new-candidate-full",
+	                 G_CALLBACK(CandidateCallback), this);
+	g_signal_connect(G_OBJECT(mNiceAgent.get()), "candidate-gathering-done",
+	                 G_CALLBACK(GatheringDoneCallback), this);
+
+	mStreamId = nice_agent_add_stream(mNiceAgent.get(), 1);
+	if (!mStreamId)
+		throw std::runtime_error("Failed to add a stream");
+
+	nice_agent_set_stream_name(mNiceAgent.get(), mStreamId, "application");
+	nice_agent_set_port_range(mNiceAgent.get(), mStreamId, 1, config.portRangeBegin,
+	                          config.portRangeEnd);
+
+	nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(mMainLoop.get()),
+	                       RecvCallback, this);
 }
 
 IceTransport::~IceTransport() { stop(); }

+ 4 - 4
test/main.cpp

@@ -30,14 +30,14 @@ template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
 int main(int argc, char **argv) {
 	// InitLogger(LogLevel::Debug);
-	Configuration config;
 
-	// config.iceServers.emplace_back("stun.l.google.com:19302");
+	Configuration config;
+	// config.iceServers.emplace_back("stun:stun.l.google.com:19302");
 	// config.enableIceTcp = true;
 
-	// Add TURN Server Example
+	// TURN server example
 	// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
-	//							IceServer::RelayType::TurnTls);
+	//							IceServer::RelayType::TurnUdp);
 	// config.iceServers.push_back(turnServer);
 
 	auto pc1 = std::make_shared<PeerConnection>(config);

+ 3 - 2
test/p2p/answerer.cpp

@@ -29,13 +29,14 @@ template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
 int main(int argc, char **argv) {
 	// InitLogger(LogLevel::Debug);
+
 	Configuration config;
 	// config.iceServers.emplace_back("stun.l.google.com:19302");
 	// config.enableIceTcp = true;
 
-	// Add TURN Server Example
+	// TURN Server example
 	// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
-	//							IceServer::RelayType::TurnTls);
+	//							IceServer::RelayType::TurnUdp);
 	// config.iceServers.push_back(turnServer);
 
 	auto pc = std::make_shared<PeerConnection>(config);

+ 3 - 2
test/p2p/offerer.cpp

@@ -29,13 +29,14 @@ template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
 int main(int argc, char **argv) {
 	// InitLogger(LogLevel::Debug);
+
 	Configuration config;
 	// config.iceServers.emplace_back("stun.l.google.com:19302");
 	// config.enableIceTcp = true;
 
-	// Add TURN Server Example
+	// TURN server example
 	// IceServer turnServer("TURN_SERVER_URL", "PORT_NO", "USERNAME", "PASSWORD",
-	//							IceServer::RelayType::TurnTls);
+	//							IceServer::RelayType::TurnUdp);
 	// config.iceServers.push_back(turnServer);
 
 	auto pc = std::make_shared<PeerConnection>(config);