1112c30e1e
R=wu@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2274004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4818 4adac7df-926f-26a2-2b94-8c16560cd09d
278 lines
8.7 KiB
C++
278 lines
8.7 KiB
C++
/*
|
|
* libjingle
|
|
* Copyright 2004--2005, Google Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "talk/p2p/base/rawtransportchannel.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include "talk/base/common.h"
|
|
#include "talk/p2p/base/constants.h"
|
|
#include "talk/p2p/base/portallocator.h"
|
|
#include "talk/p2p/base/portinterface.h"
|
|
#include "talk/p2p/base/rawtransport.h"
|
|
#include "talk/p2p/base/relayport.h"
|
|
#include "talk/p2p/base/sessionmanager.h"
|
|
#include "talk/p2p/base/stunport.h"
|
|
#include "talk/xmllite/qname.h"
|
|
#include "talk/xmllite/xmlelement.h"
|
|
#include "talk/xmpp/constants.h"
|
|
|
|
#if defined(FEATURE_ENABLE_PSTN)
|
|
|
|
namespace {
|
|
|
|
const uint32 MSG_DESTROY_UNUSED_PORTS = 1;
|
|
|
|
} // namespace
|
|
|
|
namespace cricket {
|
|
|
|
RawTransportChannel::RawTransportChannel(const std::string& content_name,
|
|
int component,
|
|
RawTransport* transport,
|
|
talk_base::Thread *worker_thread,
|
|
PortAllocator *allocator)
|
|
: TransportChannelImpl(content_name, component),
|
|
raw_transport_(transport),
|
|
allocator_(allocator),
|
|
allocator_session_(NULL),
|
|
stun_port_(NULL),
|
|
relay_port_(NULL),
|
|
port_(NULL),
|
|
use_relay_(false) {
|
|
if (worker_thread == NULL)
|
|
worker_thread_ = raw_transport_->worker_thread();
|
|
else
|
|
worker_thread_ = worker_thread;
|
|
}
|
|
|
|
RawTransportChannel::~RawTransportChannel() {
|
|
delete allocator_session_;
|
|
}
|
|
|
|
int RawTransportChannel::SendPacket(const char *data, size_t size,
|
|
talk_base::DiffServCodePoint dscp,
|
|
int flags) {
|
|
if (port_ == NULL)
|
|
return -1;
|
|
if (remote_address_.IsNil())
|
|
return -1;
|
|
if (flags != 0)
|
|
return -1;
|
|
return port_->SendTo(data, size, remote_address_, dscp, true);
|
|
}
|
|
|
|
int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
|
|
// TODO: allow these to be set before we have a port
|
|
if (port_ == NULL)
|
|
return -1;
|
|
return port_->SetOption(opt, value);
|
|
}
|
|
|
|
int RawTransportChannel::GetError() {
|
|
return (port_ != NULL) ? port_->GetError() : 0;
|
|
}
|
|
|
|
void RawTransportChannel::Connect() {
|
|
// Create an allocator that only returns stun and relay ports.
|
|
// Use empty string for ufrag and pwd here. There won't be any STUN or relay
|
|
// interactions when using RawTC.
|
|
// TODO: Change raw to only use local udp ports.
|
|
allocator_session_ = allocator_->CreateSession(
|
|
SessionId(), content_name(), component(), "", "");
|
|
|
|
uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
|
|
|
|
#if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
|
|
flags |= PORTALLOCATOR_DISABLE_RELAY;
|
|
#endif
|
|
allocator_session_->set_flags(flags);
|
|
allocator_session_->SignalPortReady.connect(
|
|
this, &RawTransportChannel::OnPortReady);
|
|
allocator_session_->SignalCandidatesReady.connect(
|
|
this, &RawTransportChannel::OnCandidatesReady);
|
|
|
|
// The initial ports will include stun.
|
|
allocator_session_->StartGettingPorts();
|
|
}
|
|
|
|
void RawTransportChannel::Reset() {
|
|
set_readable(false);
|
|
set_writable(false);
|
|
|
|
delete allocator_session_;
|
|
|
|
allocator_session_ = NULL;
|
|
stun_port_ = NULL;
|
|
relay_port_ = NULL;
|
|
port_ = NULL;
|
|
remote_address_ = talk_base::SocketAddress();
|
|
}
|
|
|
|
void RawTransportChannel::OnCandidate(const Candidate& candidate) {
|
|
remote_address_ = candidate.address();
|
|
ASSERT(!remote_address_.IsNil());
|
|
set_readable(true);
|
|
|
|
// We can write once we have a port and a remote address.
|
|
if (port_ != NULL)
|
|
SetWritable();
|
|
}
|
|
|
|
void RawTransportChannel::OnRemoteAddress(
|
|
const talk_base::SocketAddress& remote_address) {
|
|
remote_address_ = remote_address;
|
|
set_readable(true);
|
|
|
|
if (port_ != NULL)
|
|
SetWritable();
|
|
}
|
|
|
|
// Note about stun classification
|
|
// Code to classify our NAT type and use the relay port if we are behind an
|
|
// asymmetric NAT is under a FEATURE_ENABLE_STUN_CLASSIFICATION #define.
|
|
// To turn this one we will have to enable a second stun address and make sure
|
|
// that the relay server works for raw UDP.
|
|
//
|
|
// Another option is to classify the NAT type early and not offer the raw
|
|
// transport type at all if we can't support it.
|
|
|
|
void RawTransportChannel::OnPortReady(
|
|
PortAllocatorSession* session, PortInterface* port) {
|
|
ASSERT(session == allocator_session_);
|
|
|
|
if (port->Type() == STUN_PORT_TYPE) {
|
|
stun_port_ = static_cast<StunPort*>(port);
|
|
} else if (port->Type() == RELAY_PORT_TYPE) {
|
|
relay_port_ = static_cast<RelayPort*>(port);
|
|
} else {
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void RawTransportChannel::OnCandidatesReady(
|
|
PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
|
|
ASSERT(session == allocator_session_);
|
|
ASSERT(candidates.size() >= 1);
|
|
|
|
// The most recent candidate is the one we haven't seen yet.
|
|
Candidate c = candidates[candidates.size() - 1];
|
|
|
|
if (c.type() == STUN_PORT_TYPE) {
|
|
ASSERT(stun_port_ != NULL);
|
|
|
|
#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
|
|
// We need to wait until we have two addresses.
|
|
if (stun_port_->candidates().size() < 2)
|
|
return;
|
|
|
|
// This is the second address. If these addresses are the same, then we
|
|
// are not behind a symmetric NAT. Hence, a stun port should be sufficient.
|
|
if (stun_port_->candidates()[0].address() ==
|
|
stun_port_->candidates()[1].address()) {
|
|
SetPort(stun_port_);
|
|
return;
|
|
}
|
|
|
|
// We will need to use relay.
|
|
use_relay_ = true;
|
|
|
|
// If we already have a relay address, we're good. Otherwise, we will need
|
|
// to wait until one arrives.
|
|
if (relay_port_->candidates().size() > 0)
|
|
SetPort(relay_port_);
|
|
#else // defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
|
|
// Always use the stun port. We don't classify right now so just assume it
|
|
// will work fine.
|
|
SetPort(stun_port_);
|
|
#endif
|
|
} else if (c.type() == RELAY_PORT_TYPE) {
|
|
if (use_relay_)
|
|
SetPort(relay_port_);
|
|
} else {
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void RawTransportChannel::SetPort(PortInterface* port) {
|
|
ASSERT(port_ == NULL);
|
|
port_ = port;
|
|
|
|
// We don't need any ports other than the one we picked.
|
|
allocator_session_->StopGettingPorts();
|
|
worker_thread_->Post(
|
|
this, MSG_DESTROY_UNUSED_PORTS, NULL);
|
|
|
|
// Send a message to the other client containing our address.
|
|
|
|
ASSERT(port_->Candidates().size() >= 1);
|
|
ASSERT(port_->Candidates()[0].protocol() == "udp");
|
|
SignalCandidateReady(this, port_->Candidates()[0]);
|
|
|
|
// Read all packets from this port.
|
|
port_->EnablePortPackets();
|
|
port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket);
|
|
|
|
// We can write once we have a port and a remote address.
|
|
if (!remote_address_.IsAny())
|
|
SetWritable();
|
|
}
|
|
|
|
void RawTransportChannel::SetWritable() {
|
|
ASSERT(port_ != NULL);
|
|
ASSERT(!remote_address_.IsAny());
|
|
|
|
set_writable(true);
|
|
|
|
Candidate remote_candidate;
|
|
remote_candidate.set_address(remote_address_);
|
|
SignalRouteChange(this, remote_candidate);
|
|
}
|
|
|
|
void RawTransportChannel::OnReadPacket(
|
|
PortInterface* port, const char* data, size_t size,
|
|
const talk_base::SocketAddress& addr) {
|
|
ASSERT(port_ == port);
|
|
SignalReadPacket(this, data, size, 0);
|
|
}
|
|
|
|
void RawTransportChannel::OnMessage(talk_base::Message* msg) {
|
|
ASSERT(msg->message_id == MSG_DESTROY_UNUSED_PORTS);
|
|
ASSERT(port_ != NULL);
|
|
if (port_ != stun_port_) {
|
|
stun_port_->Destroy();
|
|
stun_port_ = NULL;
|
|
}
|
|
if (port_ != relay_port_ && relay_port_ != NULL) {
|
|
relay_port_->Destroy();
|
|
relay_port_ = NULL;
|
|
}
|
|
}
|
|
|
|
} // namespace cricket
|
|
#endif // defined(FEATURE_ENABLE_PSTN)
|