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