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/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