diff --git a/src/ringdht/p2p.cpp b/src/ringdht/p2p.cpp index 9d85531c4ea3ea81ab6dcb5658d68628863b7ecd..df817169f39e49c1a6a1c81641a618634f795620 100644 --- a/src/ringdht/p2p.cpp +++ b/src/ringdht/p2p.cpp @@ -98,12 +98,18 @@ public: PeerConnectionMsg() = default; PeerConnectionMsg(dht::Value::Id id, uint32_t aprotocol, const std::string& arelay) : id {id}, protocol {aprotocol}, addresses {{arelay}} {} + PeerConnectionMsg(dht::Value::Id id, uint32_t aprotocol, const std::vector<std::string>& asrelay) + : id {id}, protocol {aprotocol}, addresses {asrelay} {} bool isRequest() const noexcept { return (protocol & 1) == 0; } PeerConnectionMsg respond(const IpAddr& relay) const { return {id, protocol|1, relay.toString(true, true)}; } + + PeerConnectionMsg respond(const std::vector<std::string>& addresses) const { + return {id, protocol|1, addresses}; + } }; //============================================================================== @@ -195,7 +201,8 @@ public: ~Impl() { servers_.clear(); clients_.clear(); - turn_.reset(); + turnAuthv4_.reset(); + turnAuthv6_.reset(); ctrl << makeMsg<CtrlMsgType::STOP>(); } @@ -204,7 +211,8 @@ public: private: std::map<IpAddr, std::unique_ptr<ConnectedTurnTransport>> turnEndpoints_; - std::unique_ptr<TurnTransport> turn_; + std::unique_ptr<TurnTransport> turnAuthv4_; + std::unique_ptr<TurnTransport> turnAuthv6_; // key: Stored certificate PublicKey id (normaly it's the DeviceId) // value: pair of shared_ptr<Certificate> and associated RingId @@ -318,19 +326,28 @@ private: } // Check response validity - IpAddr relay_addr; + std::unique_ptr<TcpSocketEndpoint> peer_ep; if (response_.from != peer_ or response_.id != request.id or - response_.addresses.empty() or - !(relay_addr = response_.addresses[0])) { + response_.addresses.empty()) throw std::runtime_error("invalid connection reply"); - } - // Connect to TURN peer using a raw socket - RING_DBG() << parent_.account << "[CNX] connecting to TURN relay " - << relay_addr.toString(true, true); - auto peer_ep = std::make_unique<TcpSocketEndpoint>(relay_addr); - peer_ep->connect(); // IMPROVE_ME: socket timeout? + IpAddr relay_addr; + for (const auto& address: response_.addresses) { + if (!(relay_addr = address)) + throw std::runtime_error("invalid connection reply"); + try { + // Connect to TURN peer using a raw socket + RING_DBG() << parent_.account << "[CNX] connecting to TURN relay " + << relay_addr.toString(true, true); + peer_ep = std::make_unique<TcpSocketEndpoint>(relay_addr); + peer_ep->connect(); // IMPROVE_ME: socket timeout? + break; + } catch (std::system_error&) { + RING_DBG() << parent_.account << "[CNX] Failed to connect to TURN relay " + << relay_addr.toString(true, true); + } + } // Negotiate a TLS session RING_DBG() << parent_.account << "[CNX] start TLS session"; @@ -375,7 +392,7 @@ private: void DhtPeerConnector::Impl::turnConnect() { - if (turn_) + if (turnAuthv4_ || turnAuthv6_) return; auto details = account.getAccountDetails(); @@ -384,25 +401,29 @@ DhtPeerConnector::Impl::turnConnect() auto username = details[Conf::CONFIG_TURN_SERVER_UNAME]; auto password = details[Conf::CONFIG_TURN_SERVER_PWD]; - auto turn_param = TurnTransportParams {}; - turn_param.server = IpAddr {server.empty() ? "turn.ring.cx" : server}; - turn_param.realm = realm.empty() ? "ring" : realm; - turn_param.username = username.empty() ? "ring" : username; - turn_param.password = password.empty() ? "ring" : password; - turn_param.isPeerConnection = true; // Request for TCP peer connections, not UDP - turn_param.onPeerConnection = [this](uint32_t conn_id, const IpAddr& peer_addr, bool connected) { + auto turn_param_v4 = TurnTransportParams {}; + turn_param_v4.server = IpAddr {server.empty() ? "turn.ring.cx" : server}; + turn_param_v4.realm = realm.empty() ? "ring" : realm; + turn_param_v4.username = username.empty() ? "ring" : username; + turn_param_v4.password = password.empty() ? "ring" : password; + turn_param_v4.isPeerConnection = true; // Request for TCP peer connections, not UDP + turn_param_v4.onPeerConnection = [this](uint32_t conn_id, const IpAddr& peer_addr, bool connected) { (void)conn_id; if (connected) ctrl << makeMsg<CtrlMsgType::TURN_PEER_CONNECT>(peer_addr); else ctrl << makeMsg<CtrlMsgType::TURN_PEER_DISCONNECT>(peer_addr); }; - turn_ = std::make_unique<TurnTransport>(turn_param); + turn_param_v4.authorized_family = PJ_AF_INET; + turnAuthv4_ = std::make_unique<TurnTransport>(turn_param_v4); + auto turn_param_v6 = turn_param_v4; + turn_param_v6.authorized_family = PJ_AF_INET6; + turnAuthv6_ = std::make_unique<TurnTransport>(turn_param_v6); // Wait until TURN server READY state (or timeout) Timeout<Clock> timeout {NET_CONNECTION_TIMEOUT}; timeout.start(); - while (!turn_->isReady()) { + while (!turnAuthv4_->isReady() && !turnAuthv6_->isReady()) { if (timeout) throw std::runtime_error("no response from TURN"); std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -432,7 +453,11 @@ DhtPeerConnector::Impl::onTurnPeerConnection(const IpAddr& peer_addr) RING_DBG() << account << "[CNX] TURN connection attempt from " << peer_addr.toString(true, true); - auto turn_ep = std::make_unique<ConnectedTurnTransport>(*turn_, peer_addr); + auto turn_ep = std::unique_ptr<ConnectedTurnTransport>(nullptr); + if (peer_addr.isIpv4()) + turn_ep = std::make_unique<ConnectedTurnTransport>(*turnAuthv4_, peer_addr); + else + turn_ep = std::make_unique<ConnectedTurnTransport>(*turnAuthv6_, peer_addr); RING_DBG() << account << "[CNX] start TLS session over TURN socket"; dht::InfoHash peer_h; @@ -500,19 +525,39 @@ DhtPeerConnector::Impl::onTrustedRequestMsg(PeerConnectionMsg&& request, // Save peer certificate for later TLS session (MUST BE DONE BEFORE TURN PEER AUTHORIZATION) certMap_.emplace(cert->getId(), std::make_pair(cert, peer_h)); + auto sendRelayV4 = false, sendRelayV6 = false; + for (auto& ip: request.addresses) { try { - turn_->permitPeer(ip); - RING_DBG() << account << "[CNX] authorized peer connection from " << ip; + if (IpAddr(ip).isIpv4()) { + sendRelayV4 = true; + turnAuthv4_->permitPeer(ip); + RING_DBG() << account << "[CNX] authorized peer connection from " << ip; + } else if (IpAddr(ip).isIpv6()) { + sendRelayV6 = true; + turnAuthv6_->permitPeer(ip); + RING_DBG() << account << "[CNX] authorized peer connection from " << ip; + } else { + RING_DBG() << account << "Unknown family type: " << ip; + } } catch (const std::exception& e) { RING_WARN() << account << "[CNX] ignored peer connection '" << ip << "', " << e.what(); } } + std::vector<std::string> addresses; + if (sendRelayV4) + addresses.emplace_back(turnAuthv4_->peerRelayAddr().toString(true, true)); + if (sendRelayV6) + addresses.emplace_back(turnAuthv6_->peerRelayAddr().toString(true, true)); + if (addresses.empty()) { + RING_DBG() << account << "[CNX] connection aborted, no family address found"; + return; + } RING_DBG() << account << "[CNX] connection accepted, DHT reply to " << request.from; account.dht().putEncrypted( dht::InfoHash::get(PeerConnectionMsg::key_prefix + request.from.toString()), - request.from, request.respond(turn_->peerRelayAddr())); + request.from, request.respond(addresses)); // Now wait for a TURN connection from peer (see onTurnPeerConnection) } diff --git a/src/turn_transport.cpp b/src/turn_transport.cpp index e13a0f7fca09b7b6eafd34317fd0b6bd8bfc0ec2..c115052b8cf1243047ea141d88a14d222e9bbee6 100644 --- a/src/turn_transport.cpp +++ b/src/turn_transport.cpp @@ -337,6 +337,10 @@ TurnTransport::TurnTransport(const TurnTransportParams& params) // TURN allocation setup pj_turn_alloc_param turn_alloc_param; pj_turn_alloc_param_default(&turn_alloc_param); +#ifndef RING_UWP // RING_UWP pjsip version is too old for now (need to have > 2.7.1) + if (params.authorized_family != 0) + turn_alloc_param.af = params.authorized_family; // RFC 6156!!! +#endif if (params.isPeerConnection) turn_alloc_param.peer_conn_type = PJ_TURN_TP_TCP; // RFC 6062!!! diff --git a/src/turn_transport.h b/src/turn_transport.h index 46424deb88813ac4fc72493d50a7b3f8a3bcd588..c1ae966f8f3b8409accbba32b35859ffa7df11d8 100644 --- a/src/turn_transport.h +++ b/src/turn_transport.h @@ -42,6 +42,8 @@ struct TurnTransportParams std::string username; std::string password; + pj_uint16_t authorized_family {0}; + bool isPeerConnection {false}; uint32_t connectionId {0}; std::function<void(uint32_t conn_id, const IpAddr& peer_addr, bool success)> onPeerConnection;