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