• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RawTransportChannel(const std::string & name,const std::string & content_type,RawTransport * transport,talk_base::Thread * worker_thread,PortAllocator * allocator)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 
~RawTransportChannel()73 RawTransportChannel::~RawTransportChannel() {
74   delete allocator_session_;
75 }
76 
SendPacket(const char * data,size_t size)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 
SetOption(talk_base::Socket::Option opt,int value)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 
GetError()92 int RawTransportChannel::GetError() {
93   return (port_ != NULL) ? port_->GetError() : 0;
94 }
95 
Connect()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 
Reset()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 
OnCandidate(const Candidate & candidate)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 
OnRemoteAddress(const talk_base::SocketAddress & remote_address)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 
OnPortReady(PortAllocatorSession * session,Port * port)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 
OnCandidatesReady(PortAllocatorSession * session,const std::vector<Candidate> & candidates)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 
SetPort(Port * port)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 
SetWritable()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 
OnReadPacket(Port * port,const char * data,size_t size,const talk_base::SocketAddress & addr)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 
OnMessage(talk_base::Message * msg)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