1/* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/p2p/base/rawtransportchannel.h" 29 30#include <string> 31#include <vector> 32#include "talk/p2p/base/constants.h" 33#include "talk/p2p/base/portallocator.h" 34#include "talk/p2p/base/portinterface.h" 35#include "talk/p2p/base/rawtransport.h" 36#include "talk/p2p/base/relayport.h" 37#include "talk/p2p/base/sessionmanager.h" 38#include "talk/p2p/base/stunport.h" 39#include "webrtc/libjingle/xmllite/qname.h" 40#include "webrtc/libjingle/xmllite/xmlelement.h" 41#include "talk/xmpp/constants.h" 42#include "webrtc/base/common.h" 43 44#if defined(FEATURE_ENABLE_PSTN) 45 46namespace { 47 48const uint32 MSG_DESTROY_RTC_UNUSED_PORTS = 1; 49 50} // namespace 51 52namespace cricket { 53 54RawTransportChannel::RawTransportChannel(const std::string& content_name, 55 int component, 56 RawTransport* transport, 57 rtc::Thread *worker_thread, 58 PortAllocator *allocator) 59 : TransportChannelImpl(content_name, component), 60 raw_transport_(transport), 61 allocator_(allocator), 62 allocator_session_(NULL), 63 stun_port_(NULL), 64 relay_port_(NULL), 65 port_(NULL), 66 use_relay_(false) { 67 if (worker_thread == NULL) 68 worker_thread_ = raw_transport_->worker_thread(); 69 else 70 worker_thread_ = worker_thread; 71} 72 73RawTransportChannel::~RawTransportChannel() { 74 delete allocator_session_; 75} 76 77int RawTransportChannel::SendPacket(const char *data, size_t size, 78 const rtc::PacketOptions& options, 79 int flags) { 80 if (port_ == NULL) 81 return -1; 82 if (remote_address_.IsNil()) 83 return -1; 84 if (flags != 0) 85 return -1; 86 return port_->SendTo(data, size, remote_address_, options, true); 87} 88 89int RawTransportChannel::SetOption(rtc::Socket::Option opt, int value) { 90 // TODO: allow these to be set before we have a port 91 if (port_ == NULL) 92 return -1; 93 return port_->SetOption(opt, value); 94} 95 96int RawTransportChannel::GetError() { 97 return (port_ != NULL) ? port_->GetError() : 0; 98} 99 100void RawTransportChannel::Connect() { 101 // Create an allocator that only returns stun and relay ports. 102 // Use empty string for ufrag and pwd here. There won't be any STUN or relay 103 // interactions when using RawTC. 104 // TODO: Change raw to only use local udp ports. 105 allocator_session_ = allocator_->CreateSession( 106 SessionId(), content_name(), component(), "", ""); 107 108 uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP; 109 110#if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 111 flags |= PORTALLOCATOR_DISABLE_RELAY; 112#endif 113 allocator_session_->set_flags(flags); 114 allocator_session_->SignalPortReady.connect( 115 this, &RawTransportChannel::OnPortReady); 116 allocator_session_->SignalCandidatesReady.connect( 117 this, &RawTransportChannel::OnCandidatesReady); 118 119 // The initial ports will include stun. 120 allocator_session_->StartGettingPorts(); 121} 122 123void RawTransportChannel::Reset() { 124 set_readable(false); 125 set_writable(false); 126 127 delete allocator_session_; 128 129 allocator_session_ = NULL; 130 stun_port_ = NULL; 131 relay_port_ = NULL; 132 port_ = NULL; 133 remote_address_ = rtc::SocketAddress(); 134} 135 136void RawTransportChannel::OnCandidate(const Candidate& candidate) { 137 remote_address_ = candidate.address(); 138 ASSERT(!remote_address_.IsNil()); 139 set_readable(true); 140 141 // We can write once we have a port and a remote address. 142 if (port_ != NULL) 143 SetWritable(); 144} 145 146void RawTransportChannel::OnRemoteAddress( 147 const rtc::SocketAddress& remote_address) { 148 remote_address_ = remote_address; 149 set_readable(true); 150 151 if (port_ != NULL) 152 SetWritable(); 153} 154 155// Note about stun classification 156// Code to classify our NAT type and use the relay port if we are behind an 157// asymmetric NAT is under a FEATURE_ENABLE_STUN_CLASSIFICATION #define. 158// To turn this one we will have to enable a second stun address and make sure 159// that the relay server works for raw UDP. 160// 161// Another option is to classify the NAT type early and not offer the raw 162// transport type at all if we can't support it. 163 164void RawTransportChannel::OnPortReady( 165 PortAllocatorSession* session, PortInterface* port) { 166 ASSERT(session == allocator_session_); 167 168 if (port->Type() == STUN_PORT_TYPE) { 169 stun_port_ = static_cast<StunPort*>(port); 170 } else if (port->Type() == RELAY_PORT_TYPE) { 171 relay_port_ = static_cast<RelayPort*>(port); 172 } else { 173 ASSERT(false); 174 } 175} 176 177void RawTransportChannel::OnCandidatesReady( 178 PortAllocatorSession *session, const std::vector<Candidate>& candidates) { 179 ASSERT(session == allocator_session_); 180 ASSERT(candidates.size() >= 1); 181 182 // The most recent candidate is the one we haven't seen yet. 183 Candidate c = candidates[candidates.size() - 1]; 184 185 if (c.type() == STUN_PORT_TYPE) { 186 ASSERT(stun_port_ != NULL); 187 188#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 189 // We need to wait until we have two addresses. 190 if (stun_port_->candidates().size() < 2) 191 return; 192 193 // This is the second address. If these addresses are the same, then we 194 // are not behind a symmetric NAT. Hence, a stun port should be sufficient. 195 if (stun_port_->candidates()[0].address() == 196 stun_port_->candidates()[1].address()) { 197 SetPort(stun_port_); 198 return; 199 } 200 201 // We will need to use relay. 202 use_relay_ = true; 203 204 // If we already have a relay address, we're good. Otherwise, we will need 205 // to wait until one arrives. 206 if (relay_port_->candidates().size() > 0) 207 SetPort(relay_port_); 208#else // defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 209 // Always use the stun port. We don't classify right now so just assume it 210 // will work fine. 211 SetPort(stun_port_); 212#endif 213 } else if (c.type() == RELAY_PORT_TYPE) { 214 if (use_relay_) 215 SetPort(relay_port_); 216 } else { 217 ASSERT(false); 218 } 219} 220 221void RawTransportChannel::SetPort(PortInterface* port) { 222 ASSERT(port_ == NULL); 223 port_ = port; 224 225 // We don't need any ports other than the one we picked. 226 allocator_session_->StopGettingPorts(); 227 worker_thread_->Post( 228 this, MSG_DESTROY_RTC_UNUSED_PORTS, NULL); 229 230 // Send a message to the other client containing our address. 231 232 ASSERT(port_->Candidates().size() >= 1); 233 ASSERT(port_->Candidates()[0].protocol() == "udp"); 234 SignalCandidateReady(this, port_->Candidates()[0]); 235 236 // Read all packets from this port. 237 port_->EnablePortPackets(); 238 port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket); 239 240 // We can write once we have a port and a remote address. 241 if (!remote_address_.IsAny()) 242 SetWritable(); 243} 244 245void RawTransportChannel::SetWritable() { 246 ASSERT(port_ != NULL); 247 ASSERT(!remote_address_.IsAny()); 248 249 set_writable(true); 250 251 Candidate remote_candidate; 252 remote_candidate.set_address(remote_address_); 253 SignalRouteChange(this, remote_candidate); 254} 255 256void RawTransportChannel::OnReadPacket( 257 PortInterface* port, const char* data, size_t size, 258 const rtc::SocketAddress& addr) { 259 ASSERT(port_ == port); 260 SignalReadPacket(this, data, size, rtc::CreatePacketTime(0), 0); 261} 262 263void RawTransportChannel::OnMessage(rtc::Message* msg) { 264 ASSERT(msg->message_id == MSG_DESTROY_RTC_UNUSED_PORTS); 265 ASSERT(port_ != NULL); 266 if (port_ != stun_port_) { 267 stun_port_->Destroy(); 268 stun_port_ = NULL; 269 } 270 if (port_ != relay_port_ && relay_port_ != NULL) { 271 relay_port_->Destroy(); 272 relay_port_ = NULL; 273 } 274} 275 276} // namespace cricket 277#endif // defined(FEATURE_ENABLE_PSTN) 278