3 * Copyright 2004--2005, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "talk/p2p/base/rawtransportchannel.h"
32 #include "talk/base/common.h"
33 #include "talk/p2p/base/constants.h"
34 #include "talk/p2p/base/portallocator.h"
35 #include "talk/p2p/base/portinterface.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"
44 #if defined(FEATURE_ENABLE_PSTN)
48 const uint32 MSG_DESTROY_UNUSED_PORTS = 1;
54 RawTransportChannel::RawTransportChannel(const std::string& content_name,
56 RawTransport* transport,
57 talk_base::Thread *worker_thread,
58 PortAllocator *allocator)
59 : TransportChannelImpl(content_name, component),
60 raw_transport_(transport),
61 allocator_(allocator),
62 allocator_session_(NULL),
67 if (worker_thread == NULL)
68 worker_thread_ = raw_transport_->worker_thread();
70 worker_thread_ = worker_thread;
73 RawTransportChannel::~RawTransportChannel() {
74 delete allocator_session_;
77 int RawTransportChannel::SendPacket(const char *data, size_t size,
78 const talk_base::PacketOptions& options,
82 if (remote_address_.IsNil())
86 return port_->SendTo(data, size, remote_address_, options, true);
89 int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
90 // TODO: allow these to be set before we have a port
93 return port_->SetOption(opt, value);
96 int RawTransportChannel::GetError() {
97 return (port_ != NULL) ? port_->GetError() : 0;
100 void 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(), "", "");
108 uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
110 #if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
111 flags |= PORTALLOCATOR_DISABLE_RELAY;
113 allocator_session_->set_flags(flags);
114 allocator_session_->SignalPortReady.connect(
115 this, &RawTransportChannel::OnPortReady);
116 allocator_session_->SignalCandidatesReady.connect(
117 this, &RawTransportChannel::OnCandidatesReady);
119 // The initial ports will include stun.
120 allocator_session_->StartGettingPorts();
123 void RawTransportChannel::Reset() {
127 delete allocator_session_;
129 allocator_session_ = NULL;
133 remote_address_ = talk_base::SocketAddress();
136 void RawTransportChannel::OnCandidate(const Candidate& candidate) {
137 remote_address_ = candidate.address();
138 ASSERT(!remote_address_.IsNil());
141 // We can write once we have a port and a remote address.
146 void RawTransportChannel::OnRemoteAddress(
147 const talk_base::SocketAddress& remote_address) {
148 remote_address_ = remote_address;
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.
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.
164 void RawTransportChannel::OnPortReady(
165 PortAllocatorSession* session, PortInterface* port) {
166 ASSERT(session == allocator_session_);
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);
177 void RawTransportChannel::OnCandidatesReady(
178 PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
179 ASSERT(session == allocator_session_);
180 ASSERT(candidates.size() >= 1);
182 // The most recent candidate is the one we haven't seen yet.
183 Candidate c = candidates[candidates.size() - 1];
185 if (c.type() == STUN_PORT_TYPE) {
186 ASSERT(stun_port_ != NULL);
188 #if defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
189 // We need to wait until we have two addresses.
190 if (stun_port_->candidates().size() < 2)
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()) {
201 // We will need to use relay.
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
213 } else if (c.type() == RELAY_PORT_TYPE) {
215 SetPort(relay_port_);
221 void RawTransportChannel::SetPort(PortInterface* port) {
222 ASSERT(port_ == NULL);
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_UNUSED_PORTS, NULL);
230 // Send a message to the other client containing our address.
232 ASSERT(port_->Candidates().size() >= 1);
233 ASSERT(port_->Candidates()[0].protocol() == "udp");
234 SignalCandidateReady(this, port_->Candidates()[0]);
236 // Read all packets from this port.
237 port_->EnablePortPackets();
238 port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket);
240 // We can write once we have a port and a remote address.
241 if (!remote_address_.IsAny())
245 void RawTransportChannel::SetWritable() {
246 ASSERT(port_ != NULL);
247 ASSERT(!remote_address_.IsAny());
251 Candidate remote_candidate;
252 remote_candidate.set_address(remote_address_);
253 SignalRouteChange(this, remote_candidate);
256 void RawTransportChannel::OnReadPacket(
257 PortInterface* port, const char* data, size_t size,
258 const talk_base::SocketAddress& addr) {
259 ASSERT(port_ == port);
260 SignalReadPacket(this, data, size, talk_base::CreatePacketTime(0), 0);
263 void RawTransportChannel::OnMessage(talk_base::Message* msg) {
264 ASSERT(msg->message_id == MSG_DESTROY_UNUSED_PORTS);
265 ASSERT(port_ != NULL);
266 if (port_ != stun_port_) {
267 stun_port_->Destroy();
270 if (port_ != relay_port_ && relay_port_ != NULL) {
271 relay_port_->Destroy();
276 } // namespace cricket
277 #endif // defined(FEATURE_ENABLE_PSTN)