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/base/common.h" 33 #include "talk/p2p/base/constants.h" 34 #include "talk/p2p/base/port.h" 35 #include "talk/p2p/base/portallocator.h" 36 #include "talk/p2p/base/rawtransport.h" 37 #include "talk/p2p/base/relayport.h" 38 #include "talk/p2p/base/sessionmanager.h" 39 #include "talk/p2p/base/stunport.h" 40 #include "talk/xmllite/qname.h" 41 #include "talk/xmllite/xmlelement.h" 42 #include "talk/xmpp/constants.h" 43 44 #if defined(FEATURE_ENABLE_PSTN) 45 46 namespace { 47 48 const uint32 MSG_DESTROY_UNUSED_PORTS = 1; 49 50 } // namespace 51 52 namespace cricket { 53 54 RawTransportChannel::RawTransportChannel(const std::string &name, 55 const std::string &content_type, 56 RawTransport* transport, 57 talk_base::Thread *worker_thread, 58 PortAllocator *allocator) 59 : TransportChannelImpl(name, content_type), 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 73 RawTransportChannel::~RawTransportChannel() { 74 delete allocator_session_; 75 } 76 77 int RawTransportChannel::SendPacket(const char *data, size_t size) { 78 if (port_ == NULL) 79 return -1; 80 if (remote_address_.IsAny()) 81 return -1; 82 return port_->SendTo(data, size, remote_address_, true); 83 } 84 85 int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) { 86 // TODO: allow these to be set before we have a port 87 if (port_ == NULL) 88 return -1; 89 return port_->SetOption(opt, value); 90 } 91 92 int RawTransportChannel::GetError() { 93 return (port_ != NULL) ? port_->GetError() : 0; 94 } 95 96 void RawTransportChannel::Connect() { 97 // Create an allocator that only returns stun and relay ports. 98 allocator_session_ = allocator_->CreateSession(name(), content_type()); 99 100 uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP; 101 102 #if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 103 flags |= PORTALLOCATOR_DISABLE_RELAY; 104 #endif 105 allocator_session_->set_flags(flags); 106 allocator_session_->SignalPortReady.connect( 107 this, &RawTransportChannel::OnPortReady); 108 allocator_session_->SignalCandidatesReady.connect( 109 this, &RawTransportChannel::OnCandidatesReady); 110 111 // The initial ports will include stun. 112 allocator_session_->GetInitialPorts(); 113 } 114 115 void RawTransportChannel::Reset() { 116 set_readable(false); 117 set_writable(false); 118 119 delete allocator_session_; 120 121 allocator_session_ = NULL; 122 stun_port_ = NULL; 123 relay_port_ = NULL; 124 port_ = NULL; 125 remote_address_ = talk_base::SocketAddress(); 126 } 127 128 void RawTransportChannel::OnCandidate(const Candidate& candidate) { 129 remote_address_ = candidate.address(); 130 ASSERT(!remote_address_.IsAny()); 131 set_readable(true); 132 133 // We can write once we have a port and a remote address. 134 if (port_ != NULL) 135 SetWritable(); 136 } 137 138 void RawTransportChannel::OnRemoteAddress( 139 const talk_base::SocketAddress& remote_address) { 140 remote_address_ = remote_address; 141 set_readable(true); 142 143 if (port_ != NULL) 144 SetWritable(); 145 } 146 147 // Note about stun classification 148 // Code to classify our NAT type and use the relay port if we are behind an 149 // asymmetric NAT is under a FEATURE_ENABLE_STUN_CLASSIFICATION #define. 150 // To turn this one we will have to enable a second stun address and make sure 151 // that the relay server works for raw UDP. 152 // 153 // Another option is to classify the NAT type early and not offer the raw 154 // transport type at all if we can't support it. 155 156 void RawTransportChannel::OnPortReady( 157 PortAllocatorSession* session, Port* port) { 158 ASSERT(session == allocator_session_); 159 160 if (port->type() == STUN_PORT_TYPE) { 161 stun_port_ = static_cast<StunPort*>(port); 162 163 #if defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 164 // We need a secondary address to determine the NAT type. 165 stun_port_->PrepareSecondaryAddress(); 166 #endif 167 } else if (port->type() == RELAY_PORT_TYPE) { 168 relay_port_ = static_cast<RelayPort*>(port); 169 } else { 170 ASSERT(false); 171 } 172 } 173 174 void RawTransportChannel::OnCandidatesReady( 175 PortAllocatorSession *session, const std::vector<Candidate>& candidates) { 176 ASSERT(session == allocator_session_); 177 ASSERT(candidates.size() >= 1); 178 179 // The most recent candidate is the one we haven't seen yet. 180 Candidate c = candidates[candidates.size() - 1]; 181 182 if (c.type() == STUN_PORT_TYPE) { 183 ASSERT(stun_port_ != NULL); 184 185 #if defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 186 // We need to wait until we have two addresses. 187 if (stun_port_->candidates().size() < 2) 188 return; 189 190 // This is the second address. If these addresses are the same, then we 191 // are not behind a symmetric NAT. Hence, a stun port should be sufficient. 192 if (stun_port_->candidates()[0].address() == 193 stun_port_->candidates()[1].address()) { 194 SetPort(stun_port_); 195 return; 196 } 197 198 // We will need to use relay. 199 use_relay_ = true; 200 201 // If we weren't given a relay port, we'll need to request it. 202 if (relay_port_ == NULL) { 203 allocator_session_->StartGetAllPorts(); 204 return; 205 } 206 207 // If we already have a relay address, we're good. Otherwise, we will need 208 // to wait until one arrives. 209 if (relay_port_->candidates().size() > 0) 210 SetPort(relay_port_); 211 #else // defined(FEATURE_ENABLE_STUN_CLASSIFICATION) 212 // Always use the stun port. We don't classify right now so just assume it 213 // will work fine. 214 SetPort(stun_port_); 215 #endif 216 } else if (c.type() == RELAY_PORT_TYPE) { 217 if (use_relay_) 218 SetPort(relay_port_); 219 } else { 220 ASSERT(false); 221 } 222 } 223 224 void RawTransportChannel::SetPort(Port* port) { 225 ASSERT(port_ == NULL); 226 port_ = port; 227 228 // We don't need any ports other than the one we picked. 229 allocator_session_->StopGetAllPorts(); 230 worker_thread_->Post( 231 this, MSG_DESTROY_UNUSED_PORTS, NULL); 232 233 // Send a message to the other client containing our address. 234 235 ASSERT(port_->candidates().size() >= 1); 236 ASSERT(port_->candidates()[0].protocol() == "udp"); 237 SignalCandidateReady(this, port_->candidates()[0]); 238 239 // Read all packets from this port. 240 port_->EnablePortPackets(); 241 port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket); 242 243 // We can write once we have a port and a remote address. 244 if (!remote_address_.IsAny()) 245 SetWritable(); 246 } 247 248 void RawTransportChannel::SetWritable() { 249 ASSERT(port_ != NULL); 250 ASSERT(!remote_address_.IsAny()); 251 252 set_writable(true); 253 254 SignalRouteChange(this, remote_address_); 255 } 256 257 void RawTransportChannel::OnReadPacket( 258 Port* port, const char* data, size_t size, 259 const talk_base::SocketAddress& addr) { 260 ASSERT(port_ == port); 261 SignalReadPacket(this, data, size); 262 } 263 264 void RawTransportChannel::OnMessage(talk_base::Message* msg) { 265 ASSERT(msg->message_id == MSG_DESTROY_UNUSED_PORTS); 266 ASSERT(port_ != NULL); 267 if (port_ != stun_port_) { 268 stun_port_->Destroy(); 269 stun_port_ = NULL; 270 } 271 if (port_ != relay_port_ && relay_port_ != NULL) { 272 relay_port_->Destroy(); 273 relay_port_ = NULL; 274 } 275 } 276 277 } // namespace cricket 278 #endif // defined(FEATURE_ENABLE_PSTN) 279