Removes talk/xmllite, talk/xmpp and talk/p2p as they are no longer used by gyp/gn builds.

TBR=niklas.enbom@webrtc.org
BUG=3379

Review URL: https://webrtc-codereview.appspot.com/30959004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7670 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrike@webrtc.org 2014-11-10 15:31:24 +00:00
parent d819803d45
commit c4922316b4
220 changed files with 0 additions and 66476 deletions

View File

@ -1,170 +0,0 @@
/*
* libjingle
* Copyright 2013, 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 "webrtc/p2p/base/asyncstuntcpsocket.h"
#include <string.h>
#include "webrtc/p2p/base/stun.h"
#include "webrtc/base/common.h"
#include "webrtc/base/logging.h"
namespace cricket {
static const size_t kMaxPacketSize = 64 * 1024;
typedef uint16 PacketLength;
static const size_t kPacketLenSize = sizeof(PacketLength);
static const size_t kPacketLenOffset = 2;
static const size_t kBufSize = kMaxPacketSize + kStunHeaderSize;
static const size_t kTurnChannelDataHdrSize = 4;
inline bool IsStunMessage(uint16 msg_type) {
// The first two bits of a channel data message are 0b01.
return (msg_type & 0xC000) ? false : true;
}
// AsyncStunTCPSocket
// Binds and connects |socket| and creates AsyncTCPSocket for
// it. Takes ownership of |socket|. Returns NULL if bind() or
// connect() fail (|socket| is destroyed in that case).
AsyncStunTCPSocket* AsyncStunTCPSocket::Create(
rtc::AsyncSocket* socket,
const rtc::SocketAddress& bind_address,
const rtc::SocketAddress& remote_address) {
return new AsyncStunTCPSocket(AsyncTCPSocketBase::ConnectSocket(
socket, bind_address, remote_address), false);
}
AsyncStunTCPSocket::AsyncStunTCPSocket(
rtc::AsyncSocket* socket, bool listen)
: rtc::AsyncTCPSocketBase(socket, listen, kBufSize) {
}
int AsyncStunTCPSocket::Send(const void *pv, size_t cb,
const rtc::PacketOptions& options) {
if (cb > kBufSize || cb < kPacketLenSize + kPacketLenOffset) {
SetError(EMSGSIZE);
return -1;
}
// If we are blocking on send, then silently drop this packet
if (!IsOutBufferEmpty())
return static_cast<int>(cb);
int pad_bytes;
size_t expected_pkt_len = GetExpectedLength(pv, cb, &pad_bytes);
// Accepts only complete STUN/ChannelData packets.
if (cb != expected_pkt_len)
return -1;
AppendToOutBuffer(pv, cb);
ASSERT(pad_bytes < 4);
char padding[4] = {0};
AppendToOutBuffer(padding, pad_bytes);
int res = FlushOutBuffer();
if (res <= 0) {
// drop packet if we made no progress
ClearOutBuffer();
return res;
}
// We claim to have sent the whole thing, even if we only sent partial
return static_cast<int>(cb);
}
void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) {
rtc::SocketAddress remote_addr(GetRemoteAddress());
// STUN packet - First 4 bytes. Total header size is 20 bytes.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0 0| STUN Message Type | Message Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// TURN ChannelData
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Channel Number | Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
while (true) {
// We need at least 4 bytes to read the STUN or ChannelData packet length.
if (*len < kPacketLenOffset + kPacketLenSize)
return;
int pad_bytes;
size_t expected_pkt_len = GetExpectedLength(data, *len, &pad_bytes);
size_t actual_length = expected_pkt_len + pad_bytes;
if (*len < actual_length) {
return;
}
SignalReadPacket(this, data, expected_pkt_len, remote_addr,
rtc::CreatePacketTime(0));
*len -= actual_length;
if (*len > 0) {
memmove(data, data + actual_length, *len);
}
}
}
void AsyncStunTCPSocket::HandleIncomingConnection(
rtc::AsyncSocket* socket) {
SignalNewConnection(this, new AsyncStunTCPSocket(socket, false));
}
size_t AsyncStunTCPSocket::GetExpectedLength(const void* data, size_t len,
int* pad_bytes) {
*pad_bytes = 0;
PacketLength pkt_len =
rtc::GetBE16(static_cast<const char*>(data) + kPacketLenOffset);
size_t expected_pkt_len;
uint16 msg_type = rtc::GetBE16(data);
if (IsStunMessage(msg_type)) {
// STUN message.
expected_pkt_len = kStunHeaderSize + pkt_len;
} else {
// TURN ChannelData message.
expected_pkt_len = kTurnChannelDataHdrSize + pkt_len;
// From RFC 5766 section 11.5
// Over TCP and TLS-over-TCP, the ChannelData message MUST be padded to
// a multiple of four bytes in order to ensure the alignment of
// subsequent messages. The padding is not reflected in the length
// field of the ChannelData message, so the actual size of a ChannelData
// message (including padding) is (4 + Length) rounded up to the nearest
// multiple of 4. Over UDP, the padding is not required but MAY be
// included.
if (expected_pkt_len % 4)
*pad_bytes = 4 - (expected_pkt_len % 4);
}
return expected_pkt_len;
}
} // namespace cricket

View File

@ -1,67 +0,0 @@
/*
* libjingle
* Copyright 2013, 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.
*/
#ifndef WEBRTC_P2P_BASE_ASYNCSTUNTCPSOCKET_H_
#define WEBRTC_P2P_BASE_ASYNCSTUNTCPSOCKET_H_
#include "webrtc/base/asynctcpsocket.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/socketfactory.h"
namespace cricket {
class AsyncStunTCPSocket : public rtc::AsyncTCPSocketBase {
public:
// Binds and connects |socket| and creates AsyncTCPSocket for
// it. Takes ownership of |socket|. Returns NULL if bind() or
// connect() fail (|socket| is destroyed in that case).
static AsyncStunTCPSocket* Create(
rtc::AsyncSocket* socket,
const rtc::SocketAddress& bind_address,
const rtc::SocketAddress& remote_address);
AsyncStunTCPSocket(rtc::AsyncSocket* socket, bool listen);
virtual ~AsyncStunTCPSocket() {}
virtual int Send(const void* pv, size_t cb,
const rtc::PacketOptions& options);
virtual void ProcessInput(char* data, size_t* len);
virtual void HandleIncomingConnection(rtc::AsyncSocket* socket);
private:
// This method returns the message hdr + length written in the header.
// This method also returns the number of padding bytes needed/added to the
// turn message. |pad_bytes| should be used only when |is_turn| is true.
size_t GetExpectedLength(const void* data, size_t len,
int* pad_bytes);
DISALLOW_EVIL_CONSTRUCTORS(AsyncStunTCPSocket);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_ASYNCSTUNTCPSOCKET_H_

View File

@ -1,280 +0,0 @@
/*
* libjingle
* Copyright 2013, 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 "webrtc/p2p/base/asyncstuntcpsocket.h"
#include "webrtc/base/asyncsocket.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/virtualsocketserver.h"
namespace cricket {
static unsigned char kStunMessageWithZeroLength[] = {
0x00, 0x01, 0x00, 0x00, // length of 0 (last 2 bytes)
0x21, 0x12, 0xA4, 0x42,
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
};
static unsigned char kTurnChannelDataMessageWithZeroLength[] = {
0x40, 0x00, 0x00, 0x00, // length of 0 (last 2 bytes)
};
static unsigned char kTurnChannelDataMessage[] = {
0x40, 0x00, 0x00, 0x10,
0x21, 0x12, 0xA4, 0x42,
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
};
static unsigned char kStunMessageWithInvalidLength[] = {
0x00, 0x01, 0x00, 0x10,
0x21, 0x12, 0xA4, 0x42,
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
};
static unsigned char kTurnChannelDataMessageWithInvalidLength[] = {
0x80, 0x00, 0x00, 0x20,
0x21, 0x12, 0xA4, 0x42,
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
};
static unsigned char kTurnChannelDataMessageWithOddLength[] = {
0x40, 0x00, 0x00, 0x05,
0x21, 0x12, 0xA4, 0x42,
'0',
};
static const rtc::SocketAddress kClientAddr("11.11.11.11", 0);
static const rtc::SocketAddress kServerAddr("22.22.22.22", 0);
class AsyncStunTCPSocketTest : public testing::Test,
public sigslot::has_slots<> {
protected:
AsyncStunTCPSocketTest()
: vss_(new rtc::VirtualSocketServer(NULL)),
ss_scope_(vss_.get()) {
}
virtual void SetUp() {
CreateSockets();
}
void CreateSockets() {
rtc::AsyncSocket* server = vss_->CreateAsyncSocket(
kServerAddr.family(), SOCK_STREAM);
server->Bind(kServerAddr);
recv_socket_.reset(new AsyncStunTCPSocket(server, true));
recv_socket_->SignalNewConnection.connect(
this, &AsyncStunTCPSocketTest::OnNewConnection);
rtc::AsyncSocket* client = vss_->CreateAsyncSocket(
kClientAddr.family(), SOCK_STREAM);
send_socket_.reset(AsyncStunTCPSocket::Create(
client, kClientAddr, recv_socket_->GetLocalAddress()));
ASSERT_TRUE(send_socket_.get() != NULL);
vss_->ProcessMessagesUntilIdle();
}
void OnReadPacket(rtc::AsyncPacketSocket* socket, const char* data,
size_t len, const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
recv_packets_.push_back(std::string(data, len));
}
void OnNewConnection(rtc::AsyncPacketSocket* server,
rtc::AsyncPacketSocket* new_socket) {
listen_socket_.reset(new_socket);
new_socket->SignalReadPacket.connect(
this, &AsyncStunTCPSocketTest::OnReadPacket);
}
bool Send(const void* data, size_t len) {
rtc::PacketOptions options;
size_t ret = send_socket_->Send(
reinterpret_cast<const char*>(data), len, options);
vss_->ProcessMessagesUntilIdle();
return (ret == len);
}
bool CheckData(const void* data, int len) {
bool ret = false;
if (recv_packets_.size()) {
std::string packet = recv_packets_.front();
recv_packets_.pop_front();
ret = (memcmp(data, packet.c_str(), len) == 0);
}
return ret;
}
rtc::scoped_ptr<rtc::VirtualSocketServer> vss_;
rtc::SocketServerScope ss_scope_;
rtc::scoped_ptr<AsyncStunTCPSocket> send_socket_;
rtc::scoped_ptr<AsyncStunTCPSocket> recv_socket_;
rtc::scoped_ptr<rtc::AsyncPacketSocket> listen_socket_;
std::list<std::string> recv_packets_;
};
// Testing a stun packet sent/recv properly.
TEST_F(AsyncStunTCPSocketTest, TestSingleStunPacket) {
EXPECT_TRUE(Send(kStunMessageWithZeroLength,
sizeof(kStunMessageWithZeroLength)));
EXPECT_EQ(1u, recv_packets_.size());
EXPECT_TRUE(CheckData(kStunMessageWithZeroLength,
sizeof(kStunMessageWithZeroLength)));
}
// Verify sending multiple packets.
TEST_F(AsyncStunTCPSocketTest, TestMultipleStunPackets) {
EXPECT_TRUE(Send(kStunMessageWithZeroLength,
sizeof(kStunMessageWithZeroLength)));
EXPECT_TRUE(Send(kStunMessageWithZeroLength,
sizeof(kStunMessageWithZeroLength)));
EXPECT_TRUE(Send(kStunMessageWithZeroLength,
sizeof(kStunMessageWithZeroLength)));
EXPECT_TRUE(Send(kStunMessageWithZeroLength,
sizeof(kStunMessageWithZeroLength)));
EXPECT_EQ(4u, recv_packets_.size());
}
// Verifying TURN channel data message with zero length.
TEST_F(AsyncStunTCPSocketTest, TestTurnChannelDataWithZeroLength) {
EXPECT_TRUE(Send(kTurnChannelDataMessageWithZeroLength,
sizeof(kTurnChannelDataMessageWithZeroLength)));
EXPECT_EQ(1u, recv_packets_.size());
EXPECT_TRUE(CheckData(kTurnChannelDataMessageWithZeroLength,
sizeof(kTurnChannelDataMessageWithZeroLength)));
}
// Verifying TURN channel data message.
TEST_F(AsyncStunTCPSocketTest, TestTurnChannelData) {
EXPECT_TRUE(Send(kTurnChannelDataMessage,
sizeof(kTurnChannelDataMessage)));
EXPECT_EQ(1u, recv_packets_.size());
EXPECT_TRUE(CheckData(kTurnChannelDataMessage,
sizeof(kTurnChannelDataMessage)));
}
// Verifying TURN channel messages which needs padding handled properly.
TEST_F(AsyncStunTCPSocketTest, TestTurnChannelDataPadding) {
EXPECT_TRUE(Send(kTurnChannelDataMessageWithOddLength,
sizeof(kTurnChannelDataMessageWithOddLength)));
EXPECT_EQ(1u, recv_packets_.size());
EXPECT_TRUE(CheckData(kTurnChannelDataMessageWithOddLength,
sizeof(kTurnChannelDataMessageWithOddLength)));
}
// Verifying stun message with invalid length.
TEST_F(AsyncStunTCPSocketTest, TestStunInvalidLength) {
EXPECT_FALSE(Send(kStunMessageWithInvalidLength,
sizeof(kStunMessageWithInvalidLength)));
EXPECT_EQ(0u, recv_packets_.size());
// Modify the message length to larger value.
kStunMessageWithInvalidLength[2] = 0xFF;
kStunMessageWithInvalidLength[3] = 0xFF;
EXPECT_FALSE(Send(kStunMessageWithInvalidLength,
sizeof(kStunMessageWithInvalidLength)));
// Modify the message length to smaller value.
kStunMessageWithInvalidLength[2] = 0x00;
kStunMessageWithInvalidLength[3] = 0x01;
EXPECT_FALSE(Send(kStunMessageWithInvalidLength,
sizeof(kStunMessageWithInvalidLength)));
}
// Verifying TURN channel data message with invalid length.
TEST_F(AsyncStunTCPSocketTest, TestTurnChannelDataWithInvalidLength) {
EXPECT_FALSE(Send(kTurnChannelDataMessageWithInvalidLength,
sizeof(kTurnChannelDataMessageWithInvalidLength)));
// Modify the length to larger value.
kTurnChannelDataMessageWithInvalidLength[2] = 0xFF;
kTurnChannelDataMessageWithInvalidLength[3] = 0xF0;
EXPECT_FALSE(Send(kTurnChannelDataMessageWithInvalidLength,
sizeof(kTurnChannelDataMessageWithInvalidLength)));
// Modify the length to smaller value.
kTurnChannelDataMessageWithInvalidLength[2] = 0x00;
kTurnChannelDataMessageWithInvalidLength[3] = 0x00;
EXPECT_FALSE(Send(kTurnChannelDataMessageWithInvalidLength,
sizeof(kTurnChannelDataMessageWithInvalidLength)));
}
// Verifying a small buffer handled (dropped) properly. This will be
// a common one for both stun and turn.
TEST_F(AsyncStunTCPSocketTest, TestTooSmallMessageBuffer) {
char data[1];
EXPECT_FALSE(Send(data, sizeof(data)));
}
// Verifying a legal large turn message.
TEST_F(AsyncStunTCPSocketTest, TestMaximumSizeTurnPacket) {
// We have problem in getting the SignalWriteEvent from the virtual socket
// server. So increasing the send buffer to 64k.
// TODO(mallinath) - Remove this setting after we fix vss issue.
vss_->set_send_buffer_capacity(64 * 1024);
unsigned char packet[65539];
packet[0] = 0x40;
packet[1] = 0x00;
packet[2] = 0xFF;
packet[3] = 0xFF;
EXPECT_TRUE(Send(packet, sizeof(packet)));
}
// Verifying a legal large stun message.
TEST_F(AsyncStunTCPSocketTest, TestMaximumSizeStunPacket) {
// We have problem in getting the SignalWriteEvent from the virtual socket
// server. So increasing the send buffer to 64k.
// TODO(mallinath) - Remove this setting after we fix vss issue.
vss_->set_send_buffer_capacity(64 * 1024);
unsigned char packet[65552];
packet[0] = 0x00;
packet[1] = 0x01;
packet[2] = 0xFF;
packet[3] = 0xFC;
EXPECT_TRUE(Send(packet, sizeof(packet)));
}
// Investigate why WriteEvent is not signaled from VSS.
TEST_F(AsyncStunTCPSocketTest, DISABLED_TestWithSmallSendBuffer) {
vss_->set_send_buffer_capacity(1);
Send(kTurnChannelDataMessageWithOddLength,
sizeof(kTurnChannelDataMessageWithOddLength));
EXPECT_EQ(1u, recv_packets_.size());
EXPECT_TRUE(CheckData(kTurnChannelDataMessageWithOddLength,
sizeof(kTurnChannelDataMessageWithOddLength)));
}
} // namespace cricket

View File

@ -1,221 +0,0 @@
/*
* libjingle
* Copyright 2011, 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 "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/asyncstuntcpsocket.h"
#include "webrtc/p2p/base/stun.h"
#include "webrtc/base/asynctcpsocket.h"
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/nethelpers.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/socketadapters.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
namespace rtc {
BasicPacketSocketFactory::BasicPacketSocketFactory()
: thread_(Thread::Current()),
socket_factory_(NULL) {
}
BasicPacketSocketFactory::BasicPacketSocketFactory(Thread* thread)
: thread_(thread),
socket_factory_(NULL) {
}
BasicPacketSocketFactory::BasicPacketSocketFactory(
SocketFactory* socket_factory)
: thread_(NULL),
socket_factory_(socket_factory) {
}
BasicPacketSocketFactory::~BasicPacketSocketFactory() {
}
AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket(
const SocketAddress& address, int min_port, int max_port) {
// UDP sockets are simple.
rtc::AsyncSocket* socket =
socket_factory()->CreateAsyncSocket(
address.family(), SOCK_DGRAM);
if (!socket) {
return NULL;
}
if (BindSocket(socket, address, min_port, max_port) < 0) {
LOG(LS_ERROR) << "UDP bind failed with error "
<< socket->GetError();
delete socket;
return NULL;
}
return new rtc::AsyncUDPSocket(socket);
}
AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
const SocketAddress& local_address, int min_port, int max_port, int opts) {
// Fail if TLS is required.
if (opts & PacketSocketFactory::OPT_TLS) {
LOG(LS_ERROR) << "TLS support currently is not available.";
return NULL;
}
rtc::AsyncSocket* socket =
socket_factory()->CreateAsyncSocket(local_address.family(),
SOCK_STREAM);
if (!socket) {
return NULL;
}
if (BindSocket(socket, local_address, min_port, max_port) < 0) {
LOG(LS_ERROR) << "TCP bind failed with error "
<< socket->GetError();
delete socket;
return NULL;
}
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
if (opts & PacketSocketFactory::OPT_SSLTCP) {
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
socket = new rtc::AsyncSSLSocket(socket);
}
// Set TCP_NODELAY (via OPT_NODELAY) for improved performance.
// See http://go/gtalktcpnodelayexperiment
socket->SetOption(rtc::Socket::OPT_NODELAY, 1);
if (opts & PacketSocketFactory::OPT_STUN)
return new cricket::AsyncStunTCPSocket(socket, true);
return new rtc::AsyncTCPSocket(socket, true);
}
AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
const SocketAddress& local_address, const SocketAddress& remote_address,
const ProxyInfo& proxy_info, const std::string& user_agent, int opts) {
rtc::AsyncSocket* socket =
socket_factory()->CreateAsyncSocket(local_address.family(), SOCK_STREAM);
if (!socket) {
return NULL;
}
if (BindSocket(socket, local_address, 0, 0) < 0) {
LOG(LS_ERROR) << "TCP bind failed with error "
<< socket->GetError();
delete socket;
return NULL;
}
// If using a proxy, wrap the socket in a proxy socket.
if (proxy_info.type == rtc::PROXY_SOCKS5) {
socket = new rtc::AsyncSocksProxySocket(
socket, proxy_info.address, proxy_info.username, proxy_info.password);
} else if (proxy_info.type == rtc::PROXY_HTTPS) {
socket = new rtc::AsyncHttpsProxySocket(
socket, user_agent, proxy_info.address,
proxy_info.username, proxy_info.password);
}
// If using TLS, wrap the socket in an SSL adapter.
if (opts & PacketSocketFactory::OPT_TLS) {
ASSERT(!(opts & PacketSocketFactory::OPT_SSLTCP));
rtc::SSLAdapter* ssl_adapter = rtc::SSLAdapter::Create(socket);
if (!ssl_adapter) {
return NULL;
}
socket = ssl_adapter;
if (ssl_adapter->StartSSL(remote_address.hostname().c_str(), false) != 0) {
delete ssl_adapter;
return NULL;
}
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
} else if (opts & PacketSocketFactory::OPT_SSLTCP) {
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
socket = new rtc::AsyncSSLSocket(socket);
}
if (socket->Connect(remote_address) < 0) {
LOG(LS_ERROR) << "TCP connect failed with error "
<< socket->GetError();
delete socket;
return NULL;
}
// Finally, wrap that socket in a TCP or STUN TCP packet socket.
AsyncPacketSocket* tcp_socket;
if (opts & PacketSocketFactory::OPT_STUN) {
tcp_socket = new cricket::AsyncStunTCPSocket(socket, false);
} else {
tcp_socket = new rtc::AsyncTCPSocket(socket, false);
}
// Set TCP_NODELAY (via OPT_NODELAY) for improved performance.
// See http://go/gtalktcpnodelayexperiment
tcp_socket->SetOption(rtc::Socket::OPT_NODELAY, 1);
return tcp_socket;
}
AsyncResolverInterface* BasicPacketSocketFactory::CreateAsyncResolver() {
return new rtc::AsyncResolver();
}
int BasicPacketSocketFactory::BindSocket(
AsyncSocket* socket, const SocketAddress& local_address,
int min_port, int max_port) {
int ret = -1;
if (min_port == 0 && max_port == 0) {
// If there's no port range, let the OS pick a port for us.
ret = socket->Bind(local_address);
} else {
// Otherwise, try to find a port in the provided range.
for (int port = min_port; ret < 0 && port <= max_port; ++port) {
ret = socket->Bind(rtc::SocketAddress(local_address.ipaddr(),
port));
}
}
return ret;
}
SocketFactory* BasicPacketSocketFactory::socket_factory() {
if (thread_) {
ASSERT(thread_ == Thread::Current());
return thread_->socketserver();
} else {
return socket_factory_;
}
}
} // namespace rtc

View File

@ -1,68 +0,0 @@
/*
* libjingle
* Copyright 2011, 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.
*/
#ifndef WEBRTC_P2P_BASE_BASICPACKETSOCKETFACTORY_H_
#define WEBRTC_P2P_BASE_BASICPACKETSOCKETFACTORY_H_
#include "webrtc/p2p/base/packetsocketfactory.h"
namespace rtc {
class AsyncSocket;
class SocketFactory;
class Thread;
class BasicPacketSocketFactory : public PacketSocketFactory {
public:
BasicPacketSocketFactory();
explicit BasicPacketSocketFactory(Thread* thread);
explicit BasicPacketSocketFactory(SocketFactory* socket_factory);
virtual ~BasicPacketSocketFactory();
virtual AsyncPacketSocket* CreateUdpSocket(
const SocketAddress& local_address, int min_port, int max_port);
virtual AsyncPacketSocket* CreateServerTcpSocket(
const SocketAddress& local_address, int min_port, int max_port, int opts);
virtual AsyncPacketSocket* CreateClientTcpSocket(
const SocketAddress& local_address, const SocketAddress& remote_address,
const ProxyInfo& proxy_info, const std::string& user_agent, int opts);
virtual AsyncResolverInterface* CreateAsyncResolver();
private:
int BindSocket(AsyncSocket* socket, const SocketAddress& local_address,
int min_port, int max_port);
SocketFactory* socket_factory();
Thread* thread_;
SocketFactory* socket_factory_;
};
} // namespace rtc
#endif // WEBRTC_P2P_BASE_BASICPACKETSOCKETFACTORY_H_

View File

@ -1,229 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_CANDIDATE_H_
#define WEBRTC_P2P_BASE_CANDIDATE_H_
#include <limits.h>
#include <math.h>
#include <iomanip>
#include <sstream>
#include <string>
#include "webrtc/p2p/base/constants.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/socketaddress.h"
namespace cricket {
// Candidate for ICE based connection discovery.
class Candidate {
public:
// TODO: Match the ordering and param list as per RFC 5245
// candidate-attribute syntax. http://tools.ietf.org/html/rfc5245#section-15.1
Candidate() : component_(0), priority_(0), generation_(0) {}
Candidate(const std::string& id, int component, const std::string& protocol,
const rtc::SocketAddress& address, uint32 priority,
const std::string& username, const std::string& password,
const std::string& type, const std::string& network_name,
uint32 generation, const std::string& foundation)
: id_(id), component_(component), protocol_(protocol), address_(address),
priority_(priority), username_(username), password_(password),
type_(type), network_name_(network_name), generation_(generation),
foundation_(foundation) {
}
const std::string & id() const { return id_; }
void set_id(const std::string & id) { id_ = id; }
int component() const { return component_; }
void set_component(int component) { component_ = component; }
const std::string & protocol() const { return protocol_; }
void set_protocol(const std::string & protocol) { protocol_ = protocol; }
const rtc::SocketAddress & address() const { return address_; }
void set_address(const rtc::SocketAddress & address) {
address_ = address;
}
uint32 priority() const { return priority_; }
void set_priority(const uint32 priority) { priority_ = priority; }
// void set_type_preference(uint32 type_preference) {
// priority_ = GetPriority(type_preference);
// }
// Maps old preference (which was 0.0-1.0) to match priority (which
// is 0-2^32-1) to to match RFC 5245, section 4.1.2.1. Also see
// https://docs.google.com/a/google.com/document/d/
// 1iNQDiwDKMh0NQOrCqbj3DKKRT0Dn5_5UJYhmZO-t7Uc/edit
float preference() const {
// The preference value is clamped to two decimal precision.
return static_cast<float>(((priority_ >> 24) * 100 / 127) / 100.0);
}
void set_preference(float preference) {
// Limiting priority to UINT_MAX when value exceeds uint32 max.
// This can happen for e.g. when preference = 3.
uint64 prio_val = static_cast<uint64>(preference * 127) << 24;
priority_ = static_cast<uint32>(
rtc::_min(prio_val, static_cast<uint64>(UINT_MAX)));
}
const std::string & username() const { return username_; }
void set_username(const std::string & username) { username_ = username; }
const std::string & password() const { return password_; }
void set_password(const std::string & password) { password_ = password; }
const std::string & type() const { return type_; }
void set_type(const std::string & type) { type_ = type; }
const std::string & network_name() const { return network_name_; }
void set_network_name(const std::string & network_name) {
network_name_ = network_name;
}
// Candidates in a new generation replace those in the old generation.
uint32 generation() const { return generation_; }
void set_generation(uint32 generation) { generation_ = generation; }
const std::string generation_str() const {
std::ostringstream ost;
ost << generation_;
return ost.str();
}
void set_generation_str(const std::string& str) {
std::istringstream ist(str);
ist >> generation_;
}
const std::string& foundation() const {
return foundation_;
}
void set_foundation(const std::string& foundation) {
foundation_ = foundation;
}
const rtc::SocketAddress & related_address() const {
return related_address_;
}
void set_related_address(
const rtc::SocketAddress & related_address) {
related_address_ = related_address;
}
const std::string& tcptype() const { return tcptype_; }
void set_tcptype(const std::string& tcptype){
tcptype_ = tcptype;
}
// Determines whether this candidate is equivalent to the given one.
bool IsEquivalent(const Candidate& c) const {
// We ignore the network name, since that is just debug information, and
// the priority, since that should be the same if the rest is (and it's
// a float so equality checking is always worrisome).
return (id_ == c.id_) &&
(component_ == c.component_) &&
(protocol_ == c.protocol_) &&
(address_ == c.address_) &&
(username_ == c.username_) &&
(password_ == c.password_) &&
(type_ == c.type_) &&
(generation_ == c.generation_) &&
(foundation_ == c.foundation_) &&
(related_address_ == c.related_address_);
}
std::string ToString() const {
return ToStringInternal(false);
}
std::string ToSensitiveString() const {
return ToStringInternal(true);
}
uint32 GetPriority(uint32 type_preference,
int network_adapter_preference,
int relay_preference) const {
// RFC 5245 - 4.1.2.1.
// priority = (2^24)*(type preference) +
// (2^8)*(local preference) +
// (2^0)*(256 - component ID)
// |local_preference| length is 2 bytes, 0-65535 inclusive.
// In our implemenation we will partion local_preference into
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NIC Pref | Addr Pref |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// NIC Type - Type of the network adapter e.g. 3G/Wifi/Wired.
// Addr Pref - Address preference value as per RFC 3484.
// local preference = (NIC Type << 8 | Addr_Pref) - relay preference.
int addr_pref = IPAddressPrecedence(address_.ipaddr());
int local_preference = ((network_adapter_preference << 8) | addr_pref) +
relay_preference;
return (type_preference << 24) |
(local_preference << 8) |
(256 - component_);
}
private:
std::string ToStringInternal(bool sensitive) const {
std::ostringstream ost;
std::string address = sensitive ? address_.ToSensitiveString() :
address_.ToString();
ost << "Cand[" << foundation_ << ":" << component_ << ":"
<< protocol_ << ":" << priority_ << ":"
<< address << ":" << type_ << ":" << related_address_ << ":"
<< username_ << ":" << password_ << "]";
return ost.str();
}
std::string id_;
int component_;
std::string protocol_;
rtc::SocketAddress address_;
uint32 priority_;
std::string username_;
std::string password_;
std::string type_;
std::string network_name_;
uint32 generation_;
std::string foundation_;
rtc::SocketAddress related_address_;
std::string tcptype_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_CANDIDATE_H_

View File

@ -1,37 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_COMMON_H_
#define WEBRTC_P2P_BASE_COMMON_H_
#include "webrtc/base/logging.h"
// Common log description format for jingle messages
#define LOG_J(sev, obj) LOG(sev) << "Jingle:" << obj->ToString() << ": "
#define LOG_JV(sev, obj) LOG_V(sev) << "Jingle:" << obj->ToString() << ": "
#endif // WEBRTC_P2P_BASE_COMMON_H_

View File

@ -1,274 +0,0 @@
/*
* 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 "webrtc/p2p/base/constants.h"
#include <string>
#include "webrtc/libjingle/xmllite/qname.h"
namespace cricket {
const char NS_EMPTY[] = "";
const char NS_JINGLE[] = "urn:xmpp:jingle:1";
const char NS_JINGLE_DRAFT[] = "google:jingle";
const char NS_GINGLE[] = "http://www.google.com/session";
// actions (aka <session> or <jingle>)
const buzz::StaticQName QN_ACTION = { NS_EMPTY, "action" };
const char LN_INITIATOR[] = "initiator";
const buzz::StaticQName QN_INITIATOR = { NS_EMPTY, LN_INITIATOR };
const buzz::StaticQName QN_CREATOR = { NS_EMPTY, "creator" };
const buzz::StaticQName QN_JINGLE = { NS_JINGLE, "jingle" };
const buzz::StaticQName QN_JINGLE_CONTENT = { NS_JINGLE, "content" };
const buzz::StaticQName QN_JINGLE_CONTENT_NAME = { NS_EMPTY, "name" };
const buzz::StaticQName QN_JINGLE_CONTENT_MEDIA = { NS_EMPTY, "media" };
const buzz::StaticQName QN_JINGLE_REASON = { NS_JINGLE, "reason" };
const buzz::StaticQName QN_JINGLE_DRAFT_GROUP = { NS_JINGLE_DRAFT, "group" };
const buzz::StaticQName QN_JINGLE_DRAFT_GROUP_TYPE = { NS_EMPTY, "type" };
const char JINGLE_CONTENT_MEDIA_AUDIO[] = "audio";
const char JINGLE_CONTENT_MEDIA_VIDEO[] = "video";
const char JINGLE_CONTENT_MEDIA_DATA[] = "data";
const char JINGLE_ACTION_SESSION_INITIATE[] = "session-initiate";
const char JINGLE_ACTION_SESSION_INFO[] = "session-info";
const char JINGLE_ACTION_SESSION_ACCEPT[] = "session-accept";
const char JINGLE_ACTION_SESSION_TERMINATE[] = "session-terminate";
const char JINGLE_ACTION_TRANSPORT_INFO[] = "transport-info";
const char JINGLE_ACTION_TRANSPORT_ACCEPT[] = "transport-accept";
const char JINGLE_ACTION_DESCRIPTION_INFO[] = "description-info";
const buzz::StaticQName QN_GINGLE_SESSION = { NS_GINGLE, "session" };
const char GINGLE_ACTION_INITIATE[] = "initiate";
const char GINGLE_ACTION_INFO[] = "info";
const char GINGLE_ACTION_ACCEPT[] = "accept";
const char GINGLE_ACTION_REJECT[] = "reject";
const char GINGLE_ACTION_TERMINATE[] = "terminate";
const char GINGLE_ACTION_CANDIDATES[] = "candidates";
const char GINGLE_ACTION_UPDATE[] = "update";
const char LN_ERROR[] = "error";
const buzz::StaticQName QN_GINGLE_REDIRECT = { NS_GINGLE, "redirect" };
const char STR_REDIRECT_PREFIX[] = "xmpp:";
// Session Contents (aka Gingle <session><description>
// or Jingle <content><description>)
const char LN_DESCRIPTION[] = "description";
const char LN_PAYLOADTYPE[] = "payload-type";
const buzz::StaticQName QN_ID = { NS_EMPTY, "id" };
const buzz::StaticQName QN_SID = { NS_EMPTY, "sid" };
const buzz::StaticQName QN_NAME = { NS_EMPTY, "name" };
const buzz::StaticQName QN_CLOCKRATE = { NS_EMPTY, "clockrate" };
const buzz::StaticQName QN_BITRATE = { NS_EMPTY, "bitrate" };
const buzz::StaticQName QN_CHANNELS = { NS_EMPTY, "channels" };
const buzz::StaticQName QN_WIDTH = { NS_EMPTY, "width" };
const buzz::StaticQName QN_HEIGHT = { NS_EMPTY, "height" };
const buzz::StaticQName QN_FRAMERATE = { NS_EMPTY, "framerate" };
const char LN_NAME[] = "name";
const char LN_VALUE[] = "value";
const buzz::StaticQName QN_PAYLOADTYPE_PARAMETER_NAME = { NS_EMPTY, LN_NAME };
const buzz::StaticQName QN_PAYLOADTYPE_PARAMETER_VALUE = { NS_EMPTY, LN_VALUE };
const char PAYLOADTYPE_PARAMETER_BITRATE[] = "bitrate";
const char PAYLOADTYPE_PARAMETER_HEIGHT[] = "height";
const char PAYLOADTYPE_PARAMETER_WIDTH[] = "width";
const char PAYLOADTYPE_PARAMETER_FRAMERATE[] = "framerate";
const char LN_BANDWIDTH[] = "bandwidth";
const char CN_AUDIO[] = "audio";
const char CN_VIDEO[] = "video";
const char CN_DATA[] = "data";
const char CN_OTHER[] = "main";
// other SDP related strings
const char GROUP_TYPE_BUNDLE[] = "BUNDLE";
const char NS_JINGLE_RTP[] = "urn:xmpp:jingle:apps:rtp:1";
const buzz::StaticQName QN_JINGLE_RTP_CONTENT =
{ NS_JINGLE_RTP, LN_DESCRIPTION };
const buzz::StaticQName QN_SSRC = { NS_EMPTY, "ssrc" };
const buzz::StaticQName QN_JINGLE_RTP_PAYLOADTYPE =
{ NS_JINGLE_RTP, LN_PAYLOADTYPE };
const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH =
{ NS_JINGLE_RTP, LN_BANDWIDTH };
const buzz::StaticQName QN_JINGLE_RTCP_MUX = { NS_JINGLE_RTP, "rtcp-mux" };
const buzz::StaticQName QN_JINGLE_RTCP_FB = { NS_JINGLE_RTP, "rtcp-fb" };
const buzz::StaticQName QN_SUBTYPE = { NS_EMPTY, "subtype" };
const buzz::StaticQName QN_PARAMETER = { NS_JINGLE_RTP, "parameter" };
const buzz::StaticQName QN_JINGLE_RTP_HDREXT =
{ NS_JINGLE_RTP, "rtp-hdrext" };
const buzz::StaticQName QN_URI = { NS_EMPTY, "uri" };
const char NS_JINGLE_DRAFT_SCTP[] = "google:jingle:sctp";
const buzz::StaticQName QN_JINGLE_DRAFT_SCTP_CONTENT =
{ NS_JINGLE_DRAFT_SCTP, LN_DESCRIPTION };
const buzz::StaticQName QN_JINGLE_DRAFT_SCTP_STREAM =
{ NS_JINGLE_DRAFT_SCTP, "stream" };
const char NS_GINGLE_AUDIO[] = "http://www.google.com/session/phone";
const buzz::StaticQName QN_GINGLE_AUDIO_CONTENT =
{ NS_GINGLE_AUDIO, LN_DESCRIPTION };
const buzz::StaticQName QN_GINGLE_AUDIO_PAYLOADTYPE =
{ NS_GINGLE_AUDIO, LN_PAYLOADTYPE };
const buzz::StaticQName QN_GINGLE_AUDIO_SRCID = { NS_GINGLE_AUDIO, "src-id" };
const char NS_GINGLE_VIDEO[] = "http://www.google.com/session/video";
const buzz::StaticQName QN_GINGLE_VIDEO_CONTENT =
{ NS_GINGLE_VIDEO, LN_DESCRIPTION };
const buzz::StaticQName QN_GINGLE_VIDEO_PAYLOADTYPE =
{ NS_GINGLE_VIDEO, LN_PAYLOADTYPE };
const buzz::StaticQName QN_GINGLE_VIDEO_SRCID = { NS_GINGLE_VIDEO, "src-id" };
const buzz::StaticQName QN_GINGLE_VIDEO_BANDWIDTH =
{ NS_GINGLE_VIDEO, LN_BANDWIDTH };
// Crypto support.
const buzz::StaticQName QN_ENCRYPTION = { NS_JINGLE_RTP, "encryption" };
const buzz::StaticQName QN_ENCRYPTION_REQUIRED = { NS_EMPTY, "required" };
const buzz::StaticQName QN_CRYPTO = { NS_JINGLE_RTP, "crypto" };
const buzz::StaticQName QN_GINGLE_AUDIO_CRYPTO_USAGE =
{ NS_GINGLE_AUDIO, "usage" };
const buzz::StaticQName QN_GINGLE_VIDEO_CRYPTO_USAGE =
{ NS_GINGLE_VIDEO, "usage" };
const buzz::StaticQName QN_CRYPTO_SUITE = { NS_EMPTY, "crypto-suite" };
const buzz::StaticQName QN_CRYPTO_KEY_PARAMS = { NS_EMPTY, "key-params" };
const buzz::StaticQName QN_CRYPTO_TAG = { NS_EMPTY, "tag" };
const buzz::StaticQName QN_CRYPTO_SESSION_PARAMS =
{ NS_EMPTY, "session-params" };
// Transports and candidates.
const char LN_TRANSPORT[] = "transport";
const char LN_CANDIDATE[] = "candidate";
const buzz::StaticQName QN_UFRAG = { cricket::NS_EMPTY, "ufrag" };
const buzz::StaticQName QN_PWD = { cricket::NS_EMPTY, "pwd" };
const buzz::StaticQName QN_COMPONENT = { cricket::NS_EMPTY, "component" };
const buzz::StaticQName QN_IP = { cricket::NS_EMPTY, "ip" };
const buzz::StaticQName QN_PORT = { cricket::NS_EMPTY, "port" };
const buzz::StaticQName QN_NETWORK = { cricket::NS_EMPTY, "network" };
const buzz::StaticQName QN_GENERATION = { cricket::NS_EMPTY, "generation" };
const buzz::StaticQName QN_PRIORITY = { cricket::NS_EMPTY, "priority" };
const buzz::StaticQName QN_PROTOCOL = { cricket::NS_EMPTY, "protocol" };
const char ICE_CANDIDATE_TYPE_PEER_STUN[] = "prflx";
const char ICE_CANDIDATE_TYPE_SERVER_STUN[] = "srflx";
// Minimum ufrag length is 4 characters as per RFC5245. We chose 16 because
// some internal systems expect username to be 16 bytes.
const int ICE_UFRAG_LENGTH = 16;
// Minimum password length of 22 characters as per RFC5245. We chose 24 because
// some internal systems expect password to be multiple of 4.
const int ICE_PWD_LENGTH = 24;
const size_t ICE_UFRAG_MIN_LENGTH = 4;
const size_t ICE_PWD_MIN_LENGTH = 22;
const size_t ICE_UFRAG_MAX_LENGTH = 255;
const size_t ICE_PWD_MAX_LENGTH = 256;
// TODO: This is media-specific, so might belong
// somewhere like media/base/constants.h
const int ICE_CANDIDATE_COMPONENT_RTP = 1;
const int ICE_CANDIDATE_COMPONENT_RTCP = 2;
const int ICE_CANDIDATE_COMPONENT_DEFAULT = 1;
const buzz::StaticQName QN_FINGERPRINT = { cricket::NS_EMPTY, "fingerprint" };
const buzz::StaticQName QN_FINGERPRINT_ALGORITHM =
{ cricket::NS_EMPTY, "algorithm" };
const buzz::StaticQName QN_FINGERPRINT_DIGEST = { cricket::NS_EMPTY, "digest" };
const char NS_JINGLE_ICE_UDP[] = "urn:xmpp:jingle:transports:ice-udp:1";
const char ICE_OPTION_GICE[] = "google-ice";
const char NS_GINGLE_P2P[] = "http://www.google.com/transport/p2p";
const buzz::StaticQName QN_GINGLE_P2P_TRANSPORT =
{ NS_GINGLE_P2P, LN_TRANSPORT };
const buzz::StaticQName QN_GINGLE_P2P_CANDIDATE =
{ NS_GINGLE_P2P, LN_CANDIDATE };
const buzz::StaticQName QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME =
{ NS_GINGLE_P2P, "unknown-channel-name" };
const buzz::StaticQName QN_GINGLE_CANDIDATE = { NS_GINGLE, LN_CANDIDATE };
const buzz::StaticQName QN_ADDRESS = { cricket::NS_EMPTY, "address" };
const buzz::StaticQName QN_USERNAME = { cricket::NS_EMPTY, "username" };
const buzz::StaticQName QN_PASSWORD = { cricket::NS_EMPTY, "password" };
const buzz::StaticQName QN_PREFERENCE = { cricket::NS_EMPTY, "preference" };
const char GICE_CHANNEL_NAME_RTP[] = "rtp";
const char GICE_CHANNEL_NAME_RTCP[] = "rtcp";
const char GICE_CHANNEL_NAME_VIDEO_RTP[] = "video_rtp";
const char GICE_CHANNEL_NAME_VIDEO_RTCP[] = "video_rtcp";
const char GICE_CHANNEL_NAME_DATA_RTP[] = "data_rtp";
const char GICE_CHANNEL_NAME_DATA_RTCP[] = "data_rtcp";
// terminate reasons and errors
const char JINGLE_ERROR_BAD_REQUEST[] = "bad-request";
const char JINGLE_ERROR_OUT_OF_ORDER[] = "out-of-order";
const char JINGLE_ERROR_UNKNOWN_SESSION[] = "unknown-session";
// Call terminate reasons from XEP-166
const char STR_TERMINATE_DECLINE[] = "decline";
const char STR_TERMINATE_SUCCESS[] = "success";
const char STR_TERMINATE_ERROR[] = "general-error";
const char STR_TERMINATE_INCOMPATIBLE_PARAMETERS[] = "incompatible-parameters";
// Old terminate reasons used by cricket
const char STR_TERMINATE_CALL_ENDED[] = "call-ended";
const char STR_TERMINATE_RECIPIENT_UNAVAILABLE[] = "recipient-unavailable";
const char STR_TERMINATE_RECIPIENT_BUSY[] = "recipient-busy";
const char STR_TERMINATE_INSUFFICIENT_FUNDS[] = "insufficient-funds";
const char STR_TERMINATE_NUMBER_MALFORMED[] = "number-malformed";
const char STR_TERMINATE_NUMBER_DISALLOWED[] = "number-disallowed";
const char STR_TERMINATE_PROTOCOL_ERROR[] = "protocol-error";
const char STR_TERMINATE_INTERNAL_SERVER_ERROR[] = "internal-server-error";
const char STR_TERMINATE_UNKNOWN_ERROR[] = "unknown-error";
// Draft view and notify messages.
const char STR_JINGLE_DRAFT_CONTENT_NAME_VIDEO[] = "video";
const char STR_JINGLE_DRAFT_CONTENT_NAME_AUDIO[] = "audio";
const buzz::StaticQName QN_NICK = { cricket::NS_EMPTY, "nick" };
const buzz::StaticQName QN_TYPE = { cricket::NS_EMPTY, "type" };
const buzz::StaticQName QN_JINGLE_DRAFT_VIEW = { NS_JINGLE_DRAFT, "view" };
const char STR_JINGLE_DRAFT_VIEW_TYPE_NONE[] = "none";
const char STR_JINGLE_DRAFT_VIEW_TYPE_STATIC[] = "static";
const buzz::StaticQName QN_JINGLE_DRAFT_PARAMS = { NS_JINGLE_DRAFT, "params" };
const buzz::StaticQName QN_JINGLE_DRAFT_STREAMS = { NS_JINGLE_DRAFT, "streams" };
const buzz::StaticQName QN_JINGLE_DRAFT_STREAM = { NS_JINGLE_DRAFT, "stream" };
const buzz::StaticQName QN_DISPLAY = { cricket::NS_EMPTY, "display" };
const buzz::StaticQName QN_CNAME = { cricket::NS_EMPTY, "cname" };
const buzz::StaticQName QN_JINGLE_DRAFT_SSRC = { NS_JINGLE_DRAFT, "ssrc" };
const buzz::StaticQName QN_JINGLE_DRAFT_SSRC_GROUP =
{ NS_JINGLE_DRAFT, "ssrc-group" };
const buzz::StaticQName QN_SEMANTICS = { cricket::NS_EMPTY, "semantics" };
const buzz::StaticQName QN_JINGLE_LEGACY_NOTIFY = { NS_JINGLE_DRAFT, "notify" };
const buzz::StaticQName QN_JINGLE_LEGACY_SOURCE = { NS_JINGLE_DRAFT, "source" };
const char NS_GINGLE_RAW[] = "http://www.google.com/transport/raw-udp";
const buzz::StaticQName QN_GINGLE_RAW_TRANSPORT = { NS_GINGLE_RAW, "transport" };
const buzz::StaticQName QN_GINGLE_RAW_CHANNEL = { NS_GINGLE_RAW, "channel" };
// old stuff
#ifdef FEATURE_ENABLE_VOICEMAIL
const char NS_VOICEMAIL[] = "http://www.google.com/session/voicemail";
const buzz::StaticQName QN_VOICEMAIL_REGARDING = { NS_VOICEMAIL, "regarding" };
#endif
// From RFC 4145, SDP setup attribute values.
const char CONNECTIONROLE_ACTIVE_STR[] = "active";
const char CONNECTIONROLE_PASSIVE_STR[] = "passive";
const char CONNECTIONROLE_ACTPASS_STR[] = "actpass";
const char CONNECTIONROLE_HOLDCONN_STR[] = "holdconn";
} // namespace cricket

View File

@ -1,276 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_CONSTANTS_H_
#define WEBRTC_P2P_BASE_CONSTANTS_H_
#include <string>
#include "webrtc/libjingle/xmllite/qname.h"
// This file contains constants related to signaling that are used in various
// classes in this directory.
namespace cricket {
// NS_ == namespace
// QN_ == buzz::QName (namespace + name)
// LN_ == "local name" == QName::LocalPart()
// these are useful when you need to find a tag
// that has different namespaces (like <description> or <transport>)
extern const char NS_EMPTY[];
extern const char NS_JINGLE[];
extern const char NS_JINGLE_DRAFT[];
extern const char NS_GINGLE[];
enum SignalingProtocol {
PROTOCOL_JINGLE,
PROTOCOL_GINGLE,
PROTOCOL_HYBRID,
};
// actions (aka Gingle <session> or Jingle <jingle>)
extern const buzz::StaticQName QN_ACTION;
extern const char LN_INITIATOR[];
extern const buzz::StaticQName QN_INITIATOR;
extern const buzz::StaticQName QN_CREATOR;
extern const buzz::StaticQName QN_JINGLE;
extern const buzz::StaticQName QN_JINGLE_CONTENT;
extern const buzz::StaticQName QN_JINGLE_CONTENT_NAME;
extern const buzz::StaticQName QN_JINGLE_CONTENT_MEDIA;
extern const buzz::StaticQName QN_JINGLE_REASON;
extern const buzz::StaticQName QN_JINGLE_DRAFT_GROUP;
extern const buzz::StaticQName QN_JINGLE_DRAFT_GROUP_TYPE;
extern const char JINGLE_CONTENT_MEDIA_AUDIO[];
extern const char JINGLE_CONTENT_MEDIA_VIDEO[];
extern const char JINGLE_CONTENT_MEDIA_DATA[];
extern const char JINGLE_ACTION_SESSION_INITIATE[];
extern const char JINGLE_ACTION_SESSION_INFO[];
extern const char JINGLE_ACTION_SESSION_ACCEPT[];
extern const char JINGLE_ACTION_SESSION_TERMINATE[];
extern const char JINGLE_ACTION_TRANSPORT_INFO[];
extern const char JINGLE_ACTION_TRANSPORT_ACCEPT[];
extern const char JINGLE_ACTION_DESCRIPTION_INFO[];
extern const buzz::StaticQName QN_GINGLE_SESSION;
extern const char GINGLE_ACTION_INITIATE[];
extern const char GINGLE_ACTION_INFO[];
extern const char GINGLE_ACTION_ACCEPT[];
extern const char GINGLE_ACTION_REJECT[];
extern const char GINGLE_ACTION_TERMINATE[];
extern const char GINGLE_ACTION_CANDIDATES[];
extern const char GINGLE_ACTION_UPDATE[];
extern const char LN_ERROR[];
extern const buzz::StaticQName QN_GINGLE_REDIRECT;
extern const char STR_REDIRECT_PREFIX[];
// Session Contents (aka Gingle <session><description>
// or Jingle <content><description>)
extern const char LN_DESCRIPTION[];
extern const char LN_PAYLOADTYPE[];
extern const buzz::StaticQName QN_ID;
extern const buzz::StaticQName QN_SID;
extern const buzz::StaticQName QN_NAME;
extern const buzz::StaticQName QN_CLOCKRATE;
extern const buzz::StaticQName QN_BITRATE;
extern const buzz::StaticQName QN_CHANNELS;
extern const buzz::StaticQName QN_PARAMETER;
extern const char LN_NAME[];
extern const char LN_VALUE[];
extern const buzz::StaticQName QN_PAYLOADTYPE_PARAMETER_NAME;
extern const buzz::StaticQName QN_PAYLOADTYPE_PARAMETER_VALUE;
extern const char PAYLOADTYPE_PARAMETER_BITRATE[];
extern const char PAYLOADTYPE_PARAMETER_HEIGHT[];
extern const char PAYLOADTYPE_PARAMETER_WIDTH[];
extern const char PAYLOADTYPE_PARAMETER_FRAMERATE[];
extern const char LN_BANDWIDTH[];
// CN_ == "content name". When we initiate a session, we choose the
// name, and when we receive a Gingle session, we provide default
// names (since Gingle has no content names). But when we receive a
// Jingle call, the content name can be anything, so don't rely on
// these values being the same as the ones received.
extern const char CN_AUDIO[];
extern const char CN_VIDEO[];
extern const char CN_DATA[];
extern const char CN_OTHER[];
// other SDP related strings
// GN stands for group name
extern const char GROUP_TYPE_BUNDLE[];
extern const char NS_JINGLE_RTP[];
extern const buzz::StaticQName QN_JINGLE_RTP_CONTENT;
extern const buzz::StaticQName QN_SSRC;
extern const buzz::StaticQName QN_JINGLE_RTP_PAYLOADTYPE;
extern const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH;
extern const buzz::StaticQName QN_JINGLE_RTCP_MUX;
extern const buzz::StaticQName QN_JINGLE_RTCP_FB;
extern const buzz::StaticQName QN_SUBTYPE;
extern const buzz::StaticQName QN_JINGLE_RTP_HDREXT;
extern const buzz::StaticQName QN_URI;
extern const char NS_JINGLE_DRAFT_SCTP[];
extern const buzz::StaticQName QN_JINGLE_DRAFT_SCTP_CONTENT;
extern const buzz::StaticQName QN_JINGLE_DRAFT_SCTP_STREAM;
extern const char NS_GINGLE_AUDIO[];
extern const buzz::StaticQName QN_GINGLE_AUDIO_CONTENT;
extern const buzz::StaticQName QN_GINGLE_AUDIO_PAYLOADTYPE;
extern const buzz::StaticQName QN_GINGLE_AUDIO_SRCID;
extern const char NS_GINGLE_VIDEO[];
extern const buzz::StaticQName QN_GINGLE_VIDEO_CONTENT;
extern const buzz::StaticQName QN_GINGLE_VIDEO_PAYLOADTYPE;
extern const buzz::StaticQName QN_GINGLE_VIDEO_SRCID;
extern const buzz::StaticQName QN_GINGLE_VIDEO_BANDWIDTH;
// Crypto support.
extern const buzz::StaticQName QN_ENCRYPTION;
extern const buzz::StaticQName QN_ENCRYPTION_REQUIRED;
extern const buzz::StaticQName QN_CRYPTO;
extern const buzz::StaticQName QN_GINGLE_AUDIO_CRYPTO_USAGE;
extern const buzz::StaticQName QN_GINGLE_VIDEO_CRYPTO_USAGE;
extern const buzz::StaticQName QN_CRYPTO_SUITE;
extern const buzz::StaticQName QN_CRYPTO_KEY_PARAMS;
extern const buzz::StaticQName QN_CRYPTO_TAG;
extern const buzz::StaticQName QN_CRYPTO_SESSION_PARAMS;
// Transports and candidates.
extern const char LN_TRANSPORT[];
extern const char LN_CANDIDATE[];
extern const buzz::StaticQName QN_JINGLE_P2P_TRANSPORT;
extern const buzz::StaticQName QN_JINGLE_P2P_CANDIDATE;
extern const buzz::StaticQName QN_UFRAG;
extern const buzz::StaticQName QN_COMPONENT;
extern const buzz::StaticQName QN_PWD;
extern const buzz::StaticQName QN_IP;
extern const buzz::StaticQName QN_PORT;
extern const buzz::StaticQName QN_NETWORK;
extern const buzz::StaticQName QN_GENERATION;
extern const buzz::StaticQName QN_PRIORITY;
extern const buzz::StaticQName QN_PROTOCOL;
extern const char ICE_CANDIDATE_TYPE_PEER_STUN[];
extern const char ICE_CANDIDATE_TYPE_SERVER_STUN[];
extern const int ICE_UFRAG_LENGTH;
extern const int ICE_PWD_LENGTH;
extern const size_t ICE_UFRAG_MIN_LENGTH;
extern const size_t ICE_PWD_MIN_LENGTH;
extern const size_t ICE_UFRAG_MAX_LENGTH;
extern const size_t ICE_PWD_MAX_LENGTH;
extern const int ICE_CANDIDATE_COMPONENT_RTP;
extern const int ICE_CANDIDATE_COMPONENT_RTCP;
extern const int ICE_CANDIDATE_COMPONENT_DEFAULT;
extern const buzz::StaticQName QN_FINGERPRINT;
extern const buzz::StaticQName QN_FINGERPRINT_ALGORITHM;
extern const buzz::StaticQName QN_FINGERPRINT_DIGEST;
extern const char NS_JINGLE_ICE_UDP[];
extern const char ICE_OPTION_GICE[];
extern const char NS_GINGLE_P2P[];
extern const buzz::StaticQName QN_GINGLE_P2P_TRANSPORT;
extern const buzz::StaticQName QN_GINGLE_P2P_CANDIDATE;
extern const buzz::StaticQName QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME;
extern const buzz::StaticQName QN_GINGLE_CANDIDATE;
extern const buzz::StaticQName QN_ADDRESS;
extern const buzz::StaticQName QN_USERNAME;
extern const buzz::StaticQName QN_PASSWORD;
extern const buzz::StaticQName QN_PREFERENCE;
extern const char GINGLE_CANDIDATE_TYPE_STUN[];
extern const char GICE_CHANNEL_NAME_RTP[];
extern const char GICE_CHANNEL_NAME_RTCP[];
extern const char GICE_CHANNEL_NAME_VIDEO_RTP[];
extern const char GICE_CHANNEL_NAME_VIDEO_RTCP[];
extern const char GICE_CHANNEL_NAME_DATA_RTP[];
extern const char GICE_CHANNEL_NAME_DATA_RTCP[];
extern const char NS_GINGLE_RAW[];
extern const buzz::StaticQName QN_GINGLE_RAW_TRANSPORT;
extern const buzz::StaticQName QN_GINGLE_RAW_CHANNEL;
// terminate reasons and errors: see http://xmpp.org/extensions/xep-0166.html
extern const char JINGLE_ERROR_BAD_REQUEST[]; // like parse error
// got transport-info before session-initiate, for example
extern const char JINGLE_ERROR_OUT_OF_ORDER[];
extern const char JINGLE_ERROR_UNKNOWN_SESSION[];
// Call terminate reasons from XEP-166
extern const char STR_TERMINATE_DECLINE[]; // polite reject
extern const char STR_TERMINATE_SUCCESS[]; // polite hangup
extern const char STR_TERMINATE_ERROR[]; // something bad happened
extern const char STR_TERMINATE_INCOMPATIBLE_PARAMETERS[]; // no codecs?
// Old terminate reasons used by cricket
extern const char STR_TERMINATE_CALL_ENDED[];
extern const char STR_TERMINATE_RECIPIENT_UNAVAILABLE[];
extern const char STR_TERMINATE_RECIPIENT_BUSY[];
extern const char STR_TERMINATE_INSUFFICIENT_FUNDS[];
extern const char STR_TERMINATE_NUMBER_MALFORMED[];
extern const char STR_TERMINATE_NUMBER_DISALLOWED[];
extern const char STR_TERMINATE_PROTOCOL_ERROR[];
extern const char STR_TERMINATE_INTERNAL_SERVER_ERROR[];
extern const char STR_TERMINATE_UNKNOWN_ERROR[];
// Draft view and notify messages.
extern const char STR_JINGLE_DRAFT_CONTENT_NAME_VIDEO[];
extern const char STR_JINGLE_DRAFT_CONTENT_NAME_AUDIO[];
extern const buzz::StaticQName QN_NICK;
extern const buzz::StaticQName QN_TYPE;
extern const buzz::StaticQName QN_JINGLE_DRAFT_VIEW;
extern const char STR_JINGLE_DRAFT_VIEW_TYPE_NONE[];
extern const char STR_JINGLE_DRAFT_VIEW_TYPE_STATIC[];
extern const buzz::StaticQName QN_JINGLE_DRAFT_PARAMS;
extern const buzz::StaticQName QN_WIDTH;
extern const buzz::StaticQName QN_HEIGHT;
extern const buzz::StaticQName QN_FRAMERATE;
extern const buzz::StaticQName QN_JINGLE_DRAFT_STREAM;
extern const buzz::StaticQName QN_JINGLE_DRAFT_STREAMS;
extern const buzz::StaticQName QN_DISPLAY;
extern const buzz::StaticQName QN_CNAME;
extern const buzz::StaticQName QN_JINGLE_DRAFT_SSRC;
extern const buzz::StaticQName QN_JINGLE_DRAFT_SSRC_GROUP;
extern const buzz::StaticQName QN_SEMANTICS;
extern const buzz::StaticQName QN_JINGLE_LEGACY_NOTIFY;
extern const buzz::StaticQName QN_JINGLE_LEGACY_SOURCE;
// old stuff
#ifdef FEATURE_ENABLE_VOICEMAIL
extern const char NS_VOICEMAIL[];
extern const buzz::StaticQName QN_VOICEMAIL_REGARDING;
#endif
// RFC 4145, SDP setup attribute values.
extern const char CONNECTIONROLE_ACTIVE_STR[];
extern const char CONNECTIONROLE_PASSIVE_STR[];
extern const char CONNECTIONROLE_ACTPASS_STR[];
extern const char CONNECTIONROLE_HOLDCONN_STR[];
} // namespace cricket
#endif // WEBRTC_P2P_BASE_CONSTANTS_H_

View File

@ -1,257 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#ifndef WEBRTC_P2P_BASE_DTLSTRANSPORT_H_
#define WEBRTC_P2P_BASE_DTLSTRANSPORT_H_
#include "webrtc/p2p/base/dtlstransportchannel.h"
#include "webrtc/p2p/base/transport.h"
namespace rtc {
class SSLIdentity;
}
namespace cricket {
class PortAllocator;
// Base should be a descendant of cricket::Transport
template<class Base>
class DtlsTransport : public Base {
public:
DtlsTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
PortAllocator* allocator,
rtc::SSLIdentity* identity)
: Base(signaling_thread, worker_thread, content_name, allocator),
identity_(identity),
secure_role_(rtc::SSL_CLIENT) {
}
~DtlsTransport() {
Base::DestroyAllChannels();
}
virtual void SetIdentity_w(rtc::SSLIdentity* identity) {
identity_ = identity;
}
virtual bool GetIdentity_w(rtc::SSLIdentity** identity) {
if (!identity_)
return false;
*identity = identity_->GetReference();
return true;
}
virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
std::string* error_desc) {
rtc::SSLFingerprint* local_fp =
Base::local_description()->identity_fingerprint.get();
if (local_fp) {
// Sanity check local fingerprint.
if (identity_) {
rtc::scoped_ptr<rtc::SSLFingerprint> local_fp_tmp(
rtc::SSLFingerprint::Create(local_fp->algorithm,
identity_));
ASSERT(local_fp_tmp.get() != NULL);
if (!(*local_fp_tmp == *local_fp)) {
std::ostringstream desc;
desc << "Local fingerprint does not match identity. Expected: ";
desc << local_fp_tmp->ToString();
desc << " Got: " << local_fp->ToString();
return BadTransportDescription(desc.str(), error_desc);
}
} else {
return BadTransportDescription(
"Local fingerprint provided but no identity available.",
error_desc);
}
} else {
identity_ = NULL;
}
if (!channel->SetLocalIdentity(identity_)) {
return BadTransportDescription("Failed to set local identity.",
error_desc);
}
// Apply the description in the base class.
return Base::ApplyLocalTransportDescription_w(channel, error_desc);
}
virtual bool NegotiateTransportDescription_w(ContentAction local_role,
std::string* error_desc) {
if (!Base::local_description() || !Base::remote_description()) {
const std::string msg = "Local and Remote description must be set before "
"transport descriptions are negotiated";
return BadTransportDescription(msg, error_desc);
}
rtc::SSLFingerprint* local_fp =
Base::local_description()->identity_fingerprint.get();
rtc::SSLFingerprint* remote_fp =
Base::remote_description()->identity_fingerprint.get();
if (remote_fp && local_fp) {
remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
// From RFC 4145, section-4.1, The following are the values that the
// 'setup' attribute can take in an offer/answer exchange:
// Offer Answer
// ________________
// active passive / holdconn
// passive active / holdconn
// actpass active / passive / holdconn
// holdconn holdconn
//
// Set the role that is most conformant with RFC 5763, Section 5, bullet 1
// The endpoint MUST use the setup attribute defined in [RFC4145].
// The endpoint that is the offerer MUST use the setup attribute
// value of setup:actpass and be prepared to receive a client_hello
// before it receives the answer. The answerer MUST use either a
// setup attribute value of setup:active or setup:passive. Note that
// if the answerer uses setup:passive, then the DTLS handshake will
// not begin until the answerer is received, which adds additional
// latency. setup:active allows the answer and the DTLS handshake to
// occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
// party is active MUST initiate a DTLS handshake by sending a
// ClientHello over each flow (host/port quartet).
// IOW - actpass and passive modes should be treated as server and
// active as client.
ConnectionRole local_connection_role =
Base::local_description()->connection_role;
ConnectionRole remote_connection_role =
Base::remote_description()->connection_role;
bool is_remote_server = false;
if (local_role == CA_OFFER) {
if (local_connection_role != CONNECTIONROLE_ACTPASS) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.",
error_desc);
}
if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
remote_connection_role == CONNECTIONROLE_PASSIVE ||
remote_connection_role == CONNECTIONROLE_NONE) {
is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If remote is NONE or ACTIVE it will act as client.
} else {
if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
remote_connection_role != CONNECTIONROLE_NONE) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.",
error_desc);
}
if (local_connection_role == CONNECTIONROLE_ACTIVE ||
local_connection_role == CONNECTIONROLE_PASSIVE) {
is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If local is passive, local will act as server.
}
secure_role_ = is_remote_server ? rtc::SSL_CLIENT :
rtc::SSL_SERVER;
} else if (local_fp && (local_role == CA_ANSWER)) {
return BadTransportDescription(
"Local fingerprint supplied when caller didn't offer DTLS.",
error_desc);
} else {
// We are not doing DTLS
remote_fingerprint_.reset(new rtc::SSLFingerprint(
"", NULL, 0));
}
// Now run the negotiation for the base class.
return Base::NegotiateTransportDescription_w(local_role, error_desc);
}
virtual DtlsTransportChannelWrapper* CreateTransportChannel(int component) {
return new DtlsTransportChannelWrapper(
this, Base::CreateTransportChannel(component));
}
virtual void DestroyTransportChannel(TransportChannelImpl* channel) {
// Kind of ugly, but this lets us do the exact inverse of the create.
DtlsTransportChannelWrapper* dtls_channel =
static_cast<DtlsTransportChannelWrapper*>(channel);
TransportChannelImpl* base_channel = dtls_channel->channel();
delete dtls_channel;
Base::DestroyTransportChannel(base_channel);
}
virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const {
ASSERT(ssl_role != NULL);
*ssl_role = secure_role_;
return true;
}
private:
virtual bool ApplyNegotiatedTransportDescription_w(
TransportChannelImpl* channel,
std::string* error_desc) {
// Set ssl role. Role must be set before fingerprint is applied, which
// initiates DTLS setup.
if (!channel->SetSslRole(secure_role_)) {
return BadTransportDescription("Failed to set ssl role for the channel.",
error_desc);
}
// Apply remote fingerprint.
if (!channel->SetRemoteFingerprint(
remote_fingerprint_->algorithm,
reinterpret_cast<const uint8 *>(remote_fingerprint_->
digest.data()),
remote_fingerprint_->digest.length())) {
return BadTransportDescription("Failed to apply remote fingerprint.",
error_desc);
}
return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc);
}
rtc::SSLIdentity* identity_;
rtc::SSLRole secure_role_;
rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_DTLSTRANSPORT_H_

View File

@ -1,641 +0,0 @@
/*
* libjingle
* Copyright 2011, Google Inc.
* Copyright 2011, RTFM, 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 "webrtc/p2p/base/dtlstransportchannel.h"
#include "webrtc/p2p/base/common.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/dscp.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/sslstreamadapter.h"
#include "webrtc/base/stream.h"
#include "webrtc/base/thread.h"
namespace cricket {
// We don't pull the RTP constants from rtputils.h, to avoid a layer violation.
static const size_t kDtlsRecordHeaderLen = 13;
static const size_t kMaxDtlsPacketLen = 2048;
static const size_t kMinRtpPacketLen = 12;
static bool IsDtlsPacket(const char* data, size_t len) {
const uint8* u = reinterpret_cast<const uint8*>(data);
return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64));
}
static bool IsRtpPacket(const char* data, size_t len) {
const uint8* u = reinterpret_cast<const uint8*>(data);
return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80);
}
rtc::StreamResult StreamInterfaceChannel::Read(void* buffer,
size_t buffer_len,
size_t* read,
int* error) {
if (state_ == rtc::SS_CLOSED)
return rtc::SR_EOS;
if (state_ == rtc::SS_OPENING)
return rtc::SR_BLOCK;
return fifo_.Read(buffer, buffer_len, read, error);
}
rtc::StreamResult StreamInterfaceChannel::Write(const void* data,
size_t data_len,
size_t* written,
int* error) {
// Always succeeds, since this is an unreliable transport anyway.
// TODO: Should this block if channel_'s temporarily unwritable?
rtc::PacketOptions packet_options;
channel_->SendPacket(static_cast<const char*>(data), data_len,
packet_options);
if (written) {
*written = data_len;
}
return rtc::SR_SUCCESS;
}
bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) {
// We force a read event here to ensure that we don't overflow our FIFO.
// Under high packet rate this can occur if we wait for the FIFO to post its
// own SE_READ.
bool ret = (fifo_.WriteAll(data, size, NULL, NULL) == rtc::SR_SUCCESS);
if (ret) {
SignalEvent(this, rtc::SE_READ, 0);
}
return ret;
}
void StreamInterfaceChannel::OnEvent(rtc::StreamInterface* stream,
int sig, int err) {
SignalEvent(this, sig, err);
}
DtlsTransportChannelWrapper::DtlsTransportChannelWrapper(
Transport* transport,
TransportChannelImpl* channel)
: TransportChannelImpl(channel->content_name(), channel->component()),
transport_(transport),
worker_thread_(rtc::Thread::Current()),
channel_(channel),
downward_(NULL),
dtls_state_(STATE_NONE),
local_identity_(NULL),
ssl_role_(rtc::SSL_CLIENT) {
channel_->SignalReadableState.connect(this,
&DtlsTransportChannelWrapper::OnReadableState);
channel_->SignalWritableState.connect(this,
&DtlsTransportChannelWrapper::OnWritableState);
channel_->SignalReadPacket.connect(this,
&DtlsTransportChannelWrapper::OnReadPacket);
channel_->SignalReadyToSend.connect(this,
&DtlsTransportChannelWrapper::OnReadyToSend);
channel_->SignalRequestSignaling.connect(this,
&DtlsTransportChannelWrapper::OnRequestSignaling);
channel_->SignalCandidateReady.connect(this,
&DtlsTransportChannelWrapper::OnCandidateReady);
channel_->SignalCandidatesAllocationDone.connect(this,
&DtlsTransportChannelWrapper::OnCandidatesAllocationDone);
channel_->SignalRoleConflict.connect(this,
&DtlsTransportChannelWrapper::OnRoleConflict);
channel_->SignalRouteChange.connect(this,
&DtlsTransportChannelWrapper::OnRouteChange);
channel_->SignalConnectionRemoved.connect(this,
&DtlsTransportChannelWrapper::OnConnectionRemoved);
}
DtlsTransportChannelWrapper::~DtlsTransportChannelWrapper() {
}
void DtlsTransportChannelWrapper::Connect() {
// We should only get a single call to Connect.
ASSERT(dtls_state_ == STATE_NONE ||
dtls_state_ == STATE_OFFERED ||
dtls_state_ == STATE_ACCEPTED);
channel_->Connect();
}
void DtlsTransportChannelWrapper::Reset() {
channel_->Reset();
set_writable(false);
set_readable(false);
// Re-call SetupDtls()
if (!SetupDtls()) {
LOG_J(LS_ERROR, this) << "Error re-initializing DTLS";
dtls_state_ = STATE_CLOSED;
return;
}
dtls_state_ = STATE_ACCEPTED;
}
bool DtlsTransportChannelWrapper::SetLocalIdentity(
rtc::SSLIdentity* identity) {
if (dtls_state_ != STATE_NONE) {
if (identity == local_identity_) {
// This may happen during renegotiation.
LOG_J(LS_INFO, this) << "Ignoring identical DTLS identity";
return true;
} else {
LOG_J(LS_ERROR, this) << "Can't change DTLS local identity in this state";
return false;
}
}
if (identity) {
local_identity_ = identity;
dtls_state_ = STATE_OFFERED;
} else {
LOG_J(LS_INFO, this) << "NULL DTLS identity supplied. Not doing DTLS";
}
return true;
}
bool DtlsTransportChannelWrapper::GetLocalIdentity(
rtc::SSLIdentity** identity) const {
if (!local_identity_)
return false;
*identity = local_identity_->GetReference();
return true;
}
bool DtlsTransportChannelWrapper::SetSslRole(rtc::SSLRole role) {
if (dtls_state_ == STATE_OPEN) {
if (ssl_role_ != role) {
LOG(LS_ERROR) << "SSL Role can't be reversed after the session is setup.";
return false;
}
return true;
}
ssl_role_ = role;
return true;
}
bool DtlsTransportChannelWrapper::GetSslRole(rtc::SSLRole* role) const {
*role = ssl_role_;
return true;
}
bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
const std::string& digest_alg,
const uint8* digest,
size_t digest_len) {
rtc::Buffer remote_fingerprint_value(digest, digest_len);
if (dtls_state_ != STATE_NONE &&
remote_fingerprint_value_ == remote_fingerprint_value &&
!digest_alg.empty()) {
// This may happen during renegotiation.
LOG_J(LS_INFO, this) << "Ignoring identical remote DTLS fingerprint";
return true;
}
// Allow SetRemoteFingerprint with a NULL digest even if SetLocalIdentity
// hasn't been called.
if (dtls_state_ > STATE_OFFERED ||
(dtls_state_ == STATE_NONE && !digest_alg.empty())) {
LOG_J(LS_ERROR, this) << "Can't set DTLS remote settings in this state.";
return false;
}
if (digest_alg.empty()) {
LOG_J(LS_INFO, this) << "Other side didn't support DTLS.";
dtls_state_ = STATE_NONE;
return true;
}
// At this point we know we are doing DTLS
remote_fingerprint_value.TransferTo(&remote_fingerprint_value_);
remote_fingerprint_algorithm_ = digest_alg;
if (!SetupDtls()) {
dtls_state_ = STATE_CLOSED;
return false;
}
dtls_state_ = STATE_ACCEPTED;
return true;
}
bool DtlsTransportChannelWrapper::GetRemoteCertificate(
rtc::SSLCertificate** cert) const {
if (!dtls_)
return false;
return dtls_->GetPeerCertificate(cert);
}
bool DtlsTransportChannelWrapper::SetupDtls() {
StreamInterfaceChannel* downward =
new StreamInterfaceChannel(worker_thread_, channel_);
dtls_.reset(rtc::SSLStreamAdapter::Create(downward));
if (!dtls_) {
LOG_J(LS_ERROR, this) << "Failed to create DTLS adapter.";
delete downward;
return false;
}
downward_ = downward;
dtls_->SetIdentity(local_identity_->GetReference());
dtls_->SetMode(rtc::SSL_MODE_DTLS);
dtls_->SetServerRole(ssl_role_);
dtls_->SignalEvent.connect(this, &DtlsTransportChannelWrapper::OnDtlsEvent);
if (!dtls_->SetPeerCertificateDigest(
remote_fingerprint_algorithm_,
reinterpret_cast<unsigned char *>(remote_fingerprint_value_.data()),
remote_fingerprint_value_.length())) {
LOG_J(LS_ERROR, this) << "Couldn't set DTLS certificate digest.";
return false;
}
// Set up DTLS-SRTP, if it's been enabled.
if (!srtp_ciphers_.empty()) {
if (!dtls_->SetDtlsSrtpCiphers(srtp_ciphers_)) {
LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers.";
return false;
}
} else {
LOG_J(LS_INFO, this) << "Not using DTLS.";
}
LOG_J(LS_INFO, this) << "DTLS setup complete.";
return true;
}
bool DtlsTransportChannelWrapper::SetSrtpCiphers(
const std::vector<std::string>& ciphers) {
if (srtp_ciphers_ == ciphers)
return true;
if (dtls_state_ == STATE_STARTED) {
LOG(LS_WARNING) << "Ignoring new SRTP ciphers while DTLS is negotiating";
return true;
}
if (dtls_state_ == STATE_OPEN) {
// We don't support DTLS renegotiation currently. If new set of srtp ciphers
// are different than what's being used currently, we will not use it.
// So for now, let's be happy (or sad) with a warning message.
std::string current_srtp_cipher;
if (!dtls_->GetDtlsSrtpCipher(&current_srtp_cipher)) {
LOG(LS_ERROR) << "Failed to get the current SRTP cipher for DTLS channel";
return false;
}
const std::vector<std::string>::const_iterator iter =
std::find(ciphers.begin(), ciphers.end(), current_srtp_cipher);
if (iter == ciphers.end()) {
std::string requested_str;
for (size_t i = 0; i < ciphers.size(); ++i) {
requested_str.append(" ");
requested_str.append(ciphers[i]);
requested_str.append(" ");
}
LOG(LS_WARNING) << "Ignoring new set of SRTP ciphers, as DTLS "
<< "renegotiation is not supported currently "
<< "current cipher = " << current_srtp_cipher << " and "
<< "requested = " << "[" << requested_str << "]";
}
return true;
}
if (dtls_state_ != STATE_NONE &&
dtls_state_ != STATE_OFFERED &&
dtls_state_ != STATE_ACCEPTED) {
ASSERT(false);
return false;
}
srtp_ciphers_ = ciphers;
return true;
}
bool DtlsTransportChannelWrapper::GetSrtpCipher(std::string* cipher) {
if (dtls_state_ != STATE_OPEN) {
return false;
}
return dtls_->GetDtlsSrtpCipher(cipher);
}
// Called from upper layers to send a media packet.
int DtlsTransportChannelWrapper::SendPacket(
const char* data, size_t size,
const rtc::PacketOptions& options, int flags) {
int result = -1;
switch (dtls_state_) {
case STATE_OFFERED:
// We don't know if we are doing DTLS yet, so we can't send a packet.
// TODO(ekr@rtfm.com): assert here?
result = -1;
break;
case STATE_STARTED:
case STATE_ACCEPTED:
// Can't send data until the connection is active
result = -1;
break;
case STATE_OPEN:
if (flags & PF_SRTP_BYPASS) {
ASSERT(!srtp_ciphers_.empty());
if (!IsRtpPacket(data, size)) {
result = -1;
break;
}
result = channel_->SendPacket(data, size, options);
} else {
result = (dtls_->WriteAll(data, size, NULL, NULL) ==
rtc::SR_SUCCESS) ? static_cast<int>(size) : -1;
}
break;
// Not doing DTLS.
case STATE_NONE:
result = channel_->SendPacket(data, size, options);
break;
case STATE_CLOSED: // Can't send anything when we're closed.
return -1;
}
return result;
}
// The state transition logic here is as follows:
// (1) If we're not doing DTLS-SRTP, then the state is just the
// state of the underlying impl()
// (2) If we're doing DTLS-SRTP:
// - Prior to the DTLS handshake, the state is neither readable or
// writable
// - When the impl goes writable for the first time we
// start the DTLS handshake
// - Once the DTLS handshake completes, the state is that of the
// impl again
void DtlsTransportChannelWrapper::OnReadableState(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
LOG_J(LS_VERBOSE, this)
<< "DTLSTransportChannelWrapper: channel readable state changed.";
if (dtls_state_ == STATE_NONE || dtls_state_ == STATE_OPEN) {
set_readable(channel_->readable());
// Note: SignalReadableState fired by set_readable.
}
}
void DtlsTransportChannelWrapper::OnWritableState(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
LOG_J(LS_VERBOSE, this)
<< "DTLSTransportChannelWrapper: channel writable state changed.";
switch (dtls_state_) {
case STATE_NONE:
case STATE_OPEN:
set_writable(channel_->writable());
// Note: SignalWritableState fired by set_writable.
break;
case STATE_OFFERED:
// Do nothing
break;
case STATE_ACCEPTED:
if (!MaybeStartDtls()) {
// This should never happen:
// Because we are operating in a nonblocking mode and all
// incoming packets come in via OnReadPacket(), which rejects
// packets in this state, the incoming queue must be empty. We
// ignore write errors, thus any errors must be because of
// configuration and therefore are our fault.
// Note that in non-debug configurations, failure in
// MaybeStartDtls() changes the state to STATE_CLOSED.
ASSERT(false);
}
break;
case STATE_STARTED:
// Do nothing
break;
case STATE_CLOSED:
// Should not happen. Do nothing
break;
}
}
void DtlsTransportChannelWrapper::OnReadPacket(
TransportChannel* channel, const char* data, size_t size,
const rtc::PacketTime& packet_time, int flags) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == channel_);
ASSERT(flags == 0);
switch (dtls_state_) {
case STATE_NONE:
// We are not doing DTLS
SignalReadPacket(this, data, size, packet_time, 0);
break;
case STATE_OFFERED:
// Currently drop the packet, but we might in future
// decide to take this as evidence that the other
// side is ready to do DTLS and start the handshake
// on our end
LOG_J(LS_WARNING, this) << "Received packet before we know if we are "
<< "doing DTLS or not; dropping.";
break;
case STATE_ACCEPTED:
// Drop packets received before DTLS has actually started
LOG_J(LS_INFO, this) << "Dropping packet received before DTLS started.";
break;
case STATE_STARTED:
case STATE_OPEN:
// We should only get DTLS or SRTP packets; STUN's already been demuxed.
// Is this potentially a DTLS packet?
if (IsDtlsPacket(data, size)) {
if (!HandleDtlsPacket(data, size)) {
LOG_J(LS_ERROR, this) << "Failed to handle DTLS packet.";
return;
}
} else {
// Not a DTLS packet; our handshake should be complete by now.
if (dtls_state_ != STATE_OPEN) {
LOG_J(LS_ERROR, this) << "Received non-DTLS packet before DTLS "
<< "complete.";
return;
}
// And it had better be a SRTP packet.
if (!IsRtpPacket(data, size)) {
LOG_J(LS_ERROR, this) << "Received unexpected non-DTLS packet.";
return;
}
// Sanity check.
ASSERT(!srtp_ciphers_.empty());
// Signal this upwards as a bypass packet.
SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS);
}
break;
case STATE_CLOSED:
// This shouldn't be happening. Drop the packet
break;
}
}
void DtlsTransportChannelWrapper::OnReadyToSend(TransportChannel* channel) {
if (writable()) {
SignalReadyToSend(this);
}
}
void DtlsTransportChannelWrapper::OnDtlsEvent(rtc::StreamInterface* dtls,
int sig, int err) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(dtls == dtls_.get());
if (sig & rtc::SE_OPEN) {
// This is the first time.
LOG_J(LS_INFO, this) << "DTLS handshake complete.";
if (dtls_->GetState() == rtc::SS_OPEN) {
// The check for OPEN shouldn't be necessary but let's make
// sure we don't accidentally frob the state if it's closed.
dtls_state_ = STATE_OPEN;
set_readable(true);
set_writable(true);
}
}
if (sig & rtc::SE_READ) {
char buf[kMaxDtlsPacketLen];
size_t read;
if (dtls_->Read(buf, sizeof(buf), &read, NULL) == rtc::SR_SUCCESS) {
SignalReadPacket(this, buf, read, rtc::CreatePacketTime(0), 0);
}
}
if (sig & rtc::SE_CLOSE) {
ASSERT(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself.
if (!err) {
LOG_J(LS_INFO, this) << "DTLS channel closed";
} else {
LOG_J(LS_INFO, this) << "DTLS channel error, code=" << err;
}
set_readable(false);
set_writable(false);
dtls_state_ = STATE_CLOSED;
}
}
bool DtlsTransportChannelWrapper::MaybeStartDtls() {
if (channel_->writable()) {
if (dtls_->StartSSLWithPeer()) {
LOG_J(LS_ERROR, this) << "Couldn't start DTLS handshake";
dtls_state_ = STATE_CLOSED;
return false;
}
LOG_J(LS_INFO, this)
<< "DtlsTransportChannelWrapper: Started DTLS handshake";
dtls_state_ = STATE_STARTED;
}
return true;
}
// Called from OnReadPacket when a DTLS packet is received.
bool DtlsTransportChannelWrapper::HandleDtlsPacket(const char* data,
size_t size) {
// Sanity check we're not passing junk that
// just looks like DTLS.
const uint8* tmp_data = reinterpret_cast<const uint8* >(data);
size_t tmp_size = size;
while (tmp_size > 0) {
if (tmp_size < kDtlsRecordHeaderLen)
return false; // Too short for the header
size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]);
if ((record_len + kDtlsRecordHeaderLen) > tmp_size)
return false; // Body too short
tmp_data += record_len + kDtlsRecordHeaderLen;
tmp_size -= record_len + kDtlsRecordHeaderLen;
}
// Looks good. Pass to the SIC which ends up being passed to
// the DTLS stack.
return downward_->OnPacketReceived(data, size);
}
void DtlsTransportChannelWrapper::OnRequestSignaling(
TransportChannelImpl* channel) {
ASSERT(channel == channel_);
SignalRequestSignaling(this);
}
void DtlsTransportChannelWrapper::OnCandidateReady(
TransportChannelImpl* channel, const Candidate& c) {
ASSERT(channel == channel_);
SignalCandidateReady(this, c);
}
void DtlsTransportChannelWrapper::OnCandidatesAllocationDone(
TransportChannelImpl* channel) {
ASSERT(channel == channel_);
SignalCandidatesAllocationDone(this);
}
void DtlsTransportChannelWrapper::OnRoleConflict(
TransportChannelImpl* channel) {
ASSERT(channel == channel_);
SignalRoleConflict(this);
}
void DtlsTransportChannelWrapper::OnRouteChange(
TransportChannel* channel, const Candidate& candidate) {
ASSERT(channel == channel_);
SignalRouteChange(this, candidate);
}
void DtlsTransportChannelWrapper::OnConnectionRemoved(
TransportChannelImpl* channel) {
ASSERT(channel == channel_);
SignalConnectionRemoved(this);
}
} // namespace cricket

View File

@ -1,264 +0,0 @@
/*
* libjingle
* Copyright 2011, Google Inc.
* Copyright 2011, RTFM, 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.
*/
#ifndef WEBRTC_P2P_BASE_DTLSTRANSPORTCHANNEL_H_
#define WEBRTC_P2P_BASE_DTLSTRANSPORTCHANNEL_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/transportchannelimpl.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/sslstreamadapter.h"
#include "webrtc/base/stream.h"
namespace cricket {
// A bridge between a packet-oriented/channel-type interface on
// the bottom and a StreamInterface on the top.
class StreamInterfaceChannel : public rtc::StreamInterface,
public sigslot::has_slots<> {
public:
StreamInterfaceChannel(rtc::Thread* owner, TransportChannel* channel)
: channel_(channel),
state_(rtc::SS_OPEN),
fifo_(kFifoSize, owner) {
fifo_.SignalEvent.connect(this, &StreamInterfaceChannel::OnEvent);
}
// Push in a packet; this gets pulled out from Read().
bool OnPacketReceived(const char* data, size_t size);
// Implementations of StreamInterface
virtual rtc::StreamState GetState() const { return state_; }
virtual void Close() { state_ = rtc::SS_CLOSED; }
virtual rtc::StreamResult Read(void* buffer, size_t buffer_len,
size_t* read, int* error);
virtual rtc::StreamResult Write(const void* data, size_t data_len,
size_t* written, int* error);
private:
static const size_t kFifoSize = 8192;
// Forward events
virtual void OnEvent(rtc::StreamInterface* stream, int sig, int err);
TransportChannel* channel_; // owned by DtlsTransportChannelWrapper
rtc::StreamState state_;
rtc::FifoBuffer fifo_;
DISALLOW_COPY_AND_ASSIGN(StreamInterfaceChannel);
};
// This class provides a DTLS SSLStreamAdapter inside a TransportChannel-style
// packet-based interface, wrapping an existing TransportChannel instance
// (e.g a P2PTransportChannel)
// Here's the way this works:
//
// DtlsTransportChannelWrapper {
// SSLStreamAdapter* dtls_ {
// StreamInterfaceChannel downward_ {
// TransportChannelImpl* channel_;
// }
// }
// }
//
// - Data which comes into DtlsTransportChannelWrapper from the underlying
// channel_ via OnReadPacket() is checked for whether it is DTLS
// or not, and if it is, is passed to DtlsTransportChannelWrapper::
// HandleDtlsPacket, which pushes it into to downward_.
// dtls_ is listening for events on downward_, so it immediately calls
// downward_->Read().
//
// - Data written to DtlsTransportChannelWrapper is passed either to
// downward_ or directly to channel_, depending on whether DTLS is
// negotiated and whether the flags include PF_SRTP_BYPASS
//
// - The SSLStreamAdapter writes to downward_->Write()
// which translates it into packet writes on channel_.
class DtlsTransportChannelWrapper : public TransportChannelImpl {
public:
enum State {
STATE_NONE, // No state or rejected.
STATE_OFFERED, // Our identity has been set.
STATE_ACCEPTED, // The other side sent a fingerprint.
STATE_STARTED, // We are negotiating.
STATE_OPEN, // Negotiation complete.
STATE_CLOSED // Connection closed.
};
// The parameters here are:
// transport -- the DtlsTransport that created us
// channel -- the TransportChannel we are wrapping
DtlsTransportChannelWrapper(Transport* transport,
TransportChannelImpl* channel);
virtual ~DtlsTransportChannelWrapper();
virtual void SetIceRole(IceRole role) {
channel_->SetIceRole(role);
}
virtual IceRole GetIceRole() const {
return channel_->GetIceRole();
}
virtual size_t GetConnectionCount() const {
return channel_->GetConnectionCount();
}
virtual bool SetLocalIdentity(rtc::SSLIdentity *identity);
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const;
virtual bool SetRemoteFingerprint(const std::string& digest_alg,
const uint8* digest,
size_t digest_len);
virtual bool IsDtlsActive() const { return dtls_state_ != STATE_NONE; }
// Called to send a packet (via DTLS, if turned on).
virtual int SendPacket(const char* data, size_t size,
const rtc::PacketOptions& options,
int flags);
// TransportChannel calls that we forward to the wrapped transport.
virtual int SetOption(rtc::Socket::Option opt, int value) {
return channel_->SetOption(opt, value);
}
virtual int GetError() {
return channel_->GetError();
}
virtual bool GetStats(ConnectionInfos* infos) {
return channel_->GetStats(infos);
}
virtual const std::string SessionId() const {
return channel_->SessionId();
}
// Set up the ciphers to use for DTLS-SRTP. If this method is not called
// before DTLS starts, or |ciphers| is empty, SRTP keys won't be negotiated.
// This method should be called before SetupDtls.
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers);
// Find out which DTLS-SRTP cipher was negotiated
virtual bool GetSrtpCipher(std::string* cipher);
virtual bool GetSslRole(rtc::SSLRole* role) const;
virtual bool SetSslRole(rtc::SSLRole role);
// Once DTLS has been established, this method retrieves the certificate in
// use by the remote peer, for use in external identity verification.
virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const;
// Once DTLS has established (i.e., this channel is writable), this method
// extracts the keys negotiated during the DTLS handshake, for use in external
// encryption. DTLS-SRTP uses this to extract the needed SRTP keys.
// See the SSLStreamAdapter documentation for info on the specific parameters.
virtual bool ExportKeyingMaterial(const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len) {
return (dtls_.get()) ? dtls_->ExportKeyingMaterial(label, context,
context_len,
use_context,
result, result_len)
: false;
}
// TransportChannelImpl calls.
virtual Transport* GetTransport() {
return transport_;
}
virtual void SetIceTiebreaker(uint64 tiebreaker) {
channel_->SetIceTiebreaker(tiebreaker);
}
virtual bool GetIceProtocolType(IceProtocolType* type) const {
return channel_->GetIceProtocolType(type);
}
virtual void SetIceProtocolType(IceProtocolType type) {
channel_->SetIceProtocolType(type);
}
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
channel_->SetIceCredentials(ice_ufrag, ice_pwd);
}
virtual void SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
channel_->SetRemoteIceCredentials(ice_ufrag, ice_pwd);
}
virtual void SetRemoteIceMode(IceMode mode) {
channel_->SetRemoteIceMode(mode);
}
virtual void Connect();
virtual void Reset();
virtual void OnSignalingReady() {
channel_->OnSignalingReady();
}
virtual void OnCandidate(const Candidate& candidate) {
channel_->OnCandidate(candidate);
}
// Needed by DtlsTransport.
TransportChannelImpl* channel() { return channel_; }
private:
void OnReadableState(TransportChannel* channel);
void OnWritableState(TransportChannel* channel);
void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
const rtc::PacketTime& packet_time, int flags);
void OnReadyToSend(TransportChannel* channel);
void OnDtlsEvent(rtc::StreamInterface* stream_, int sig, int err);
bool SetupDtls();
bool MaybeStartDtls();
bool HandleDtlsPacket(const char* data, size_t size);
void OnRequestSignaling(TransportChannelImpl* channel);
void OnCandidateReady(TransportChannelImpl* channel, const Candidate& c);
void OnCandidatesAllocationDone(TransportChannelImpl* channel);
void OnRoleConflict(TransportChannelImpl* channel);
void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
void OnConnectionRemoved(TransportChannelImpl* channel);
Transport* transport_; // The transport_ that created us.
rtc::Thread* worker_thread_; // Everything should occur on this thread.
TransportChannelImpl* channel_; // Underlying channel, owned by transport_.
rtc::scoped_ptr<rtc::SSLStreamAdapter> dtls_; // The DTLS stream
StreamInterfaceChannel* downward_; // Wrapper for channel_, owned by dtls_.
std::vector<std::string> srtp_ciphers_; // SRTP ciphers to use with DTLS.
State dtls_state_;
rtc::SSLIdentity* local_identity_;
rtc::SSLRole ssl_role_;
rtc::Buffer remote_fingerprint_value_;
std::string remote_fingerprint_algorithm_;
DISALLOW_COPY_AND_ASSIGN(DtlsTransportChannelWrapper);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_DTLSTRANSPORTCHANNEL_H_

View File

@ -1,841 +0,0 @@
/*
* libjingle
* Copyright 2011, Google Inc.
* Copyright 2011, RTFM, 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 <set>
#include "webrtc/p2p/base/dtlstransport.h"
#include "webrtc/p2p/base/fakesession.h"
#include "webrtc/base/common.h"
#include "webrtc/base/dscp.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/sslstreamadapter.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/base/thread.h"
#define MAYBE_SKIP_TEST(feature) \
if (!(rtc::SSLStreamAdapter::feature())) { \
LOG(LS_INFO) << "Feature disabled... skipping"; \
return; \
}
static const char AES_CM_128_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80";
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const size_t kPacketNumOffset = 8;
static const size_t kPacketHeaderLen = 12;
static bool IsRtpLeadByte(uint8 b) {
return ((b & 0xC0) == 0x80);
}
using cricket::ConnectionRole;
enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 };
class DtlsTestClient : public sigslot::has_slots<> {
public:
DtlsTestClient(const std::string& name,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread) :
name_(name),
signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
protocol_(cricket::ICEPROTO_GOOGLE),
packet_size_(0),
use_dtls_srtp_(false),
negotiated_dtls_(false),
received_dtls_client_hello_(false),
received_dtls_server_hello_(false) {
}
void SetIceProtocol(cricket::TransportProtocol proto) {
protocol_ = proto;
}
void CreateIdentity() {
identity_.reset(rtc::SSLIdentity::Generate(name_));
}
rtc::SSLIdentity* identity() { return identity_.get(); }
void SetupSrtp() {
ASSERT(identity_.get() != NULL);
use_dtls_srtp_ = true;
}
void SetupChannels(int count, cricket::IceRole role) {
transport_.reset(new cricket::DtlsTransport<cricket::FakeTransport>(
signaling_thread_, worker_thread_, "dtls content name", NULL,
identity_.get()));
transport_->SetAsync(true);
transport_->SetIceRole(role);
transport_->SetIceTiebreaker(
(role == cricket::ICEROLE_CONTROLLING) ? 1 : 2);
transport_->SignalWritableState.connect(this,
&DtlsTestClient::OnTransportWritableState);
for (int i = 0; i < count; ++i) {
cricket::DtlsTransportChannelWrapper* channel =
static_cast<cricket::DtlsTransportChannelWrapper*>(
transport_->CreateChannel(i));
ASSERT_TRUE(channel != NULL);
channel->SignalWritableState.connect(this,
&DtlsTestClient::OnTransportChannelWritableState);
channel->SignalReadPacket.connect(this,
&DtlsTestClient::OnTransportChannelReadPacket);
channels_.push_back(channel);
// Hook the raw packets so that we can verify they are encrypted.
channel->channel()->SignalReadPacket.connect(
this, &DtlsTestClient::OnFakeTransportChannelReadPacket);
}
}
cricket::Transport* transport() { return transport_.get(); }
cricket::FakeTransportChannel* GetFakeChannel(int component) {
cricket::TransportChannelImpl* ch = transport_->GetChannel(component);
cricket::DtlsTransportChannelWrapper* wrapper =
static_cast<cricket::DtlsTransportChannelWrapper*>(ch);
return (wrapper) ?
static_cast<cricket::FakeTransportChannel*>(wrapper->channel()) : NULL;
}
// Offer DTLS if we have an identity; pass in a remote fingerprint only if
// both sides support DTLS.
void Negotiate(DtlsTestClient* peer, cricket::ContentAction action,
ConnectionRole local_role, ConnectionRole remote_role,
int flags) {
Negotiate(identity_.get(), (identity_) ? peer->identity_.get() : NULL,
action, local_role, remote_role, flags);
}
// Allow any DTLS configuration to be specified (including invalid ones).
void Negotiate(rtc::SSLIdentity* local_identity,
rtc::SSLIdentity* remote_identity,
cricket::ContentAction action,
ConnectionRole local_role,
ConnectionRole remote_role,
int flags) {
rtc::scoped_ptr<rtc::SSLFingerprint> local_fingerprint;
rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint;
if (local_identity) {
local_fingerprint.reset(rtc::SSLFingerprint::Create(
rtc::DIGEST_SHA_1, local_identity));
ASSERT_TRUE(local_fingerprint.get() != NULL);
}
if (remote_identity) {
remote_fingerprint.reset(rtc::SSLFingerprint::Create(
rtc::DIGEST_SHA_1, remote_identity));
ASSERT_TRUE(remote_fingerprint.get() != NULL);
}
if (use_dtls_srtp_ && !(flags & NF_REOFFER)) {
// SRTP ciphers will be set only in the beginning.
for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it =
channels_.begin(); it != channels_.end(); ++it) {
std::vector<std::string> ciphers;
ciphers.push_back(AES_CM_128_HMAC_SHA1_80);
ASSERT_TRUE((*it)->SetSrtpCiphers(ciphers));
}
}
std::string transport_type = (protocol_ == cricket::ICEPROTO_GOOGLE) ?
cricket::NS_GINGLE_P2P : cricket::NS_JINGLE_ICE_UDP;
cricket::TransportDescription local_desc(
transport_type, std::vector<std::string>(), kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, local_role,
// If remote if the offerer and has no DTLS support, answer will be
// without any fingerprint.
(action == cricket::CA_ANSWER && !remote_identity) ?
NULL : local_fingerprint.get(),
cricket::Candidates());
cricket::TransportDescription remote_desc(
transport_type, std::vector<std::string>(), kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, remote_role, remote_fingerprint.get(),
cricket::Candidates());
bool expect_success = (flags & NF_EXPECT_FAILURE) ? false : true;
// If |expect_success| is false, expect SRTD or SLTD to fail when
// content action is CA_ANSWER.
if (action == cricket::CA_OFFER) {
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, cricket::CA_OFFER, NULL));
ASSERT_EQ(expect_success, transport_->SetRemoteTransportDescription(
remote_desc, cricket::CA_ANSWER, NULL));
} else {
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, cricket::CA_OFFER, NULL));
ASSERT_EQ(expect_success, transport_->SetLocalTransportDescription(
local_desc, cricket::CA_ANSWER, NULL));
}
negotiated_dtls_ = (local_identity && remote_identity);
}
bool Connect(DtlsTestClient* peer) {
transport_->ConnectChannels();
transport_->SetDestination(peer->transport_.get());
return true;
}
bool writable() const { return transport_->writable(); }
void CheckRole(rtc::SSLRole role) {
if (role == rtc::SSL_CLIENT) {
ASSERT_FALSE(received_dtls_client_hello_);
ASSERT_TRUE(received_dtls_server_hello_);
} else {
ASSERT_TRUE(received_dtls_client_hello_);
ASSERT_FALSE(received_dtls_server_hello_);
}
}
void CheckSrtp(const std::string& expected_cipher) {
for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it =
channels_.begin(); it != channels_.end(); ++it) {
std::string cipher;
bool rv = (*it)->GetSrtpCipher(&cipher);
if (negotiated_dtls_ && !expected_cipher.empty()) {
ASSERT_TRUE(rv);
ASSERT_EQ(cipher, expected_cipher);
} else {
ASSERT_FALSE(rv);
}
}
}
void SendPackets(size_t channel, size_t size, size_t count, bool srtp) {
ASSERT(channel < channels_.size());
rtc::scoped_ptr<char[]> packet(new char[size]);
size_t sent = 0;
do {
// Fill the packet with a known value and a sequence number to check
// against, and make sure that it doesn't look like DTLS.
memset(packet.get(), sent & 0xff, size);
packet[0] = (srtp) ? 0x80 : 0x00;
rtc::SetBE32(packet.get() + kPacketNumOffset,
static_cast<uint32>(sent));
// Only set the bypass flag if we've activated DTLS.
int flags = (identity_.get() && srtp) ? cricket::PF_SRTP_BYPASS : 0;
rtc::PacketOptions packet_options;
int rv = channels_[channel]->SendPacket(
packet.get(), size, packet_options, flags);
ASSERT_GT(rv, 0);
ASSERT_EQ(size, static_cast<size_t>(rv));
++sent;
} while (sent < count);
}
int SendInvalidSrtpPacket(size_t channel, size_t size) {
ASSERT(channel < channels_.size());
rtc::scoped_ptr<char[]> packet(new char[size]);
// Fill the packet with 0 to form an invalid SRTP packet.
memset(packet.get(), 0, size);
rtc::PacketOptions packet_options;
return channels_[channel]->SendPacket(
packet.get(), size, packet_options, cricket::PF_SRTP_BYPASS);
}
void ExpectPackets(size_t channel, size_t size) {
packet_size_ = size;
received_.clear();
}
size_t NumPacketsReceived() {
return received_.size();
}
bool VerifyPacket(const char* data, size_t size, uint32* out_num) {
if (size != packet_size_ ||
(data[0] != 0 && static_cast<uint8>(data[0]) != 0x80)) {
return false;
}
uint32 packet_num = rtc::GetBE32(data + kPacketNumOffset);
for (size_t i = kPacketHeaderLen; i < size; ++i) {
if (static_cast<uint8>(data[i]) != (packet_num & 0xff)) {
return false;
}
}
if (out_num) {
*out_num = packet_num;
}
return true;
}
bool VerifyEncryptedPacket(const char* data, size_t size) {
// This is an encrypted data packet; let's make sure it's mostly random;
// less than 10% of the bytes should be equal to the cleartext packet.
if (size <= packet_size_) {
return false;
}
uint32 packet_num = rtc::GetBE32(data + kPacketNumOffset);
int num_matches = 0;
for (size_t i = kPacketNumOffset; i < size; ++i) {
if (static_cast<uint8>(data[i]) == (packet_num & 0xff)) {
++num_matches;
}
}
return (num_matches < ((static_cast<int>(size) - 5) / 10));
}
// Transport callbacks
void OnTransportWritableState(cricket::Transport* transport) {
LOG(LS_INFO) << name_ << ": is writable";
}
// Transport channel callbacks
void OnTransportChannelWritableState(cricket::TransportChannel* channel) {
LOG(LS_INFO) << name_ << ": Channel '" << channel->component()
<< "' is writable";
}
void OnTransportChannelReadPacket(cricket::TransportChannel* channel,
const char* data, size_t size,
const rtc::PacketTime& packet_time,
int flags) {
uint32 packet_num = 0;
ASSERT_TRUE(VerifyPacket(data, size, &packet_num));
received_.insert(packet_num);
// Only DTLS-SRTP packets should have the bypass flag set.
int expected_flags = (identity_.get() && IsRtpLeadByte(data[0])) ?
cricket::PF_SRTP_BYPASS : 0;
ASSERT_EQ(expected_flags, flags);
}
// Hook into the raw packet stream to make sure DTLS packets are encrypted.
void OnFakeTransportChannelReadPacket(cricket::TransportChannel* channel,
const char* data, size_t size,
const rtc::PacketTime& time,
int flags) {
// Flags shouldn't be set on the underlying TransportChannel packets.
ASSERT_EQ(0, flags);
// Look at the handshake packets to see what role we played.
// Check that non-handshake packets are DTLS data or SRTP bypass.
if (negotiated_dtls_) {
if (data[0] == 22 && size > 17) {
if (data[13] == 1) {
received_dtls_client_hello_ = true;
} else if (data[13] == 2) {
received_dtls_server_hello_ = true;
}
} else if (!(data[0] >= 20 && data[0] <= 22)) {
ASSERT_TRUE(data[0] == 23 || IsRtpLeadByte(data[0]));
if (data[0] == 23) {
ASSERT_TRUE(VerifyEncryptedPacket(data, size));
} else if (IsRtpLeadByte(data[0])) {
ASSERT_TRUE(VerifyPacket(data, size, NULL));
}
}
}
}
private:
std::string name_;
rtc::Thread* signaling_thread_;
rtc::Thread* worker_thread_;
cricket::TransportProtocol protocol_;
rtc::scoped_ptr<rtc::SSLIdentity> identity_;
rtc::scoped_ptr<cricket::FakeTransport> transport_;
std::vector<cricket::DtlsTransportChannelWrapper*> channels_;
size_t packet_size_;
std::set<int> received_;
bool use_dtls_srtp_;
bool negotiated_dtls_;
bool received_dtls_client_hello_;
bool received_dtls_server_hello_;
};
class DtlsTransportChannelTest : public testing::Test {
public:
DtlsTransportChannelTest() :
client1_("P1", rtc::Thread::Current(),
rtc::Thread::Current()),
client2_("P2", rtc::Thread::Current(),
rtc::Thread::Current()),
channel_ct_(1),
use_dtls_(false),
use_dtls_srtp_(false) {
}
void SetChannelCount(size_t channel_ct) {
channel_ct_ = static_cast<int>(channel_ct);
}
void PrepareDtls(bool c1, bool c2) {
if (c1) {
client1_.CreateIdentity();
}
if (c2) {
client2_.CreateIdentity();
}
if (c1 && c2)
use_dtls_ = true;
}
void PrepareDtlsSrtp(bool c1, bool c2) {
if (!use_dtls_)
return;
if (c1)
client1_.SetupSrtp();
if (c2)
client2_.SetupSrtp();
if (c1 && c2)
use_dtls_srtp_ = true;
}
bool Connect(ConnectionRole client1_role, ConnectionRole client2_role) {
Negotiate(client1_role, client2_role);
bool rv = client1_.Connect(&client2_);
EXPECT_TRUE(rv);
if (!rv)
return false;
EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000);
if (!client1_.writable() || !client2_.writable())
return false;
// Check that we used the right roles.
if (use_dtls_) {
rtc::SSLRole client1_ssl_role =
(client1_role == cricket::CONNECTIONROLE_ACTIVE ||
(client2_role == cricket::CONNECTIONROLE_PASSIVE &&
client1_role == cricket::CONNECTIONROLE_ACTPASS)) ?
rtc::SSL_CLIENT : rtc::SSL_SERVER;
rtc::SSLRole client2_ssl_role =
(client2_role == cricket::CONNECTIONROLE_ACTIVE ||
(client1_role == cricket::CONNECTIONROLE_PASSIVE &&
client2_role == cricket::CONNECTIONROLE_ACTPASS)) ?
rtc::SSL_CLIENT : rtc::SSL_SERVER;
client1_.CheckRole(client1_ssl_role);
client2_.CheckRole(client2_ssl_role);
}
// Check that we negotiated the right ciphers.
if (use_dtls_srtp_) {
client1_.CheckSrtp(AES_CM_128_HMAC_SHA1_80);
client2_.CheckSrtp(AES_CM_128_HMAC_SHA1_80);
} else {
client1_.CheckSrtp("");
client2_.CheckSrtp("");
}
return true;
}
bool Connect() {
// By default, Client1 will be Server and Client2 will be Client.
return Connect(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE);
}
void Negotiate() {
Negotiate(cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE);
}
void Negotiate(ConnectionRole client1_role, ConnectionRole client2_role) {
client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING);
client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED);
// Expect success from SLTD and SRTD.
client1_.Negotiate(&client2_, cricket::CA_OFFER,
client1_role, client2_role, 0);
client2_.Negotiate(&client1_, cricket::CA_ANSWER,
client2_role, client1_role, 0);
}
// Negotiate with legacy client |client2|. Legacy client doesn't use setup
// attributes, except NONE.
void NegotiateWithLegacy() {
client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING);
client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED);
// Expect success from SLTD and SRTD.
client1_.Negotiate(&client2_, cricket::CA_OFFER,
cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_NONE, 0);
client2_.Negotiate(&client1_, cricket::CA_ANSWER,
cricket::CONNECTIONROLE_ACTIVE,
cricket::CONNECTIONROLE_NONE, 0);
}
void Renegotiate(DtlsTestClient* reoffer_initiator,
ConnectionRole client1_role, ConnectionRole client2_role,
int flags) {
if (reoffer_initiator == &client1_) {
client1_.Negotiate(&client2_, cricket::CA_OFFER,
client1_role, client2_role, flags);
client2_.Negotiate(&client1_, cricket::CA_ANSWER,
client2_role, client1_role, flags);
} else {
client2_.Negotiate(&client1_, cricket::CA_OFFER,
client2_role, client1_role, flags);
client1_.Negotiate(&client2_, cricket::CA_ANSWER,
client1_role, client2_role, flags);
}
}
void TestTransfer(size_t channel, size_t size, size_t count, bool srtp) {
LOG(LS_INFO) << "Expect packets, size=" << size;
client2_.ExpectPackets(channel, size);
client1_.SendPackets(channel, size, count, srtp);
EXPECT_EQ_WAIT(count, client2_.NumPacketsReceived(), 10000);
}
protected:
DtlsTestClient client1_;
DtlsTestClient client2_;
int channel_ct_;
bool use_dtls_;
bool use_dtls_srtp_;
};
// Test that transport negotiation of ICE, no DTLS works properly.
TEST_F(DtlsTransportChannelTest, TestChannelSetupIce) {
client1_.SetIceProtocol(cricket::ICEPROTO_RFC5245);
client2_.SetIceProtocol(cricket::ICEPROTO_RFC5245);
Negotiate();
cricket::FakeTransportChannel* channel1 = client1_.GetFakeChannel(0);
cricket::FakeTransportChannel* channel2 = client2_.GetFakeChannel(0);
ASSERT_TRUE(channel1 != NULL);
ASSERT_TRUE(channel2 != NULL);
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
EXPECT_EQ(1U, channel1->IceTiebreaker());
EXPECT_EQ(cricket::ICEPROTO_RFC5245, channel1->protocol());
EXPECT_EQ(kIceUfrag1, channel1->ice_ufrag());
EXPECT_EQ(kIcePwd1, channel1->ice_pwd());
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole());
EXPECT_EQ(2U, channel2->IceTiebreaker());
EXPECT_EQ(cricket::ICEPROTO_RFC5245, channel2->protocol());
}
// Test that transport negotiation of GICE, no DTLS works properly.
TEST_F(DtlsTransportChannelTest, TestChannelSetupGice) {
client1_.SetIceProtocol(cricket::ICEPROTO_GOOGLE);
client2_.SetIceProtocol(cricket::ICEPROTO_GOOGLE);
Negotiate();
cricket::FakeTransportChannel* channel1 = client1_.GetFakeChannel(0);
cricket::FakeTransportChannel* channel2 = client2_.GetFakeChannel(0);
ASSERT_TRUE(channel1 != NULL);
ASSERT_TRUE(channel2 != NULL);
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
EXPECT_EQ(1U, channel1->IceTiebreaker());
EXPECT_EQ(cricket::ICEPROTO_GOOGLE, channel1->protocol());
EXPECT_EQ(kIceUfrag1, channel1->ice_ufrag());
EXPECT_EQ(kIcePwd1, channel1->ice_pwd());
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole());
EXPECT_EQ(2U, channel2->IceTiebreaker());
EXPECT_EQ(cricket::ICEPROTO_GOOGLE, channel2->protocol());
}
// Connect without DTLS, and transfer some data.
TEST_F(DtlsTransportChannelTest, TestTransfer) {
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
}
// Create two channels without DTLS, and transfer some data.
TEST_F(DtlsTransportChannelTest, TestTransferTwoChannels) {
SetChannelCount(2);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
TestTransfer(1, 1000, 100, false);
}
// Connect without DTLS, and transfer SRTP data.
TEST_F(DtlsTransportChannelTest, TestTransferSrtp) {
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, true);
}
// Create two channels without DTLS, and transfer SRTP data.
TEST_F(DtlsTransportChannelTest, TestTransferSrtpTwoChannels) {
SetChannelCount(2);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
// Connect with DTLS, and transfer some data.
TEST_F(DtlsTransportChannelTest, TestTransferDtls) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
}
// Create two channels with DTLS, and transfer some data.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsTwoChannels) {
MAYBE_SKIP_TEST(HaveDtls);
SetChannelCount(2);
PrepareDtls(true, true);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
TestTransfer(1, 1000, 100, false);
}
// Connect with A doing DTLS and B not, and transfer some data.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsRejected) {
PrepareDtls(true, false);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
}
// Connect with B doing DTLS and A not, and transfer some data.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsNotOffered) {
PrepareDtls(false, true);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
}
// Connect with DTLS, negotiate DTLS-SRTP, and transfer SRTP using bypass.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtp) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, true);
}
// Connect with DTLS-SRTP, transfer an invalid SRTP packet, and expects -1
// returned.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsInvalidSrtpPacket) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect());
int result = client1_.SendInvalidSrtpPacket(0, 100);
ASSERT_EQ(-1, result);
}
// Connect with DTLS. A does DTLS-SRTP but B does not.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpRejected) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, false);
ASSERT_TRUE(Connect());
}
// Connect with DTLS. B does DTLS-SRTP but A does not.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpNotOffered) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true);
PrepareDtlsSrtp(false, true);
ASSERT_TRUE(Connect());
}
// Create two channels with DTLS, negotiate DTLS-SRTP, and transfer bypass SRTP.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpTwoChannels) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
// Create a single channel with DTLS, and send normal data and SRTP data on it.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpDemux) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect());
TestTransfer(0, 1000, 100, false);
TestTransfer(0, 1000, 100, true);
}
// Testing when the remote is passive.
TEST_F(DtlsTransportChannelTest, TestTransferDtlsAnswererIsPassive) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_PASSIVE));
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
// Testing with the legacy DTLS client which doesn't use setup attribute.
// In this case legacy is the answerer.
TEST_F(DtlsTransportChannelTest, TestDtlsSetupWithLegacyAsAnswerer) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
PrepareDtls(true, true);
NegotiateWithLegacy();
rtc::SSLRole channel1_role;
rtc::SSLRole channel2_role;
EXPECT_TRUE(client1_.transport()->GetSslRole(&channel1_role));
EXPECT_TRUE(client2_.transport()->GetSslRole(&channel2_role));
EXPECT_EQ(rtc::SSL_SERVER, channel1_role);
EXPECT_EQ(rtc::SSL_CLIENT, channel2_role);
}
// Testing re offer/answer after the session is estbalished. Roles will be
// kept same as of the previous negotiation.
TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromOfferer) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
// Initial role for client1 is ACTPASS and client2 is ACTIVE.
ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE));
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
// Using input roles for the re-offer.
Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER);
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromAnswerer) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
// Initial role for client1 is ACTPASS and client2 is ACTIVE.
ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE));
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
// Using input roles for the re-offer.
Renegotiate(&client2_, cricket::CONNECTIONROLE_PASSIVE,
cricket::CONNECTIONROLE_ACTPASS, NF_REOFFER);
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
// Test that any change in role after the intial setup will result in failure.
TEST_F(DtlsTransportChannelTest, TestDtlsRoleReversal) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_PASSIVE));
// Renegotiate from client2 with actpass and client1 as active.
Renegotiate(&client2_, cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE,
NF_REOFFER | NF_EXPECT_FAILURE);
}
// Test that using different setup attributes which results in similar ssl
// role as the initial negotiation will result in success.
TEST_F(DtlsTransportChannelTest, TestDtlsReOfferWithDifferentSetupAttr) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_PASSIVE));
// Renegotiate from client2 with actpass and client1 as active.
Renegotiate(&client2_, cricket::CONNECTIONROLE_ACTIVE,
cricket::CONNECTIONROLE_ACTPASS, NF_REOFFER);
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
// Test that re-negotiation can be started before the clients become connected
// in the first negotiation.
TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) {
MAYBE_SKIP_TEST(HaveDtlsSrtp);
SetChannelCount(2);
PrepareDtls(true, true);
PrepareDtlsSrtp(true, true);
Negotiate();
Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER);
bool rv = client1_.Connect(&client2_);
EXPECT_TRUE(rv);
EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000);
TestTransfer(0, 1000, 100, true);
TestTransfer(1, 1000, 100, true);
}
// Test Certificates state after negotiation but before connection.
TEST_F(DtlsTransportChannelTest, TestCertificatesBeforeConnect) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true);
Negotiate();
rtc::scoped_ptr<rtc::SSLIdentity> identity1;
rtc::scoped_ptr<rtc::SSLIdentity> identity2;
rtc::scoped_ptr<rtc::SSLCertificate> remote_cert1;
rtc::scoped_ptr<rtc::SSLCertificate> remote_cert2;
// After negotiation, each side has a distinct local certificate, but still no
// remote certificate, because connection has not yet occurred.
ASSERT_TRUE(client1_.transport()->GetIdentity(identity1.accept()));
ASSERT_TRUE(client2_.transport()->GetIdentity(identity2.accept()));
ASSERT_NE(identity1->certificate().ToPEMString(),
identity2->certificate().ToPEMString());
ASSERT_FALSE(
client1_.transport()->GetRemoteCertificate(remote_cert1.accept()));
ASSERT_FALSE(remote_cert1 != NULL);
ASSERT_FALSE(
client2_.transport()->GetRemoteCertificate(remote_cert2.accept()));
ASSERT_FALSE(remote_cert2 != NULL);
}
// Test Certificates state after connection.
TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) {
MAYBE_SKIP_TEST(HaveDtls);
PrepareDtls(true, true);
ASSERT_TRUE(Connect());
rtc::scoped_ptr<rtc::SSLIdentity> identity1;
rtc::scoped_ptr<rtc::SSLIdentity> identity2;
rtc::scoped_ptr<rtc::SSLCertificate> remote_cert1;
rtc::scoped_ptr<rtc::SSLCertificate> remote_cert2;
// After connection, each side has a distinct local certificate.
ASSERT_TRUE(client1_.transport()->GetIdentity(identity1.accept()));
ASSERT_TRUE(client2_.transport()->GetIdentity(identity2.accept()));
ASSERT_NE(identity1->certificate().ToPEMString(),
identity2->certificate().ToPEMString());
// Each side's remote certificate is the other side's local certificate.
ASSERT_TRUE(
client1_.transport()->GetRemoteCertificate(remote_cert1.accept()));
ASSERT_EQ(remote_cert1->ToPEMString(),
identity2->certificate().ToPEMString());
ASSERT_TRUE(
client2_.transport()->GetRemoteCertificate(remote_cert2.accept()));
ASSERT_EQ(remote_cert2->ToPEMString(),
identity1->certificate().ToPEMString());
}

View File

@ -1,509 +0,0 @@
/*
* libjingle
* Copyright 2009, 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.
*/
#ifndef WEBRTC_P2P_BASE_FAKESESSION_H_
#define WEBRTC_P2P_BASE_FAKESESSION_H_
#include <map>
#include <string>
#include <vector>
#include "webrtc/p2p/base/session.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportchannel.h"
#include "webrtc/p2p/base/transportchannelimpl.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/fakesslidentity.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslfingerprint.h"
namespace cricket {
class FakeTransport;
struct PacketMessageData : public rtc::MessageData {
PacketMessageData(const char* data, size_t len) : packet(data, len) {
}
rtc::Buffer packet;
};
// Fake transport channel class, which can be passed to anything that needs a
// transport channel. Can be informed of another FakeTransportChannel via
// SetDestination.
class FakeTransportChannel : public TransportChannelImpl,
public rtc::MessageHandler {
public:
explicit FakeTransportChannel(Transport* transport,
const std::string& content_name,
int component)
: TransportChannelImpl(content_name, component),
transport_(transport),
dest_(NULL),
state_(STATE_INIT),
async_(false),
identity_(NULL),
do_dtls_(false),
role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
ice_proto_(ICEPROTO_HYBRID),
remote_ice_mode_(ICEMODE_FULL),
dtls_fingerprint_("", NULL, 0),
ssl_role_(rtc::SSL_CLIENT),
connection_count_(0) {
}
~FakeTransportChannel() {
Reset();
}
uint64 IceTiebreaker() const { return tiebreaker_; }
TransportProtocol protocol() const { return ice_proto_; }
IceMode remote_ice_mode() const { return remote_ice_mode_; }
const std::string& ice_ufrag() const { return ice_ufrag_; }
const std::string& ice_pwd() const { return ice_pwd_; }
const std::string& remote_ice_ufrag() const { return remote_ice_ufrag_; }
const std::string& remote_ice_pwd() const { return remote_ice_pwd_; }
const rtc::SSLFingerprint& dtls_fingerprint() const {
return dtls_fingerprint_;
}
void SetAsync(bool async) {
async_ = async;
}
virtual Transport* GetTransport() {
return transport_;
}
virtual void SetIceRole(IceRole role) { role_ = role; }
virtual IceRole GetIceRole() const { return role_; }
virtual size_t GetConnectionCount() const { return connection_count_; }
virtual void SetIceTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; }
virtual bool GetIceProtocolType(IceProtocolType* type) const {
*type = ice_proto_;
return true;
}
virtual void SetIceProtocolType(IceProtocolType type) { ice_proto_ = type; }
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
ice_ufrag_ = ice_ufrag;
ice_pwd_ = ice_pwd;
}
virtual void SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
remote_ice_ufrag_ = ice_ufrag;
remote_ice_pwd_ = ice_pwd;
}
virtual void SetRemoteIceMode(IceMode mode) { remote_ice_mode_ = mode; }
virtual bool SetRemoteFingerprint(const std::string& alg, const uint8* digest,
size_t digest_len) {
dtls_fingerprint_ = rtc::SSLFingerprint(alg, digest, digest_len);
return true;
}
virtual bool SetSslRole(rtc::SSLRole role) {
ssl_role_ = role;
return true;
}
virtual bool GetSslRole(rtc::SSLRole* role) const {
*role = ssl_role_;
return true;
}
virtual void Connect() {
if (state_ == STATE_INIT) {
state_ = STATE_CONNECTING;
}
}
virtual void Reset() {
if (state_ != STATE_INIT) {
state_ = STATE_INIT;
if (dest_) {
dest_->state_ = STATE_INIT;
dest_->dest_ = NULL;
dest_ = NULL;
}
}
}
void SetWritable(bool writable) {
set_writable(writable);
}
void SetDestination(FakeTransportChannel* dest) {
if (state_ == STATE_CONNECTING && dest) {
// This simulates the delivery of candidates.
dest_ = dest;
dest_->dest_ = this;
if (identity_ && dest_->identity_) {
do_dtls_ = true;
dest_->do_dtls_ = true;
NegotiateSrtpCiphers();
}
state_ = STATE_CONNECTED;
dest_->state_ = STATE_CONNECTED;
set_writable(true);
dest_->set_writable(true);
} else if (state_ == STATE_CONNECTED && !dest) {
// Simulates loss of connectivity, by asymmetrically forgetting dest_.
dest_ = NULL;
state_ = STATE_CONNECTING;
set_writable(false);
}
}
void SetConnectionCount(size_t connection_count) {
size_t old_connection_count = connection_count_;
connection_count_ = connection_count;
if (connection_count_ < old_connection_count)
SignalConnectionRemoved(this);
}
virtual int SendPacket(const char* data, size_t len,
const rtc::PacketOptions& options, int flags) {
if (state_ != STATE_CONNECTED) {
return -1;
}
if (flags != PF_SRTP_BYPASS && flags != 0) {
return -1;
}
PacketMessageData* packet = new PacketMessageData(data, len);
if (async_) {
rtc::Thread::Current()->Post(this, 0, packet);
} else {
rtc::Thread::Current()->Send(this, 0, packet);
}
return static_cast<int>(len);
}
virtual int SetOption(rtc::Socket::Option opt, int value) {
return true;
}
virtual int GetError() {
return 0;
}
virtual void OnSignalingReady() {
}
virtual void OnCandidate(const Candidate& candidate) {
}
virtual void OnMessage(rtc::Message* msg) {
PacketMessageData* data = static_cast<PacketMessageData*>(
msg->pdata);
dest_->SignalReadPacket(dest_, data->packet.data(),
data->packet.length(),
rtc::CreatePacketTime(0), 0);
delete data;
}
bool SetLocalIdentity(rtc::SSLIdentity* identity) {
identity_ = identity;
return true;
}
void SetRemoteCertificate(rtc::FakeSSLCertificate* cert) {
remote_cert_ = cert;
}
virtual bool IsDtlsActive() const {
return do_dtls_;
}
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) {
srtp_ciphers_ = ciphers;
return true;
}
virtual bool GetSrtpCipher(std::string* cipher) {
if (!chosen_srtp_cipher_.empty()) {
*cipher = chosen_srtp_cipher_;
return true;
}
return false;
}
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
if (!identity_)
return false;
*identity = identity_->GetReference();
return true;
}
virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const {
if (!remote_cert_)
return false;
*cert = remote_cert_->GetReference();
return true;
}
virtual bool ExportKeyingMaterial(const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len) {
if (!chosen_srtp_cipher_.empty()) {
memset(result, 0xff, result_len);
return true;
}
return false;
}
virtual void NegotiateSrtpCiphers() {
for (std::vector<std::string>::const_iterator it1 = srtp_ciphers_.begin();
it1 != srtp_ciphers_.end(); ++it1) {
for (std::vector<std::string>::const_iterator it2 =
dest_->srtp_ciphers_.begin();
it2 != dest_->srtp_ciphers_.end(); ++it2) {
if (*it1 == *it2) {
chosen_srtp_cipher_ = *it1;
dest_->chosen_srtp_cipher_ = *it2;
return;
}
}
}
}
virtual bool GetStats(ConnectionInfos* infos) OVERRIDE {
ConnectionInfo info;
infos->clear();
infos->push_back(info);
return true;
}
private:
enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED };
Transport* transport_;
FakeTransportChannel* dest_;
State state_;
bool async_;
rtc::SSLIdentity* identity_;
rtc::FakeSSLCertificate* remote_cert_;
bool do_dtls_;
std::vector<std::string> srtp_ciphers_;
std::string chosen_srtp_cipher_;
IceRole role_;
uint64 tiebreaker_;
IceProtocolType ice_proto_;
std::string ice_ufrag_;
std::string ice_pwd_;
std::string remote_ice_ufrag_;
std::string remote_ice_pwd_;
IceMode remote_ice_mode_;
rtc::SSLFingerprint dtls_fingerprint_;
rtc::SSLRole ssl_role_;
size_t connection_count_;
};
// Fake transport class, which can be passed to anything that needs a Transport.
// Can be informed of another FakeTransport via SetDestination (low-tech way
// of doing candidates)
class FakeTransport : public Transport {
public:
typedef std::map<int, FakeTransportChannel*> ChannelMap;
FakeTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
PortAllocator* alllocator = NULL)
: Transport(signaling_thread, worker_thread,
content_name, "test_type", NULL),
dest_(NULL),
async_(false),
identity_(NULL) {
}
~FakeTransport() {
DestroyAllChannels();
}
const ChannelMap& channels() const { return channels_; }
void SetAsync(bool async) { async_ = async; }
void SetDestination(FakeTransport* dest) {
dest_ = dest;
for (ChannelMap::iterator it = channels_.begin(); it != channels_.end();
++it) {
it->second->SetLocalIdentity(identity_);
SetChannelDestination(it->first, it->second);
}
}
void SetWritable(bool writable) {
for (ChannelMap::iterator it = channels_.begin(); it != channels_.end();
++it) {
it->second->SetWritable(writable);
}
}
void set_identity(rtc::SSLIdentity* identity) {
identity_ = identity;
}
using Transport::local_description;
using Transport::remote_description;
protected:
virtual TransportChannelImpl* CreateTransportChannel(int component) {
if (channels_.find(component) != channels_.end()) {
return NULL;
}
FakeTransportChannel* channel =
new FakeTransportChannel(this, content_name(), component);
channel->SetAsync(async_);
SetChannelDestination(component, channel);
channels_[component] = channel;
return channel;
}
virtual void DestroyTransportChannel(TransportChannelImpl* channel) {
channels_.erase(channel->component());
delete channel;
}
virtual void SetIdentity_w(rtc::SSLIdentity* identity) {
identity_ = identity;
}
virtual bool GetIdentity_w(rtc::SSLIdentity** identity) {
if (!identity_)
return false;
*identity = identity_->GetReference();
return true;
}
private:
FakeTransportChannel* GetFakeChannel(int component) {
ChannelMap::iterator it = channels_.find(component);
return (it != channels_.end()) ? it->second : NULL;
}
void SetChannelDestination(int component,
FakeTransportChannel* channel) {
FakeTransportChannel* dest_channel = NULL;
if (dest_) {
dest_channel = dest_->GetFakeChannel(component);
if (dest_channel) {
dest_channel->SetLocalIdentity(dest_->identity_);
}
}
channel->SetDestination(dest_channel);
}
// Note, this is distinct from the Channel map owned by Transport.
// This map just tracks the FakeTransportChannels created by this class.
ChannelMap channels_;
FakeTransport* dest_;
bool async_;
rtc::SSLIdentity* identity_;
};
// Fake session class, which can be passed into a BaseChannel object for
// test purposes. Can be connected to other FakeSessions via Connect().
class FakeSession : public BaseSession {
public:
explicit FakeSession()
: BaseSession(rtc::Thread::Current(),
rtc::Thread::Current(),
NULL, "", "", true),
fail_create_channel_(false) {
}
explicit FakeSession(bool initiator)
: BaseSession(rtc::Thread::Current(),
rtc::Thread::Current(),
NULL, "", "", initiator),
fail_create_channel_(false) {
}
FakeSession(rtc::Thread* worker_thread, bool initiator)
: BaseSession(rtc::Thread::Current(),
worker_thread,
NULL, "", "", initiator),
fail_create_channel_(false) {
}
FakeTransport* GetTransport(const std::string& content_name) {
return static_cast<FakeTransport*>(
BaseSession::GetTransport(content_name));
}
void Connect(FakeSession* dest) {
// Simulate the exchange of candidates.
CompleteNegotiation();
dest->CompleteNegotiation();
for (TransportMap::const_iterator it = transport_proxies().begin();
it != transport_proxies().end(); ++it) {
static_cast<FakeTransport*>(it->second->impl())->SetDestination(
dest->GetTransport(it->first));
}
}
virtual TransportChannel* CreateChannel(
const std::string& content_name,
const std::string& channel_name,
int component) {
if (fail_create_channel_) {
return NULL;
}
return BaseSession::CreateChannel(content_name, channel_name, component);
}
void set_fail_channel_creation(bool fail_channel_creation) {
fail_create_channel_ = fail_channel_creation;
}
// TODO: Hoist this into Session when we re-work the Session code.
void set_ssl_identity(rtc::SSLIdentity* identity) {
for (TransportMap::const_iterator it = transport_proxies().begin();
it != transport_proxies().end(); ++it) {
// We know that we have a FakeTransport*
static_cast<FakeTransport*>(it->second->impl())->set_identity
(identity);
}
}
protected:
virtual Transport* CreateTransport(const std::string& content_name) {
return new FakeTransport(signaling_thread(), worker_thread(), content_name);
}
void CompleteNegotiation() {
for (TransportMap::const_iterator it = transport_proxies().begin();
it != transport_proxies().end(); ++it) {
it->second->CompleteNegotiation();
it->second->ConnectChannels();
}
}
private:
bool fail_create_channel_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_FAKESESSION_H_

View File

@ -1,263 +0,0 @@
/*
* 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 "webrtc/p2p/base/p2ptransport.h"
#include <string>
#include <vector>
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/p2ptransportchannel.h"
#include "webrtc/p2p/base/parsing.h"
#include "webrtc/p2p/base/sessionmanager.h"
#include "webrtc/p2p/base/sessionmessages.h"
#include "webrtc/libjingle/xmllite/qname.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/base/base64.h"
#include "webrtc/base/common.h"
#include "webrtc/base/stringencode.h"
#include "webrtc/base/stringutils.h"
namespace {
// Limits for GICE and ICE username sizes.
const size_t kMaxGiceUsernameSize = 16;
const size_t kMaxIceUsernameSize = 512;
} // namespace
namespace cricket {
static buzz::XmlElement* NewTransportElement(const std::string& name) {
return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true);
}
P2PTransport::P2PTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
PortAllocator* allocator)
: Transport(signaling_thread, worker_thread,
content_name, NS_GINGLE_P2P, allocator) {
}
P2PTransport::~P2PTransport() {
DestroyAllChannels();
}
TransportChannelImpl* P2PTransport::CreateTransportChannel(int component) {
return new P2PTransportChannel(content_name(), component, this,
port_allocator());
}
void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
delete channel;
}
bool P2PTransportParser::ParseTransportDescription(
const buzz::XmlElement* elem,
const CandidateTranslator* translator,
TransportDescription* desc,
ParseError* error) {
ASSERT(elem->Name().LocalPart() == LN_TRANSPORT);
desc->transport_type = elem->Name().Namespace();
if (desc->transport_type != NS_GINGLE_P2P)
return BadParse("Unsupported transport type", error);
for (const buzz::XmlElement* candidate_elem = elem->FirstElement();
candidate_elem != NULL;
candidate_elem = candidate_elem->NextElement()) {
// Only look at local part because the namespace might (eventually)
// be NS_GINGLE_P2P or NS_JINGLE_ICE_UDP.
if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) {
Candidate candidate;
if (!ParseCandidate(ICEPROTO_GOOGLE, candidate_elem, translator,
&candidate, error)) {
return false;
}
desc->candidates.push_back(candidate);
}
}
return true;
}
bool P2PTransportParser::WriteTransportDescription(
const TransportDescription& desc,
const CandidateTranslator* translator,
buzz::XmlElement** out_elem,
WriteError* error) {
TransportProtocol proto = TransportProtocolFromDescription(&desc);
rtc::scoped_ptr<buzz::XmlElement> trans_elem(
NewTransportElement(desc.transport_type));
// Fail if we get HYBRID or ICE right now.
// TODO(juberti): Add ICE and HYBRID serialization.
if (proto != ICEPROTO_GOOGLE) {
LOG(LS_ERROR) << "Failed to serialize non-GICE TransportDescription";
return false;
}
for (std::vector<Candidate>::const_iterator iter = desc.candidates.begin();
iter != desc.candidates.end(); ++iter) {
rtc::scoped_ptr<buzz::XmlElement> cand_elem(
new buzz::XmlElement(QN_GINGLE_P2P_CANDIDATE));
if (!WriteCandidate(proto, *iter, translator, cand_elem.get(), error)) {
return false;
}
trans_elem->AddElement(cand_elem.release());
}
*out_elem = trans_elem.release();
return true;
}
bool P2PTransportParser::ParseGingleCandidate(
const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidate* candidate,
ParseError* error) {
return ParseCandidate(ICEPROTO_GOOGLE, elem, translator, candidate, error);
}
bool P2PTransportParser::WriteGingleCandidate(
const Candidate& candidate,
const CandidateTranslator* translator,
buzz::XmlElement** out_elem,
WriteError* error) {
rtc::scoped_ptr<buzz::XmlElement> elem(
new buzz::XmlElement(QN_GINGLE_CANDIDATE));
bool ret = WriteCandidate(ICEPROTO_GOOGLE, candidate, translator, elem.get(),
error);
if (ret) {
*out_elem = elem.release();
}
return ret;
}
bool P2PTransportParser::VerifyUsernameFormat(TransportProtocol proto,
const std::string& username,
ParseError* error) {
if (proto == ICEPROTO_GOOGLE || proto == ICEPROTO_HYBRID) {
if (username.size() > kMaxGiceUsernameSize)
return BadParse("candidate username is too long", error);
if (!rtc::Base64::IsBase64Encoded(username))
return BadParse("candidate username has non-base64 encoded characters",
error);
} else if (proto == ICEPROTO_RFC5245) {
if (username.size() > kMaxIceUsernameSize)
return BadParse("candidate username is too long", error);
}
return true;
}
bool P2PTransportParser::ParseCandidate(TransportProtocol proto,
const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidate* candidate,
ParseError* error) {
ASSERT(proto == ICEPROTO_GOOGLE);
ASSERT(translator != NULL);
if (!elem->HasAttr(buzz::QN_NAME) ||
!elem->HasAttr(QN_ADDRESS) ||
!elem->HasAttr(QN_PORT) ||
!elem->HasAttr(QN_USERNAME) ||
!elem->HasAttr(QN_PROTOCOL) ||
!elem->HasAttr(QN_GENERATION)) {
return BadParse("candidate missing required attribute", error);
}
rtc::SocketAddress address;
if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, &address, error))
return false;
std::string channel_name = elem->Attr(buzz::QN_NAME);
int component = 0;
if (!translator ||
!translator->GetComponentFromChannelName(channel_name, &component)) {
return BadParse("candidate has unknown channel name " + channel_name,
error);
}
float preference = 0.0;
if (!GetXmlAttr(elem, QN_PREFERENCE, 0.0f, &preference)) {
return BadParse("candidate has unknown preference", error);
}
candidate->set_component(component);
candidate->set_address(address);
candidate->set_username(elem->Attr(QN_USERNAME));
candidate->set_preference(preference);
candidate->set_protocol(elem->Attr(QN_PROTOCOL));
candidate->set_generation_str(elem->Attr(QN_GENERATION));
if (elem->HasAttr(QN_PASSWORD))
candidate->set_password(elem->Attr(QN_PASSWORD));
if (elem->HasAttr(buzz::QN_TYPE))
candidate->set_type(elem->Attr(buzz::QN_TYPE));
if (elem->HasAttr(QN_NETWORK))
candidate->set_network_name(elem->Attr(QN_NETWORK));
if (!VerifyUsernameFormat(proto, candidate->username(), error))
return false;
return true;
}
bool P2PTransportParser::WriteCandidate(TransportProtocol proto,
const Candidate& candidate,
const CandidateTranslator* translator,
buzz::XmlElement* elem,
WriteError* error) {
ASSERT(proto == ICEPROTO_GOOGLE);
ASSERT(translator != NULL);
std::string channel_name;
if (!translator ||
!translator->GetChannelNameFromComponent(
candidate.component(), &channel_name)) {
return BadWrite("Cannot write candidate because of unknown component.",
error);
}
elem->SetAttr(buzz::QN_NAME, channel_name);
elem->SetAttr(QN_ADDRESS, candidate.address().ipaddr().ToString());
elem->SetAttr(QN_PORT, candidate.address().PortAsString());
AddXmlAttr(elem, QN_PREFERENCE, candidate.preference());
elem->SetAttr(QN_USERNAME, candidate.username());
elem->SetAttr(QN_PROTOCOL, candidate.protocol());
elem->SetAttr(QN_GENERATION, candidate.generation_str());
if (!candidate.password().empty())
elem->SetAttr(QN_PASSWORD, candidate.password());
elem->SetAttr(buzz::QN_TYPE, candidate.type());
if (!candidate.network_name().empty())
elem->SetAttr(QN_NETWORK, candidate.network_name());
return true;
}
} // namespace cricket

View File

@ -1,103 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_P2PTRANSPORT_H_
#define WEBRTC_P2P_BASE_P2PTRANSPORT_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/transport.h"
namespace cricket {
class P2PTransport : public Transport {
public:
P2PTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
PortAllocator* allocator);
virtual ~P2PTransport();
protected:
// Creates and destroys P2PTransportChannel.
virtual TransportChannelImpl* CreateTransportChannel(int component);
virtual void DestroyTransportChannel(TransportChannelImpl* channel);
friend class P2PTransportChannel;
DISALLOW_EVIL_CONSTRUCTORS(P2PTransport);
};
class P2PTransportParser : public TransportParser {
public:
P2PTransportParser() {}
// Translator may be null, in which case ParseCandidates should
// return false if there are candidates to parse. We can't not call
// ParseCandidates because there's no way to know ahead of time if
// there are candidates or not.
// Jingle-specific functions; can be used with either ICE, GICE, or HYBRID.
virtual bool ParseTransportDescription(const buzz::XmlElement* elem,
const CandidateTranslator* translator,
TransportDescription* desc,
ParseError* error);
virtual bool WriteTransportDescription(const TransportDescription& desc,
const CandidateTranslator* translator,
buzz::XmlElement** elem,
WriteError* error);
// Legacy Gingle functions; only can be used with GICE.
virtual bool ParseGingleCandidate(const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidate* candidate,
ParseError* error);
virtual bool WriteGingleCandidate(const Candidate& candidate,
const CandidateTranslator* translator,
buzz::XmlElement** elem,
WriteError* error);
private:
bool ParseCandidate(TransportProtocol proto,
const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidate* candidate,
ParseError* error);
bool WriteCandidate(TransportProtocol proto,
const Candidate& candidate,
const CandidateTranslator* translator,
buzz::XmlElement* elem,
WriteError* error);
bool VerifyUsernameFormat(TransportProtocol proto,
const std::string& username,
ParseError* error);
DISALLOW_EVIL_CONSTRUCTORS(P2PTransportParser);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_P2PTRANSPORT_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,259 +0,0 @@
/*
* 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.
*/
// P2PTransportChannel wraps up the state management of the connection between
// two P2P clients. Clients have candidate ports for connecting, and
// connections which are combinations of candidates from each end (Alice and
// Bob each have candidates, one candidate from Alice and one candidate from
// Bob are used to make a connection, repeat to make many connections).
//
// When all of the available connections become invalid (non-writable), we
// kick off a process of determining more candidates and more connections.
//
#ifndef WEBRTC_P2P_BASE_P2PTRANSPORTCHANNEL_H_
#define WEBRTC_P2P_BASE_P2PTRANSPORTCHANNEL_H_
#include <map>
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/p2ptransport.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportchannelimpl.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/sigslot.h"
namespace cricket {
// Adds the port on which the candidate originated.
class RemoteCandidate : public Candidate {
public:
RemoteCandidate(const Candidate& c, PortInterface* origin_port)
: Candidate(c), origin_port_(origin_port) {}
PortInterface* origin_port() { return origin_port_; }
private:
PortInterface* origin_port_;
};
// P2PTransportChannel manages the candidates and connection process to keep
// two P2P clients connected to each other.
class P2PTransportChannel : public TransportChannelImpl,
public rtc::MessageHandler {
public:
P2PTransportChannel(const std::string& content_name,
int component,
P2PTransport* transport,
PortAllocator *allocator);
virtual ~P2PTransportChannel();
// From TransportChannelImpl:
virtual Transport* GetTransport() { return transport_; }
virtual void SetIceRole(IceRole role);
virtual IceRole GetIceRole() const { return ice_role_; }
virtual void SetIceTiebreaker(uint64 tiebreaker);
virtual size_t GetConnectionCount() const { return connections_.size(); }
virtual bool GetIceProtocolType(IceProtocolType* type) const;
virtual void SetIceProtocolType(IceProtocolType type);
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd);
virtual void SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd);
virtual void SetRemoteIceMode(IceMode mode);
virtual void Connect();
virtual void Reset();
virtual void OnSignalingReady();
virtual void OnCandidate(const Candidate& candidate);
// From TransportChannel:
virtual int SendPacket(const char *data, size_t len,
const rtc::PacketOptions& options, int flags);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetError() { return error_; }
virtual bool GetStats(std::vector<ConnectionInfo>* stats);
const Connection* best_connection() const { return best_connection_; }
void set_incoming_only(bool value) { incoming_only_ = value; }
// Note: This is only for testing purpose.
// |ports_| should not be changed from outside.
const std::vector<PortInterface *>& ports() { return ports_; }
IceMode remote_ice_mode() const { return remote_ice_mode_; }
// DTLS methods.
virtual bool IsDtlsActive() const { return false; }
// Default implementation.
virtual bool GetSslRole(rtc::SSLRole* role) const {
return false;
}
virtual bool SetSslRole(rtc::SSLRole role) {
return false;
}
// Set up the ciphers to use for DTLS-SRTP.
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) {
return false;
}
// Find out which DTLS-SRTP cipher was negotiated
virtual bool GetSrtpCipher(std::string* cipher) {
return false;
}
// Returns false because the channel is not encrypted by default.
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
return false;
}
virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const {
return false;
}
// Allows key material to be extracted for external encryption.
virtual bool ExportKeyingMaterial(
const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len) {
return false;
}
virtual bool SetLocalIdentity(rtc::SSLIdentity* identity) {
return false;
}
// Set DTLS Remote fingerprint. Must be after local identity set.
virtual bool SetRemoteFingerprint(
const std::string& digest_alg,
const uint8* digest,
size_t digest_len) {
return false;
}
// Helper method used only in unittest.
rtc::DiffServCodePoint DefaultDscpValue() const;
private:
rtc::Thread* thread() { return worker_thread_; }
PortAllocatorSession* allocator_session() {
return allocator_sessions_.back();
}
void Allocate();
void UpdateConnectionStates();
void RequestSort();
void SortConnections();
void SwitchBestConnectionTo(Connection* conn);
void UpdateChannelState();
void HandleWritable();
void HandleNotWritable();
void HandleAllTimedOut();
Connection* GetBestConnectionOnNetwork(rtc::Network* network);
bool CreateConnections(const Candidate &remote_candidate,
PortInterface* origin_port, bool readable);
bool CreateConnection(PortInterface* port, const Candidate& remote_candidate,
PortInterface* origin_port, bool readable);
bool FindConnection(cricket::Connection* connection) const;
uint32 GetRemoteCandidateGeneration(const Candidate& candidate);
bool IsDuplicateRemoteCandidate(const Candidate& candidate);
void RememberRemoteCandidate(const Candidate& remote_candidate,
PortInterface* origin_port);
bool IsPingable(Connection* conn);
Connection* FindNextPingableConnection();
void PingConnection(Connection* conn);
void AddAllocatorSession(PortAllocatorSession* session);
void AddConnection(Connection* connection);
void OnPortReady(PortAllocatorSession *session, PortInterface* port);
void OnCandidatesReady(PortAllocatorSession *session,
const std::vector<Candidate>& candidates);
void OnCandidatesAllocationDone(PortAllocatorSession* session);
void OnUnknownAddress(PortInterface* port,
const rtc::SocketAddress& addr,
ProtocolType proto,
IceMessage* stun_msg,
const std::string& remote_username,
bool port_muxed);
void OnPortDestroyed(PortInterface* port);
void OnRoleConflict(PortInterface* port);
void OnConnectionStateChange(Connection* connection);
void OnReadPacket(Connection *connection, const char *data, size_t len,
const rtc::PacketTime& packet_time);
void OnReadyToSend(Connection* connection);
void OnConnectionDestroyed(Connection *connection);
void OnUseCandidate(Connection* conn);
virtual void OnMessage(rtc::Message *pmsg);
void OnSort();
void OnPing();
P2PTransport* transport_;
PortAllocator *allocator_;
rtc::Thread *worker_thread_;
bool incoming_only_;
bool waiting_for_signaling_;
int error_;
std::vector<PortAllocatorSession*> allocator_sessions_;
std::vector<PortInterface *> ports_;
std::vector<Connection *> connections_;
Connection* best_connection_;
// Connection selected by the controlling agent. This should be used only
// at controlled side when protocol type is RFC5245.
Connection* pending_best_connection_;
std::vector<RemoteCandidate> remote_candidates_;
bool sort_dirty_; // indicates whether another sort is needed right now
bool was_writable_;
typedef std::map<rtc::Socket::Option, int> OptionMap;
OptionMap options_;
std::string ice_ufrag_;
std::string ice_pwd_;
std::string remote_ice_ufrag_;
std::string remote_ice_pwd_;
IceProtocolType protocol_type_;
IceMode remote_ice_mode_;
IceRole ice_role_;
uint64 tiebreaker_;
uint32 remote_candidate_generation_;
DISALLOW_EVIL_CONSTRUCTORS(P2PTransportChannel);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_P2PTRANSPORTCHANNEL_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +0,0 @@
/*
* libjingle
* Copyright 2011, 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.
*/
#ifndef WEBRTC_P2P_BASE_PACKETSOCKETFACTORY_H_
#define WEBRTC_P2P_BASE_PACKETSOCKETFACTORY_H_
#include "webrtc/base/proxyinfo.h"
namespace rtc {
class AsyncPacketSocket;
class AsyncResolverInterface;
class PacketSocketFactory {
public:
enum Options {
OPT_SSLTCP = 0x01, // Pseudo-TLS.
OPT_TLS = 0x02,
OPT_STUN = 0x04,
};
PacketSocketFactory() { }
virtual ~PacketSocketFactory() { }
virtual AsyncPacketSocket* CreateUdpSocket(
const SocketAddress& address, int min_port, int max_port) = 0;
virtual AsyncPacketSocket* CreateServerTcpSocket(
const SocketAddress& local_address, int min_port, int max_port,
int opts) = 0;
// TODO: |proxy_info| and |user_agent| should be set
// per-factory and not when socket is created.
virtual AsyncPacketSocket* CreateClientTcpSocket(
const SocketAddress& local_address, const SocketAddress& remote_address,
const ProxyInfo& proxy_info, const std::string& user_agent, int opts) = 0;
virtual AsyncResolverInterface* CreateAsyncResolver() = 0;
private:
DISALLOW_EVIL_CONSTRUCTORS(PacketSocketFactory);
};
} // namespace rtc
#endif // WEBRTC_P2P_BASE_PACKETSOCKETFACTORY_H_

View File

@ -1,158 +0,0 @@
/*
* libjingle
* Copyright 2010, 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 "webrtc/p2p/base/parsing.h"
#include <stdlib.h>
#include <algorithm>
#include "webrtc/base/stringutils.h"
namespace {
static const char kTrue[] = "true";
static const char kOne[] = "1";
}
namespace cricket {
bool BadParse(const std::string& text, ParseError* err) {
if (err != NULL) {
err->text = text;
}
return false;
}
bool BadWrite(const std::string& text, WriteError* err) {
if (err != NULL) {
err->text = text;
}
return false;
}
std::string GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
const std::string& def) {
std::string val = elem->Attr(name);
return val.empty() ? def : val;
}
std::string GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
const char* def) {
return GetXmlAttr(elem, name, std::string(def));
}
bool GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name, bool def) {
std::string val = elem->Attr(name);
std::transform(val.begin(), val.end(), val.begin(), tolower);
return val.empty() ? def : (val == kTrue || val == kOne);
}
int GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name, int def) {
std::string val = elem->Attr(name);
return val.empty() ? def : atoi(val.c_str());
}
const buzz::XmlElement* GetXmlChild(const buzz::XmlElement* parent,
const std::string& name) {
for (const buzz::XmlElement* child = parent->FirstElement();
child != NULL;
child = child->NextElement()) {
if (child->Name().LocalPart() == name) {
return child;
}
}
return NULL;
}
bool RequireXmlChild(const buzz::XmlElement* parent,
const std::string& name,
const buzz::XmlElement** child,
ParseError* error) {
*child = GetXmlChild(parent, name);
if (*child == NULL) {
return BadParse("element '" + parent->Name().Merged() +
"' missing required child '" + name,
error);
} else {
return true;
}
}
bool RequireXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
std::string* value,
ParseError* error) {
if (!elem->HasAttr(name)) {
return BadParse("element '" + elem->Name().Merged() +
"' missing required attribute '"
+ name.Merged() + "'",
error);
} else {
*value = elem->Attr(name);
return true;
}
}
void AddXmlAttrIfNonEmpty(buzz::XmlElement* elem,
const buzz::QName name,
const std::string& value) {
if (!value.empty()) {
elem->AddAttr(name, value);
}
}
void AddXmlChildren(buzz::XmlElement* parent,
const std::vector<buzz::XmlElement*>& children) {
for (std::vector<buzz::XmlElement*>::const_iterator iter = children.begin();
iter != children.end();
iter++) {
parent->AddElement(*iter);
}
}
void CopyXmlChildren(const buzz::XmlElement* source, buzz::XmlElement* dest) {
for (const buzz::XmlElement* child = source->FirstElement();
child != NULL;
child = child->NextElement()) {
dest->AddElement(new buzz::XmlElement(*child));
}
}
std::vector<buzz::XmlElement*> CopyOfXmlChildren(const buzz::XmlElement* elem) {
std::vector<buzz::XmlElement*> children;
for (const buzz::XmlElement* child = elem->FirstElement();
child != NULL;
child = child->NextElement()) {
children.push_back(new buzz::XmlElement(*child));
}
return children;
}
} // namespace cricket

View File

@ -1,157 +0,0 @@
/*
* libjingle
* Copyright 2010, 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.
*/
#ifndef WEBRTC_P2P_BASE_PARSING_H_
#define WEBRTC_P2P_BASE_PARSING_H_
#include <string>
#include <vector>
#include "webrtc/libjingle/xmllite/xmlelement.h" // Needed to delete ParseError.extra.
#include "webrtc/base/basictypes.h"
#include "webrtc/base/stringencode.h"
namespace cricket {
typedef std::vector<buzz::XmlElement*> XmlElements;
// We decided "bool Parse(in, out*, error*)" is generally the best
// parse signature. "out Parse(in)" doesn't allow for errors.
// "error* Parse(in, out*)" doesn't allow flexible memory management.
// The error type for parsing.
struct ParseError {
public:
// explains the error
std::string text;
// provide details about what wasn't parsable
const buzz::XmlElement* extra;
ParseError() : extra(NULL) {}
~ParseError() {
delete extra;
}
void SetText(const std::string& text) {
this->text = text;
}
};
// The error type for writing.
struct WriteError {
std::string text;
void SetText(const std::string& text) {
this->text = text;
}
};
// Convenience method for returning a message when parsing fails.
bool BadParse(const std::string& text, ParseError* err);
// Convenience method for returning a message when writing fails.
bool BadWrite(const std::string& text, WriteError* error);
// helper XML functions
std::string GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
const std::string& def);
std::string GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
const char* def);
// Return true if the value is "true" or "1".
bool GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name, bool def);
int GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name, int def);
template <class T>
bool GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
T* val_out) {
if (!elem->HasAttr(name)) {
return false;
}
std::string unparsed = elem->Attr(name);
return rtc::FromString(unparsed, val_out);
}
template <class T>
bool GetXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
const T& def,
T* val_out) {
if (!elem->HasAttr(name)) {
*val_out = def;
return true;
}
return GetXmlAttr(elem, name, val_out);
}
template <class T>
bool AddXmlAttr(buzz::XmlElement* elem,
const buzz::QName& name, const T& val) {
std::string buf;
if (!rtc::ToString(val, &buf)) {
return false;
}
elem->AddAttr(name, buf);
return true;
}
template <class T>
bool SetXmlBody(buzz::XmlElement* elem, const T& val) {
std::string buf;
if (!rtc::ToString(val, &buf)) {
return false;
}
elem->SetBodyText(buf);
return true;
}
const buzz::XmlElement* GetXmlChild(const buzz::XmlElement* parent,
const std::string& name);
bool RequireXmlChild(const buzz::XmlElement* parent,
const std::string& name,
const buzz::XmlElement** child,
ParseError* error);
bool RequireXmlAttr(const buzz::XmlElement* elem,
const buzz::QName& name,
std::string* value,
ParseError* error);
void AddXmlAttrIfNonEmpty(buzz::XmlElement* elem,
const buzz::QName name,
const std::string& value);
void AddXmlChildren(buzz::XmlElement* parent,
const std::vector<buzz::XmlElement*>& children);
void CopyXmlChildren(const buzz::XmlElement* source, buzz::XmlElement* dest);
std::vector<buzz::XmlElement*> CopyOfXmlChildren(const buzz::XmlElement* elem);
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PARSING_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,619 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_PORT_H_
#define WEBRTC_P2P_BASE_PORT_H_
#include <map>
#include <set>
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/packetsocketfactory.h"
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/p2p/base/stun.h"
#include "webrtc/p2p/base/stunrequest.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/network.h"
#include "webrtc/base/proxyinfo.h"
#include "webrtc/base/ratetracker.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/thread.h"
namespace cricket {
class Connection;
class ConnectionRequest;
extern const char LOCAL_PORT_TYPE[];
extern const char STUN_PORT_TYPE[];
extern const char PRFLX_PORT_TYPE[];
extern const char RELAY_PORT_TYPE[];
extern const char UDP_PROTOCOL_NAME[];
extern const char TCP_PROTOCOL_NAME[];
extern const char SSLTCP_PROTOCOL_NAME[];
// RFC 6544, TCP candidate encoding rules.
extern const int DISCARD_PORT;
extern const char TCPTYPE_ACTIVE_STR[];
extern const char TCPTYPE_PASSIVE_STR[];
extern const char TCPTYPE_SIMOPEN_STR[];
// The length of time we wait before timing out readability on a connection.
const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds
// The length of time we wait before timing out writability on a connection.
const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds
// The length of time we wait before we become unwritable.
const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds
// The number of pings that must fail to respond before we become unwritable.
const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5;
// This is the length of time that we wait for a ping response to come back.
const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds
enum RelayType {
RELAY_GTURN, // Legacy google relay service.
RELAY_TURN // Standard (TURN) relay service.
};
enum IcePriorityValue {
// The reason we are choosing Relay preference 2 is because, we can run
// Relay from client to server on UDP/TCP/TLS. To distinguish the transport
// protocol, we prefer UDP over TCP over TLS.
// For UDP ICE_TYPE_PREFERENCE_RELAY will be 2.
// For TCP ICE_TYPE_PREFERENCE_RELAY will be 1.
// For TLS ICE_TYPE_PREFERENCE_RELAY will be 0.
// Check turnport.cc for setting these values.
ICE_TYPE_PREFERENCE_RELAY = 2,
ICE_TYPE_PREFERENCE_HOST_TCP = 90,
ICE_TYPE_PREFERENCE_SRFLX = 100,
ICE_TYPE_PREFERENCE_PRFLX = 110,
ICE_TYPE_PREFERENCE_HOST = 126
};
const char* ProtoToString(ProtocolType proto);
bool StringToProto(const char* value, ProtocolType* proto);
struct ProtocolAddress {
rtc::SocketAddress address;
ProtocolType proto;
bool secure;
ProtocolAddress(const rtc::SocketAddress& a, ProtocolType p)
: address(a), proto(p), secure(false) { }
ProtocolAddress(const rtc::SocketAddress& a, ProtocolType p, bool sec)
: address(a), proto(p), secure(sec) { }
};
typedef std::set<rtc::SocketAddress> ServerAddresses;
// Represents a local communication mechanism that can be used to create
// connections to similar mechanisms of the other client. Subclasses of this
// one add support for specific mechanisms like local UDP ports.
class Port : public PortInterface, public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
Port(rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
const std::string& username_fragment, const std::string& password);
Port(rtc::Thread* thread, const std::string& type,
rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port, const std::string& username_fragment,
const std::string& password);
virtual ~Port();
virtual const std::string& Type() const { return type_; }
virtual rtc::Network* Network() const { return network_; }
// This method will set the flag which enables standard ICE/STUN procedures
// in STUN connectivity checks. Currently this method does
// 1. Add / Verify MI attribute in STUN binding requests.
// 2. Username attribute in STUN binding request will be RFRAF:LFRAG,
// as opposed to RFRAGLFRAG.
virtual void SetIceProtocolType(IceProtocolType protocol) {
ice_protocol_ = protocol;
}
virtual IceProtocolType IceProtocol() const { return ice_protocol_; }
// Methods to set/get ICE role and tiebreaker values.
IceRole GetIceRole() const { return ice_role_; }
void SetIceRole(IceRole role) { ice_role_ = role; }
void SetIceTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; }
uint64 IceTiebreaker() const { return tiebreaker_; }
virtual bool SharedSocket() const { return shared_socket_; }
void ResetSharedSocket() { shared_socket_ = false; }
// The thread on which this port performs its I/O.
rtc::Thread* thread() { return thread_; }
// The factory used to create the sockets of this port.
rtc::PacketSocketFactory* socket_factory() const { return factory_; }
void set_socket_factory(rtc::PacketSocketFactory* factory) {
factory_ = factory;
}
// For debugging purposes.
const std::string& content_name() const { return content_name_; }
void set_content_name(const std::string& content_name) {
content_name_ = content_name;
}
int component() const { return component_; }
void set_component(int component) { component_ = component; }
bool send_retransmit_count_attribute() const {
return send_retransmit_count_attribute_;
}
void set_send_retransmit_count_attribute(bool enable) {
send_retransmit_count_attribute_ = enable;
}
// Identifies the generation that this port was created in.
uint32 generation() { return generation_; }
void set_generation(uint32 generation) { generation_ = generation; }
// ICE requires a single username/password per content/media line. So the
// |ice_username_fragment_| of the ports that belongs to the same content will
// be the same. However this causes a small complication with our relay
// server, which expects different username for RTP and RTCP.
//
// To resolve this problem, we implemented the username_fragment(),
// which returns a different username (calculated from
// |ice_username_fragment_|) for RTCP in the case of ICEPROTO_GOOGLE. And the
// username_fragment() simply returns |ice_username_fragment_| when running
// in ICEPROTO_RFC5245.
//
// As a result the ICEPROTO_GOOGLE will use different usernames for RTP and
// RTCP. And the ICEPROTO_RFC5245 will use same username for both RTP and
// RTCP.
const std::string username_fragment() const;
const std::string& password() const { return password_; }
// Fired when candidates are discovered by the port. When all candidates
// are discovered that belong to port SignalAddressReady is fired.
sigslot::signal2<Port*, const Candidate&> SignalCandidateReady;
// Provides all of the above information in one handy object.
virtual const std::vector<Candidate>& Candidates() const {
return candidates_;
}
// SignalPortComplete is sent when port completes the task of candidates
// allocation.
sigslot::signal1<Port*> SignalPortComplete;
// This signal sent when port fails to allocate candidates and this port
// can't be used in establishing the connections. When port is in shared mode
// and port fails to allocate one of the candidates, port shouldn't send
// this signal as other candidates might be usefull in establishing the
// connection.
sigslot::signal1<Port*> SignalPortError;
// Returns a map containing all of the connections of this port, keyed by the
// remote address.
typedef std::map<rtc::SocketAddress, Connection*> AddressMap;
const AddressMap& connections() { return connections_; }
// Returns the connection to the given address or NULL if none exists.
virtual Connection* GetConnection(
const rtc::SocketAddress& remote_addr);
// Called each time a connection is created.
sigslot::signal2<Port*, Connection*> SignalConnectionCreated;
// In a shared socket mode each port which shares the socket will decide
// to accept the packet based on the |remote_addr|. Currently only UDP
// port implemented this method.
// TODO(mallinath) - Make it pure virtual.
virtual bool HandleIncomingPacket(
rtc::AsyncPacketSocket* socket, const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
ASSERT(false);
return false;
}
// Sends a response message (normal or error) to the given request. One of
// these methods should be called as a response to SignalUnknownAddress.
// NOTE: You MUST call CreateConnection BEFORE SendBindingResponse.
virtual void SendBindingResponse(StunMessage* request,
const rtc::SocketAddress& addr);
virtual void SendBindingErrorResponse(
StunMessage* request, const rtc::SocketAddress& addr,
int error_code, const std::string& reason);
void set_proxy(const std::string& user_agent,
const rtc::ProxyInfo& proxy) {
user_agent_ = user_agent;
proxy_ = proxy;
}
const std::string& user_agent() { return user_agent_; }
const rtc::ProxyInfo& proxy() { return proxy_; }
virtual void EnablePortPackets();
// Called if the port has no connections and is no longer useful.
void Destroy();
virtual void OnMessage(rtc::Message *pmsg);
// Debugging description of this port
virtual std::string ToString() const;
rtc::IPAddress& ip() { return ip_; }
int min_port() { return min_port_; }
int max_port() { return max_port_; }
// Timeout shortening function to speed up unit tests.
void set_timeout_delay(int delay) { timeout_delay_ = delay; }
// This method will return local and remote username fragements from the
// stun username attribute if present.
bool ParseStunUsername(const StunMessage* stun_msg,
std::string* local_username,
std::string* remote_username,
IceProtocolType* remote_protocol_type) const;
void CreateStunUsername(const std::string& remote_username,
std::string* stun_username_attr_str) const;
bool MaybeIceRoleConflict(const rtc::SocketAddress& addr,
IceMessage* stun_msg,
const std::string& remote_ufrag);
// Called when the socket is currently able to send.
void OnReadyToSend();
// Called when the Connection discovers a local peer reflexive candidate.
// Returns the index of the new local candidate.
size_t AddPrflxCandidate(const Candidate& local);
// Returns if RFC 5245 ICE protocol is used.
bool IsStandardIce() const;
// Returns if Google ICE protocol is used.
bool IsGoogleIce() const;
// Returns if Hybrid ICE protocol is used.
bool IsHybridIce() const;
void set_candidate_filter(uint32 candidate_filter) {
candidate_filter_ = candidate_filter;
}
protected:
enum {
MSG_CHECKTIMEOUT = 0,
MSG_FIRST_AVAILABLE
};
void set_type(const std::string& type) { type_ = type; }
void AddAddress(const rtc::SocketAddress& address,
const rtc::SocketAddress& base_address,
const rtc::SocketAddress& related_address,
const std::string& protocol, const std::string& tcptype,
const std::string& type, uint32 type_preference,
uint32 relay_preference, bool final);
// Adds the given connection to the list. (Deleting removes them.)
void AddConnection(Connection* conn);
// Called when a packet is received from an unknown address that is not
// currently a connection. If this is an authenticated STUN binding request,
// then we will signal the client.
void OnReadPacket(const char* data, size_t size,
const rtc::SocketAddress& addr,
ProtocolType proto);
// If the given data comprises a complete and correct STUN message then the
// return value is true, otherwise false. If the message username corresponds
// with this port's username fragment, msg will contain the parsed STUN
// message. Otherwise, the function may send a STUN response internally.
// remote_username contains the remote fragment of the STUN username.
bool GetStunMessage(const char* data, size_t size,
const rtc::SocketAddress& addr,
IceMessage** out_msg, std::string* out_username);
// Checks if the address in addr is compatible with the port's ip.
bool IsCompatibleAddress(const rtc::SocketAddress& addr);
// Returns default DSCP value.
rtc::DiffServCodePoint DefaultDscpValue() const {
// No change from what MediaChannel set.
return rtc::DSCP_NO_CHANGE;
}
uint32 candidate_filter() { return candidate_filter_; }
private:
void Construct();
// Called when one of our connections deletes itself.
void OnConnectionDestroyed(Connection* conn);
// Checks if this port is useless, and hence, should be destroyed.
void CheckTimeout();
rtc::Thread* thread_;
rtc::PacketSocketFactory* factory_;
std::string type_;
bool send_retransmit_count_attribute_;
rtc::Network* network_;
rtc::IPAddress ip_;
int min_port_;
int max_port_;
std::string content_name_;
int component_;
uint32 generation_;
// In order to establish a connection to this Port (so that real data can be
// sent through), the other side must send us a STUN binding request that is
// authenticated with this username_fragment and password.
// PortAllocatorSession will provide these username_fragment and password.
//
// Note: we should always use username_fragment() instead of using
// |ice_username_fragment_| directly. For the details see the comment on
// username_fragment().
std::string ice_username_fragment_;
std::string password_;
std::vector<Candidate> candidates_;
AddressMap connections_;
int timeout_delay_;
bool enable_port_packets_;
IceProtocolType ice_protocol_;
IceRole ice_role_;
uint64 tiebreaker_;
bool shared_socket_;
// Information to use when going through a proxy.
std::string user_agent_;
rtc::ProxyInfo proxy_;
// Candidate filter is pushed down to Port such that each Port could
// make its own decision on how to create candidates. For example,
// when IceTransportsType is set to relay, both RelayPort and
// TurnPort will hide raddr to avoid local address leakage.
uint32 candidate_filter_;
friend class Connection;
};
// Represents a communication link between a port on the local client and a
// port on the remote client.
class Connection : public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
enum State {
STATE_WAITING = 0, // Check has not been performed, Waiting pair on CL.
STATE_INPROGRESS, // Check has been sent, transaction is in progress.
STATE_SUCCEEDED, // Check already done, produced a successful result.
STATE_FAILED // Check for this connection failed.
};
virtual ~Connection();
// The local port where this connection sends and receives packets.
Port* port() { return port_; }
const Port* port() const { return port_; }
// Returns the description of the local port
virtual const Candidate& local_candidate() const;
// Returns the description of the remote port to which we communicate.
const Candidate& remote_candidate() const { return remote_candidate_; }
// Returns the pair priority.
uint64 priority() const;
enum ReadState {
STATE_READ_INIT = 0, // we have yet to receive a ping
STATE_READABLE = 1, // we have received pings recently
STATE_READ_TIMEOUT = 2, // we haven't received pings in a while
};
ReadState read_state() const { return read_state_; }
bool readable() const { return read_state_ == STATE_READABLE; }
enum WriteState {
STATE_WRITABLE = 0, // we have received ping responses recently
STATE_WRITE_UNRELIABLE = 1, // we have had a few ping failures
STATE_WRITE_INIT = 2, // we have yet to receive a ping response
STATE_WRITE_TIMEOUT = 3, // we have had a large number of ping failures
};
WriteState write_state() const { return write_state_; }
bool writable() const { return write_state_ == STATE_WRITABLE; }
// Determines whether the connection has finished connecting. This can only
// be false for TCP connections.
bool connected() const { return connected_; }
// Estimate of the round-trip time over this connection.
uint32 rtt() const { return rtt_; }
size_t sent_total_bytes();
size_t sent_bytes_second();
size_t recv_total_bytes();
size_t recv_bytes_second();
sigslot::signal1<Connection*> SignalStateChange;
// Sent when the connection has decided that it is no longer of value. It
// will delete itself immediately after this call.
sigslot::signal1<Connection*> SignalDestroyed;
// The connection can send and receive packets asynchronously. This matches
// the interface of AsyncPacketSocket, which may use UDP or TCP under the
// covers.
virtual int Send(const void* data, size_t size,
const rtc::PacketOptions& options) = 0;
// Error if Send() returns < 0
virtual int GetError() = 0;
sigslot::signal4<Connection*, const char*, size_t,
const rtc::PacketTime&> SignalReadPacket;
sigslot::signal1<Connection*> SignalReadyToSend;
// Called when a packet is received on this connection.
void OnReadPacket(const char* data, size_t size,
const rtc::PacketTime& packet_time);
// Called when the socket is currently able to send.
void OnReadyToSend();
// Called when a connection is determined to be no longer useful to us. We
// still keep it around in case the other side wants to use it. But we can
// safely stop pinging on it and we can allow it to time out if the other
// side stops using it as well.
bool pruned() const { return pruned_; }
void Prune();
bool use_candidate_attr() const { return use_candidate_attr_; }
void set_use_candidate_attr(bool enable);
void set_remote_ice_mode(IceMode mode) {
remote_ice_mode_ = mode;
}
// Makes the connection go away.
void Destroy();
// Checks that the state of this connection is up-to-date. The argument is
// the current time, which is compared against various timeouts.
void UpdateState(uint32 now);
// Called when this connection should try checking writability again.
uint32 last_ping_sent() const { return last_ping_sent_; }
void Ping(uint32 now);
// Called whenever a valid ping is received on this connection. This is
// public because the connection intercepts the first ping for us.
uint32 last_ping_received() const { return last_ping_received_; }
void ReceivedPing();
// Debugging description of this connection
std::string ToString() const;
std::string ToSensitiveString() const;
bool reported() const { return reported_; }
void set_reported(bool reported) { reported_ = reported;}
// This flag will be set if this connection is the chosen one for media
// transmission. This connection will send STUN ping with USE-CANDIDATE
// attribute.
sigslot::signal1<Connection*> SignalUseCandidate;
// Invoked when Connection receives STUN error response with 487 code.
void HandleRoleConflictFromPeer();
State state() const { return state_; }
IceMode remote_ice_mode() const { return remote_ice_mode_; }
protected:
// Constructs a new connection to the given remote port.
Connection(Port* port, size_t index, const Candidate& candidate);
// Called back when StunRequestManager has a stun packet to send
void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
// Callbacks from ConnectionRequest
void OnConnectionRequestResponse(ConnectionRequest* req,
StunMessage* response);
void OnConnectionRequestErrorResponse(ConnectionRequest* req,
StunMessage* response);
void OnConnectionRequestTimeout(ConnectionRequest* req);
// Changes the state and signals if necessary.
void set_read_state(ReadState value);
void set_write_state(WriteState value);
void set_state(State state);
void set_connected(bool value);
// Checks if this connection is useless, and hence, should be destroyed.
void CheckTimeout();
void OnMessage(rtc::Message *pmsg);
Port* port_;
size_t local_candidate_index_;
Candidate remote_candidate_;
ReadState read_state_;
WriteState write_state_;
bool connected_;
bool pruned_;
// By default |use_candidate_attr_| flag will be true,
// as we will be using agrressive nomination.
// But when peer is ice-lite, this flag "must" be initialized to false and
// turn on when connection becomes "best connection".
bool use_candidate_attr_;
IceMode remote_ice_mode_;
StunRequestManager requests_;
uint32 rtt_;
uint32 last_ping_sent_; // last time we sent a ping to the other side
uint32 last_ping_received_; // last time we received a ping from the other
// side
uint32 last_data_received_;
uint32 last_ping_response_received_;
std::vector<uint32> pings_since_last_response_;
rtc::RateTracker recv_rate_tracker_;
rtc::RateTracker send_rate_tracker_;
private:
void MaybeAddPrflxCandidate(ConnectionRequest* request,
StunMessage* response);
bool reported_;
State state_;
friend class Port;
friend class ConnectionRequest;
};
// ProxyConnection defers all the interesting work to the port
class ProxyConnection : public Connection {
public:
ProxyConnection(Port* port, size_t index, const Candidate& candidate);
virtual int Send(const void* data, size_t size,
const rtc::PacketOptions& options);
virtual int GetError() { return error_; }
private:
int error_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PORT_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,109 +0,0 @@
/*
* 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 "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/portallocatorsessionproxy.h"
namespace cricket {
PortAllocatorSession::PortAllocatorSession(const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd,
uint32 flags)
: content_name_(content_name),
component_(component),
flags_(flags),
generation_(0),
// If PORTALLOCATOR_ENABLE_SHARED_UFRAG flag is not enabled, ignore the
// incoming ufrag and pwd, which will cause each Port to generate one
// by itself.
username_(flags_ & PORTALLOCATOR_ENABLE_SHARED_UFRAG ? ice_ufrag : ""),
password_(flags_ & PORTALLOCATOR_ENABLE_SHARED_UFRAG ? ice_pwd : "") {
}
PortAllocator::~PortAllocator() {
for (SessionMuxerMap::iterator iter = muxers_.begin();
iter != muxers_.end(); ++iter) {
delete iter->second;
}
}
PortAllocatorSession* PortAllocator::CreateSession(
const std::string& sid,
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd) {
if (flags_ & PORTALLOCATOR_ENABLE_BUNDLE) {
// If we just use |sid| as key in identifying PortAllocatorSessionMuxer,
// ICE restart will not result in different candidates, as |sid| will
// be same. To yield different candiates we are using combination of
// |ice_ufrag| and |ice_pwd|.
// Ideally |ice_ufrag| and |ice_pwd| should change together, but
// there can be instances where only ice_pwd will be changed.
std::string key_str = ice_ufrag + ":" + ice_pwd;
PortAllocatorSessionMuxer* muxer = GetSessionMuxer(key_str);
if (!muxer) {
PortAllocatorSession* session_impl = CreateSessionInternal(
content_name, component, ice_ufrag, ice_pwd);
// Create PortAllocatorSessionMuxer object for |session_impl|.
muxer = new PortAllocatorSessionMuxer(session_impl);
muxer->SignalDestroyed.connect(
this, &PortAllocator::OnSessionMuxerDestroyed);
// Add PortAllocatorSession to the map.
muxers_[key_str] = muxer;
}
PortAllocatorSessionProxy* proxy =
new PortAllocatorSessionProxy(content_name, component, flags_);
muxer->RegisterSessionProxy(proxy);
return proxy;
}
return CreateSessionInternal(content_name, component, ice_ufrag, ice_pwd);
}
PortAllocatorSessionMuxer* PortAllocator::GetSessionMuxer(
const std::string& key) const {
SessionMuxerMap::const_iterator iter = muxers_.find(key);
if (iter != muxers_.end())
return iter->second;
return NULL;
}
void PortAllocator::OnSessionMuxerDestroyed(
PortAllocatorSessionMuxer* session) {
SessionMuxerMap::iterator iter;
for (iter = muxers_.begin(); iter != muxers_.end(); ++iter) {
if (iter->second == session)
break;
}
if (iter != muxers_.end())
muxers_.erase(iter);
}
} // namespace cricket

View File

@ -1,209 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_PORTALLOCATOR_H_
#define WEBRTC_P2P_BASE_PORTALLOCATOR_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/proxyinfo.h"
#include "webrtc/base/sigslot.h"
namespace cricket {
// PortAllocator is responsible for allocating Port types for a given
// P2PSocket. It also handles port freeing.
//
// Clients can override this class to control port allocation, including
// what kinds of ports are allocated.
enum {
PORTALLOCATOR_DISABLE_UDP = 0x01,
PORTALLOCATOR_DISABLE_STUN = 0x02,
PORTALLOCATOR_DISABLE_RELAY = 0x04,
PORTALLOCATOR_DISABLE_TCP = 0x08,
PORTALLOCATOR_ENABLE_SHAKER = 0x10,
PORTALLOCATOR_ENABLE_BUNDLE = 0x20,
PORTALLOCATOR_ENABLE_IPV6 = 0x40,
PORTALLOCATOR_ENABLE_SHARED_UFRAG = 0x80,
PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100,
PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200,
};
const uint32 kDefaultPortAllocatorFlags = 0;
const uint32 kDefaultStepDelay = 1000; // 1 sec step delay.
// As per RFC 5245 Appendix B.1, STUN transactions need to be paced at certain
// internal. Less than 20ms is not acceptable. We choose 50ms as our default.
const uint32 kMinimumStepDelay = 50;
// CF = CANDIDATE FILTER
enum {
CF_NONE = 0x0,
CF_HOST = 0x1,
CF_REFLEXIVE = 0x2,
CF_RELAY = 0x4,
CF_ALL = 0x7,
};
class PortAllocatorSessionMuxer;
class PortAllocatorSession : public sigslot::has_slots<> {
public:
// Content name passed in mostly for logging and debugging.
// TODO(mallinath) - Change username and password to ice_ufrag and ice_pwd.
PortAllocatorSession(const std::string& content_name,
int component,
const std::string& username,
const std::string& password,
uint32 flags);
// Subclasses should clean up any ports created.
virtual ~PortAllocatorSession() {}
uint32 flags() const { return flags_; }
void set_flags(uint32 flags) { flags_ = flags; }
std::string content_name() const { return content_name_; }
int component() const { return component_; }
// Starts gathering STUN and Relay configurations.
virtual void StartGettingPorts() = 0;
virtual void StopGettingPorts() = 0;
virtual bool IsGettingPorts() = 0;
sigslot::signal2<PortAllocatorSession*, PortInterface*> SignalPortReady;
sigslot::signal2<PortAllocatorSession*,
const std::vector<Candidate>&> SignalCandidatesReady;
sigslot::signal1<PortAllocatorSession*> SignalCandidatesAllocationDone;
virtual uint32 generation() { return generation_; }
virtual void set_generation(uint32 generation) { generation_ = generation; }
sigslot::signal1<PortAllocatorSession*> SignalDestroyed;
protected:
const std::string& username() const { return username_; }
const std::string& password() const { return password_; }
std::string content_name_;
int component_;
private:
uint32 flags_;
uint32 generation_;
std::string username_;
std::string password_;
};
class PortAllocator : public sigslot::has_slots<> {
public:
PortAllocator() :
flags_(kDefaultPortAllocatorFlags),
min_port_(0),
max_port_(0),
step_delay_(kDefaultStepDelay),
allow_tcp_listen_(true),
candidate_filter_(CF_ALL) {
// This will allow us to have old behavior on non webrtc clients.
}
virtual ~PortAllocator();
PortAllocatorSession* CreateSession(
const std::string& sid,
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd);
PortAllocatorSessionMuxer* GetSessionMuxer(const std::string& key) const;
void OnSessionMuxerDestroyed(PortAllocatorSessionMuxer* session);
uint32 flags() const { return flags_; }
void set_flags(uint32 flags) { flags_ = flags; }
const std::string& user_agent() const { return agent_; }
const rtc::ProxyInfo& proxy() const { return proxy_; }
void set_proxy(const std::string& agent, const rtc::ProxyInfo& proxy) {
agent_ = agent;
proxy_ = proxy;
}
// Gets/Sets the port range to use when choosing client ports.
int min_port() const { return min_port_; }
int max_port() const { return max_port_; }
bool SetPortRange(int min_port, int max_port) {
if (min_port > max_port) {
return false;
}
min_port_ = min_port;
max_port_ = max_port;
return true;
}
uint32 step_delay() const { return step_delay_; }
void set_step_delay(uint32 delay) {
step_delay_ = delay;
}
bool allow_tcp_listen() const { return allow_tcp_listen_; }
void set_allow_tcp_listen(bool allow_tcp_listen) {
allow_tcp_listen_ = allow_tcp_listen;
}
uint32 candidate_filter() { return candidate_filter_; }
bool set_candidate_filter(uint32 filter) {
// TODO(mallinath) - Do transition check?
candidate_filter_ = filter;
return true;
}
protected:
virtual PortAllocatorSession* CreateSessionInternal(
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd) = 0;
typedef std::map<std::string, PortAllocatorSessionMuxer*> SessionMuxerMap;
uint32 flags_;
std::string agent_;
rtc::ProxyInfo proxy_;
int min_port_;
int max_port_;
uint32 step_delay_;
SessionMuxerMap muxers_;
bool allow_tcp_listen_;
uint32 candidate_filter_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PORTALLOCATOR_H_

View File

@ -1,239 +0,0 @@
/*
* libjingle
* Copyright 2004--2011, 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 "webrtc/p2p/base/portallocatorsessionproxy.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/portproxy.h"
#include "webrtc/base/thread.h"
namespace cricket {
enum {
MSG_SEND_ALLOCATION_DONE = 1,
MSG_SEND_ALLOCATED_PORTS,
};
typedef rtc::TypedMessageData<PortAllocatorSessionProxy*> ProxyObjData;
PortAllocatorSessionMuxer::PortAllocatorSessionMuxer(
PortAllocatorSession* session)
: worker_thread_(rtc::Thread::Current()),
session_(session),
candidate_done_signal_received_(false) {
session_->SignalPortReady.connect(
this, &PortAllocatorSessionMuxer::OnPortReady);
session_->SignalCandidatesAllocationDone.connect(
this, &PortAllocatorSessionMuxer::OnCandidatesAllocationDone);
}
PortAllocatorSessionMuxer::~PortAllocatorSessionMuxer() {
for (size_t i = 0; i < session_proxies_.size(); ++i)
delete session_proxies_[i];
SignalDestroyed(this);
}
void PortAllocatorSessionMuxer::RegisterSessionProxy(
PortAllocatorSessionProxy* session_proxy) {
session_proxies_.push_back(session_proxy);
session_proxy->SignalDestroyed.connect(
this, &PortAllocatorSessionMuxer::OnSessionProxyDestroyed);
session_proxy->set_impl(session_.get());
// Populate new proxy session with the information available in the actual
// implementation.
if (!ports_.empty()) {
worker_thread_->Post(
this, MSG_SEND_ALLOCATED_PORTS, new ProxyObjData(session_proxy));
}
if (candidate_done_signal_received_) {
worker_thread_->Post(
this, MSG_SEND_ALLOCATION_DONE, new ProxyObjData(session_proxy));
}
}
void PortAllocatorSessionMuxer::OnCandidatesAllocationDone(
PortAllocatorSession* session) {
candidate_done_signal_received_ = true;
}
void PortAllocatorSessionMuxer::OnPortReady(PortAllocatorSession* session,
PortInterface* port) {
ASSERT(session == session_.get());
ports_.push_back(port);
port->SignalDestroyed.connect(
this, &PortAllocatorSessionMuxer::OnPortDestroyed);
}
void PortAllocatorSessionMuxer::OnPortDestroyed(PortInterface* port) {
std::vector<PortInterface*>::iterator it =
std::find(ports_.begin(), ports_.end(), port);
if (it != ports_.end())
ports_.erase(it);
}
void PortAllocatorSessionMuxer::OnSessionProxyDestroyed(
PortAllocatorSession* proxy) {
std::vector<PortAllocatorSessionProxy*>::iterator it =
std::find(session_proxies_.begin(), session_proxies_.end(), proxy);
if (it != session_proxies_.end()) {
session_proxies_.erase(it);
}
if (session_proxies_.empty()) {
// Destroy PortAllocatorSession and its associated muxer object if all
// proxies belonging to this session are already destroyed.
delete this;
}
}
void PortAllocatorSessionMuxer::OnMessage(rtc::Message *pmsg) {
ProxyObjData* proxy = static_cast<ProxyObjData*>(pmsg->pdata);
switch (pmsg->message_id) {
case MSG_SEND_ALLOCATION_DONE:
SendAllocationDone_w(proxy->data());
delete proxy;
break;
case MSG_SEND_ALLOCATED_PORTS:
SendAllocatedPorts_w(proxy->data());
delete proxy;
break;
default:
ASSERT(false);
break;
}
}
void PortAllocatorSessionMuxer::SendAllocationDone_w(
PortAllocatorSessionProxy* proxy) {
std::vector<PortAllocatorSessionProxy*>::iterator iter =
std::find(session_proxies_.begin(), session_proxies_.end(), proxy);
if (iter != session_proxies_.end()) {
proxy->OnCandidatesAllocationDone(session_.get());
}
}
void PortAllocatorSessionMuxer::SendAllocatedPorts_w(
PortAllocatorSessionProxy* proxy) {
std::vector<PortAllocatorSessionProxy*>::iterator iter =
std::find(session_proxies_.begin(), session_proxies_.end(), proxy);
if (iter != session_proxies_.end()) {
for (size_t i = 0; i < ports_.size(); ++i) {
PortInterface* port = ports_[i];
proxy->OnPortReady(session_.get(), port);
// If port already has candidates, send this to the clients of proxy
// session. This can happen if proxy is created later than the actual
// implementation.
if (!port->Candidates().empty()) {
proxy->OnCandidatesReady(session_.get(), port->Candidates());
}
}
}
}
PortAllocatorSessionProxy::~PortAllocatorSessionProxy() {
std::map<PortInterface*, PortProxy*>::iterator it;
for (it = proxy_ports_.begin(); it != proxy_ports_.end(); it++)
delete it->second;
SignalDestroyed(this);
}
void PortAllocatorSessionProxy::set_impl(
PortAllocatorSession* session) {
impl_ = session;
impl_->SignalCandidatesReady.connect(
this, &PortAllocatorSessionProxy::OnCandidatesReady);
impl_->SignalPortReady.connect(
this, &PortAllocatorSessionProxy::OnPortReady);
impl_->SignalCandidatesAllocationDone.connect(
this, &PortAllocatorSessionProxy::OnCandidatesAllocationDone);
}
void PortAllocatorSessionProxy::StartGettingPorts() {
ASSERT(impl_ != NULL);
// Since all proxies share a common PortAllocatorSession, this check will
// prohibit sending multiple STUN ping messages to the stun server, which
// is a problem on Chrome. GetInitialPorts() and StartGetAllPorts() called
// from the worker thread and are called together from TransportChannel,
// checking for IsGettingAllPorts() for GetInitialPorts() will not be a
// problem.
if (!impl_->IsGettingPorts()) {
impl_->StartGettingPorts();
}
}
void PortAllocatorSessionProxy::StopGettingPorts() {
ASSERT(impl_ != NULL);
if (impl_->IsGettingPorts()) {
impl_->StopGettingPorts();
}
}
bool PortAllocatorSessionProxy::IsGettingPorts() {
ASSERT(impl_ != NULL);
return impl_->IsGettingPorts();
}
void PortAllocatorSessionProxy::OnPortReady(PortAllocatorSession* session,
PortInterface* port) {
ASSERT(session == impl_);
PortProxy* proxy_port = new PortProxy();
proxy_port->set_impl(port);
proxy_ports_[port] = proxy_port;
SignalPortReady(this, proxy_port);
}
void PortAllocatorSessionProxy::OnCandidatesReady(
PortAllocatorSession* session,
const std::vector<Candidate>& candidates) {
ASSERT(session == impl_);
// Since all proxy sessions share a common PortAllocatorSession,
// all Candidates will have name associated with the common PAS.
// Change Candidate name with the PortAllocatorSessionProxy name.
std::vector<Candidate> our_candidates;
for (size_t i = 0; i < candidates.size(); ++i) {
Candidate new_local_candidate = candidates[i];
new_local_candidate.set_component(component_);
our_candidates.push_back(new_local_candidate);
}
SignalCandidatesReady(this, our_candidates);
}
void PortAllocatorSessionProxy::OnCandidatesAllocationDone(
PortAllocatorSession* session) {
ASSERT(session == impl_);
SignalCandidatesAllocationDone(this);
}
} // namespace cricket

View File

@ -1,123 +0,0 @@
/*
* libjingle
* Copyright 2004--2011, 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.
*/
#ifndef WEBRTC_P2P_BASE_PORTALLOCATORSESSIONPROXY_H_
#define WEBRTC_P2P_BASE_PORTALLOCATORSESSIONPROXY_H_
#include <string>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/portallocator.h"
namespace cricket {
class PortAllocator;
class PortAllocatorSessionProxy;
class PortProxy;
// This class maintains the list of cricket::Port* objects. Ports will be
// deleted upon receiving SignalDestroyed signal. This class is used when
// PORTALLOCATOR_ENABLE_BUNDLE flag is set.
class PortAllocatorSessionMuxer : public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
explicit PortAllocatorSessionMuxer(PortAllocatorSession* session);
virtual ~PortAllocatorSessionMuxer();
void RegisterSessionProxy(PortAllocatorSessionProxy* session_proxy);
void OnPortReady(PortAllocatorSession* session, PortInterface* port);
void OnPortDestroyed(PortInterface* port);
void OnCandidatesAllocationDone(PortAllocatorSession* session);
const std::vector<PortInterface*>& ports() { return ports_; }
sigslot::signal1<PortAllocatorSessionMuxer*> SignalDestroyed;
private:
virtual void OnMessage(rtc::Message *pmsg);
void OnSessionProxyDestroyed(PortAllocatorSession* proxy);
void SendAllocationDone_w(PortAllocatorSessionProxy* proxy);
void SendAllocatedPorts_w(PortAllocatorSessionProxy* proxy);
// Port will be deleted when SignalDestroyed received, otherwise delete
// happens when PortAllocatorSession dtor is called.
rtc::Thread* worker_thread_;
std::vector<PortInterface*> ports_;
rtc::scoped_ptr<PortAllocatorSession> session_;
std::vector<PortAllocatorSessionProxy*> session_proxies_;
bool candidate_done_signal_received_;
};
class PortAllocatorSessionProxy : public PortAllocatorSession {
public:
PortAllocatorSessionProxy(const std::string& content_name,
int component,
uint32 flags)
// Use empty string as the ufrag and pwd because the proxy always uses
// the ufrag and pwd from the underlying implementation.
: PortAllocatorSession(content_name, component, "", "", flags),
impl_(NULL) {
}
virtual ~PortAllocatorSessionProxy();
PortAllocatorSession* impl() { return impl_; }
void set_impl(PortAllocatorSession* session);
// Forwards call to the actual PortAllocatorSession.
virtual void StartGettingPorts();
virtual void StopGettingPorts();
virtual bool IsGettingPorts();
virtual void set_generation(uint32 generation) {
ASSERT(impl_ != NULL);
impl_->set_generation(generation);
}
virtual uint32 generation() {
ASSERT(impl_ != NULL);
return impl_->generation();
}
private:
void OnPortReady(PortAllocatorSession* session, PortInterface* port);
void OnCandidatesReady(PortAllocatorSession* session,
const std::vector<Candidate>& candidates);
void OnPortDestroyed(PortInterface* port);
void OnCandidatesAllocationDone(PortAllocatorSession* session);
// This is the actual PortAllocatorSession, owned by PortAllocator.
PortAllocatorSession* impl_;
std::map<PortInterface*, PortProxy*> proxy_ports_;
friend class PortAllocatorSessionMuxer;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PORTALLOCATORSESSIONPROXY_H_

View File

@ -1,163 +0,0 @@
/*
* libjingle
* Copyright 2012 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 <vector>
#include "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/portallocatorsessionproxy.h"
#include "webrtc/p2p/client/basicportallocator.h"
#include "webrtc/p2p/client/fakeportallocator.h"
#include "webrtc/base/fakenetwork.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/thread.h"
using cricket::Candidate;
using cricket::PortAllocatorSession;
using cricket::PortAllocatorSessionMuxer;
using cricket::PortAllocatorSessionProxy;
// Based on ICE_UFRAG_LENGTH
static const char kIceUfrag0[] = "TESTICEUFRAG0000";
// Based on ICE_PWD_LENGTH
static const char kIcePwd0[] = "TESTICEPWD00000000000000";
class TestSessionChannel : public sigslot::has_slots<> {
public:
explicit TestSessionChannel(PortAllocatorSessionProxy* proxy)
: proxy_session_(proxy),
candidates_count_(0),
allocation_complete_(false),
ports_count_(0) {
proxy_session_->SignalCandidatesAllocationDone.connect(
this, &TestSessionChannel::OnCandidatesAllocationDone);
proxy_session_->SignalCandidatesReady.connect(
this, &TestSessionChannel::OnCandidatesReady);
proxy_session_->SignalPortReady.connect(
this, &TestSessionChannel::OnPortReady);
}
virtual ~TestSessionChannel() {
delete proxy_session_;
}
void OnCandidatesReady(PortAllocatorSession* session,
const std::vector<Candidate>& candidates) {
EXPECT_EQ(proxy_session_, session);
candidates_count_ += static_cast<int>(candidates.size());
}
void OnCandidatesAllocationDone(PortAllocatorSession* session) {
EXPECT_EQ(proxy_session_, session);
allocation_complete_ = true;
}
void OnPortReady(PortAllocatorSession* session,
cricket::PortInterface* port) {
EXPECT_EQ(proxy_session_, session);
++ports_count_;
}
int candidates_count() { return candidates_count_; }
bool allocation_complete() { return allocation_complete_; }
int ports_count() { return ports_count_; }
void StartGettingPorts() {
proxy_session_->StartGettingPorts();
}
void StopGettingPorts() {
proxy_session_->StopGettingPorts();
}
bool IsGettingPorts() {
return proxy_session_->IsGettingPorts();
}
private:
PortAllocatorSessionProxy* proxy_session_;
int candidates_count_;
bool allocation_complete_;
int ports_count_;
};
class PortAllocatorSessionProxyTest : public testing::Test {
public:
PortAllocatorSessionProxyTest()
: socket_factory_(rtc::Thread::Current()),
allocator_(rtc::Thread::Current(), NULL),
session_(new cricket::FakePortAllocatorSession(
rtc::Thread::Current(), &socket_factory_,
"test content", 1,
kIceUfrag0, kIcePwd0)),
session_muxer_(new PortAllocatorSessionMuxer(session_)) {
}
virtual ~PortAllocatorSessionProxyTest() {}
void RegisterSessionProxy(PortAllocatorSessionProxy* proxy) {
session_muxer_->RegisterSessionProxy(proxy);
}
TestSessionChannel* CreateChannel() {
PortAllocatorSessionProxy* proxy =
new PortAllocatorSessionProxy("test content", 1, 0);
TestSessionChannel* channel = new TestSessionChannel(proxy);
session_muxer_->RegisterSessionProxy(proxy);
channel->StartGettingPorts();
return channel;
}
protected:
rtc::BasicPacketSocketFactory socket_factory_;
cricket::FakePortAllocator allocator_;
cricket::FakePortAllocatorSession* session_;
// Muxer object will be delete itself after all registered session proxies
// are deleted.
PortAllocatorSessionMuxer* session_muxer_;
};
TEST_F(PortAllocatorSessionProxyTest, TestBasic) {
TestSessionChannel* channel = CreateChannel();
EXPECT_EQ_WAIT(1, channel->candidates_count(), 1000);
EXPECT_EQ(1, channel->ports_count());
EXPECT_TRUE(channel->allocation_complete());
delete channel;
}
TEST_F(PortAllocatorSessionProxyTest, TestLateBinding) {
TestSessionChannel* channel1 = CreateChannel();
EXPECT_EQ_WAIT(1, channel1->candidates_count(), 1000);
EXPECT_EQ(1, channel1->ports_count());
EXPECT_TRUE(channel1->allocation_complete());
EXPECT_EQ(1, session_->port_config_count());
// Creating another PortAllocatorSessionProxy and it also should receive
// already happened events.
PortAllocatorSessionProxy* proxy =
new PortAllocatorSessionProxy("test content", 2, 0);
TestSessionChannel* channel2 = new TestSessionChannel(proxy);
session_muxer_->RegisterSessionProxy(proxy);
EXPECT_TRUE(channel2->IsGettingPorts());
EXPECT_EQ_WAIT(1, channel2->candidates_count(), 1000);
EXPECT_EQ(1, channel2->ports_count());
EXPECT_TRUE_WAIT(channel2->allocation_complete(), 1000);
EXPECT_EQ(1, session_->port_config_count());
delete channel1;
delete channel2;
}

View File

@ -1,143 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#ifndef WEBRTC_P2P_BASE_PORTINTERFACE_H_
#define WEBRTC_P2P_BASE_PORTINTERFACE_H_
#include <string>
#include "webrtc/p2p/base/transport.h"
#include "webrtc/base/socketaddress.h"
namespace rtc {
class Network;
struct PacketOptions;
}
namespace cricket {
class Connection;
class IceMessage;
class StunMessage;
enum ProtocolType {
PROTO_UDP,
PROTO_TCP,
PROTO_SSLTCP,
PROTO_LAST = PROTO_SSLTCP
};
// Defines the interface for a port, which represents a local communication
// mechanism that can be used to create connections to similar mechanisms of
// the other client. Various types of ports will implement this interface.
class PortInterface {
public:
virtual ~PortInterface() {}
virtual const std::string& Type() const = 0;
virtual rtc::Network* Network() const = 0;
virtual void SetIceProtocolType(IceProtocolType protocol) = 0;
virtual IceProtocolType IceProtocol() const = 0;
// Methods to set/get ICE role and tiebreaker values.
virtual void SetIceRole(IceRole role) = 0;
virtual IceRole GetIceRole() const = 0;
virtual void SetIceTiebreaker(uint64 tiebreaker) = 0;
virtual uint64 IceTiebreaker() const = 0;
virtual bool SharedSocket() const = 0;
// PrepareAddress will attempt to get an address for this port that other
// clients can send to. It may take some time before the address is ready.
// Once it is ready, we will send SignalAddressReady. If errors are
// preventing the port from getting an address, it may send
// SignalAddressError.
virtual void PrepareAddress() = 0;
// Returns the connection to the given address or NULL if none exists.
virtual Connection* GetConnection(
const rtc::SocketAddress& remote_addr) = 0;
// Creates a new connection to the given address.
enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE };
virtual Connection* CreateConnection(
const Candidate& remote_candidate, CandidateOrigin origin) = 0;
// Functions on the underlying socket(s).
virtual int SetOption(rtc::Socket::Option opt, int value) = 0;
virtual int GetOption(rtc::Socket::Option opt, int* value) = 0;
virtual int GetError() = 0;
virtual const std::vector<Candidate>& Candidates() const = 0;
// Sends the given packet to the given address, provided that the address is
// that of a connection or an address that has sent to us already.
virtual int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options, bool payload) = 0;
// Indicates that we received a successful STUN binding request from an
// address that doesn't correspond to any current connection. To turn this
// into a real connection, call CreateConnection.
sigslot::signal6<PortInterface*, const rtc::SocketAddress&,
ProtocolType, IceMessage*, const std::string&,
bool> SignalUnknownAddress;
// Sends a response message (normal or error) to the given request. One of
// these methods should be called as a response to SignalUnknownAddress.
// NOTE: You MUST call CreateConnection BEFORE SendBindingResponse.
virtual void SendBindingResponse(StunMessage* request,
const rtc::SocketAddress& addr) = 0;
virtual void SendBindingErrorResponse(
StunMessage* request, const rtc::SocketAddress& addr,
int error_code, const std::string& reason) = 0;
// Signaled when this port decides to delete itself because it no longer has
// any usefulness.
sigslot::signal1<PortInterface*> SignalDestroyed;
// Signaled when Port discovers ice role conflict with the peer.
sigslot::signal1<PortInterface*> SignalRoleConflict;
// Normally, packets arrive through a connection (or they result signaling of
// unknown address). Calling this method turns off delivery of packets
// through their respective connection and instead delivers every packet
// through this port.
virtual void EnablePortPackets() = 0;
sigslot::signal4<PortInterface*, const char*, size_t,
const rtc::SocketAddress&> SignalReadPacket;
virtual std::string ToString() const = 0;
protected:
PortInterface() {}
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PORTINTERFACE_H_

View File

@ -1,180 +0,0 @@
/*
* libjingle
* Copyright 2004--2011, 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 "webrtc/p2p/base/portproxy.h"
namespace cricket {
void PortProxy::set_impl(PortInterface* port) {
impl_ = port;
impl_->SignalUnknownAddress.connect(
this, &PortProxy::OnUnknownAddress);
impl_->SignalDestroyed.connect(this, &PortProxy::OnPortDestroyed);
impl_->SignalRoleConflict.connect(this, &PortProxy::OnRoleConflict);
}
const std::string& PortProxy::Type() const {
ASSERT(impl_ != NULL);
return impl_->Type();
}
rtc::Network* PortProxy::Network() const {
ASSERT(impl_ != NULL);
return impl_->Network();
}
void PortProxy::SetIceProtocolType(IceProtocolType protocol) {
ASSERT(impl_ != NULL);
impl_->SetIceProtocolType(protocol);
}
IceProtocolType PortProxy::IceProtocol() const {
ASSERT(impl_ != NULL);
return impl_->IceProtocol();
}
// Methods to set/get ICE role and tiebreaker values.
void PortProxy::SetIceRole(IceRole role) {
ASSERT(impl_ != NULL);
impl_->SetIceRole(role);
}
IceRole PortProxy::GetIceRole() const {
ASSERT(impl_ != NULL);
return impl_->GetIceRole();
}
void PortProxy::SetIceTiebreaker(uint64 tiebreaker) {
ASSERT(impl_ != NULL);
impl_->SetIceTiebreaker(tiebreaker);
}
uint64 PortProxy::IceTiebreaker() const {
ASSERT(impl_ != NULL);
return impl_->IceTiebreaker();
}
bool PortProxy::SharedSocket() const {
ASSERT(impl_ != NULL);
return impl_->SharedSocket();
}
void PortProxy::PrepareAddress() {
ASSERT(impl_ != NULL);
impl_->PrepareAddress();
}
Connection* PortProxy::CreateConnection(const Candidate& remote_candidate,
CandidateOrigin origin) {
ASSERT(impl_ != NULL);
return impl_->CreateConnection(remote_candidate, origin);
}
int PortProxy::SendTo(const void* data,
size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload) {
ASSERT(impl_ != NULL);
return impl_->SendTo(data, size, addr, options, payload);
}
int PortProxy::SetOption(rtc::Socket::Option opt,
int value) {
ASSERT(impl_ != NULL);
return impl_->SetOption(opt, value);
}
int PortProxy::GetOption(rtc::Socket::Option opt,
int* value) {
ASSERT(impl_ != NULL);
return impl_->GetOption(opt, value);
}
int PortProxy::GetError() {
ASSERT(impl_ != NULL);
return impl_->GetError();
}
const std::vector<Candidate>& PortProxy::Candidates() const {
ASSERT(impl_ != NULL);
return impl_->Candidates();
}
void PortProxy::SendBindingResponse(
StunMessage* request, const rtc::SocketAddress& addr) {
ASSERT(impl_ != NULL);
impl_->SendBindingResponse(request, addr);
}
Connection* PortProxy::GetConnection(
const rtc::SocketAddress& remote_addr) {
ASSERT(impl_ != NULL);
return impl_->GetConnection(remote_addr);
}
void PortProxy::SendBindingErrorResponse(
StunMessage* request, const rtc::SocketAddress& addr,
int error_code, const std::string& reason) {
ASSERT(impl_ != NULL);
impl_->SendBindingErrorResponse(request, addr, error_code, reason);
}
void PortProxy::EnablePortPackets() {
ASSERT(impl_ != NULL);
impl_->EnablePortPackets();
}
std::string PortProxy::ToString() const {
ASSERT(impl_ != NULL);
return impl_->ToString();
}
void PortProxy::OnUnknownAddress(
PortInterface *port,
const rtc::SocketAddress &addr,
ProtocolType proto,
IceMessage *stun_msg,
const std::string &remote_username,
bool port_muxed) {
ASSERT(port == impl_);
ASSERT(!port_muxed);
SignalUnknownAddress(this, addr, proto, stun_msg, remote_username, true);
}
void PortProxy::OnRoleConflict(PortInterface* port) {
ASSERT(port == impl_);
SignalRoleConflict(this);
}
void PortProxy::OnPortDestroyed(PortInterface* port) {
ASSERT(port == impl_);
// |port| will be destroyed in PortAllocatorSessionMuxer.
SignalDestroyed(this);
}
} // namespace cricket

View File

@ -1,104 +0,0 @@
/*
* libjingle
* Copyright 2004--2011, 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.
*/
#ifndef WEBRTC_P2P_BASE_PORTPROXY_H_
#define WEBRTC_P2P_BASE_PORTPROXY_H_
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/base/sigslot.h"
namespace rtc {
class Network;
}
namespace cricket {
class PortProxy : public PortInterface, public sigslot::has_slots<> {
public:
PortProxy() {}
virtual ~PortProxy() {}
PortInterface* impl() { return impl_; }
void set_impl(PortInterface* port);
virtual const std::string& Type() const;
virtual rtc::Network* Network() const;
virtual void SetIceProtocolType(IceProtocolType protocol);
virtual IceProtocolType IceProtocol() const;
// Methods to set/get ICE role and tiebreaker values.
virtual void SetIceRole(IceRole role);
virtual IceRole GetIceRole() const;
virtual void SetIceTiebreaker(uint64 tiebreaker);
virtual uint64 IceTiebreaker() const;
virtual bool SharedSocket() const;
// Forwards call to the actual Port.
virtual void PrepareAddress();
virtual Connection* CreateConnection(const Candidate& remote_candidate,
CandidateOrigin origin);
virtual Connection* GetConnection(
const rtc::SocketAddress& remote_addr);
virtual int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetOption(rtc::Socket::Option opt, int* value);
virtual int GetError();
virtual const std::vector<Candidate>& Candidates() const;
virtual void SendBindingResponse(StunMessage* request,
const rtc::SocketAddress& addr);
virtual void SendBindingErrorResponse(
StunMessage* request, const rtc::SocketAddress& addr,
int error_code, const std::string& reason);
virtual void EnablePortPackets();
virtual std::string ToString() const;
private:
void OnUnknownAddress(PortInterface *port,
const rtc::SocketAddress &addr,
ProtocolType proto,
IceMessage *stun_msg,
const std::string &remote_username,
bool port_muxed);
void OnRoleConflict(PortInterface* port);
void OnPortDestroyed(PortInterface* port);
PortInterface* impl_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PORTPROXY_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,258 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_PSEUDOTCP_H_
#define WEBRTC_P2P_BASE_PSEUDOTCP_H_
#include <list>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/stream.h"
namespace cricket {
//////////////////////////////////////////////////////////////////////
// IPseudoTcpNotify
//////////////////////////////////////////////////////////////////////
class PseudoTcp;
class IPseudoTcpNotify {
public:
// Notification of tcp events
virtual void OnTcpOpen(PseudoTcp* tcp) = 0;
virtual void OnTcpReadable(PseudoTcp* tcp) = 0;
virtual void OnTcpWriteable(PseudoTcp* tcp) = 0;
virtual void OnTcpClosed(PseudoTcp* tcp, uint32 error) = 0;
// Write the packet onto the network
enum WriteResult { WR_SUCCESS, WR_TOO_LARGE, WR_FAIL };
virtual WriteResult TcpWritePacket(PseudoTcp* tcp,
const char* buffer, size_t len) = 0;
protected:
virtual ~IPseudoTcpNotify() {}
};
//////////////////////////////////////////////////////////////////////
// PseudoTcp
//////////////////////////////////////////////////////////////////////
class PseudoTcp {
public:
static uint32 Now();
PseudoTcp(IPseudoTcpNotify* notify, uint32 conv);
virtual ~PseudoTcp();
int Connect();
int Recv(char* buffer, size_t len);
int Send(const char* buffer, size_t len);
void Close(bool force);
int GetError();
enum TcpState {
TCP_LISTEN, TCP_SYN_SENT, TCP_SYN_RECEIVED, TCP_ESTABLISHED, TCP_CLOSED
};
TcpState State() const { return m_state; }
// Call this when the PMTU changes.
void NotifyMTU(uint16 mtu);
// Call this based on timeout value returned from GetNextClock.
// It's ok to call this too frequently.
void NotifyClock(uint32 now);
// Call this whenever a packet arrives.
// Returns true if the packet was processed successfully.
bool NotifyPacket(const char * buffer, size_t len);
// Call this to determine the next time NotifyClock should be called.
// Returns false if the socket is ready to be destroyed.
bool GetNextClock(uint32 now, long& timeout);
// Call these to get/set option values to tailor this PseudoTcp
// instance's behaviour for the kind of data it will carry.
// If an unrecognized option is set or got, an assertion will fire.
//
// Setting options for OPT_RCVBUF or OPT_SNDBUF after Connect() is called
// will result in an assertion.
enum Option {
OPT_NODELAY, // Whether to enable Nagle's algorithm (0 == off)
OPT_ACKDELAY, // The Delayed ACK timeout (0 == off).
OPT_RCVBUF, // Set the receive buffer size, in bytes.
OPT_SNDBUF, // Set the send buffer size, in bytes.
};
void GetOption(Option opt, int* value);
void SetOption(Option opt, int value);
// Returns current congestion window in bytes.
uint32 GetCongestionWindow() const;
// Returns amount of data in bytes that has been sent, but haven't
// been acknowledged.
uint32 GetBytesInFlight() const;
// Returns number of bytes that were written in buffer and haven't
// been sent.
uint32 GetBytesBufferedNotSent() const;
// Returns current round-trip time estimate in milliseconds.
uint32 GetRoundTripTimeEstimateMs() const;
protected:
enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck };
struct Segment {
uint32 conv, seq, ack;
uint8 flags;
uint16 wnd;
const char * data;
uint32 len;
uint32 tsval, tsecr;
};
struct SSegment {
SSegment(uint32 s, uint32 l, bool c)
: seq(s), len(l), /*tstamp(0),*/ xmit(0), bCtrl(c) {
}
uint32 seq, len;
//uint32 tstamp;
uint8 xmit;
bool bCtrl;
};
typedef std::list<SSegment> SList;
struct RSegment {
uint32 seq, len;
};
uint32 queue(const char* data, uint32 len, bool bCtrl);
// Creates a packet and submits it to the network. This method can either
// send payload or just an ACK packet.
//
// |seq| is the sequence number of this packet.
// |flags| is the flags for sending this packet.
// |offset| is the offset to read from |m_sbuf|.
// |len| is the number of bytes to read from |m_sbuf| as payload. If this
// value is 0 then this is an ACK packet, otherwise this packet has payload.
IPseudoTcpNotify::WriteResult packet(uint32 seq, uint8 flags,
uint32 offset, uint32 len);
bool parse(const uint8* buffer, uint32 size);
void attemptSend(SendFlags sflags = sfNone);
void closedown(uint32 err = 0);
bool clock_check(uint32 now, long& nTimeout);
bool process(Segment& seg);
bool transmit(const SList::iterator& seg, uint32 now);
void adjustMTU();
protected:
// This method is used in test only to query receive buffer state.
bool isReceiveBufferFull() const;
// This method is only used in tests, to disable window scaling
// support for testing backward compatibility.
void disableWindowScale();
private:
// Queue the connect message with TCP options.
void queueConnectMessage();
// Parse TCP options in the header.
void parseOptions(const char* data, uint32 len);
// Apply a TCP option that has been read from the header.
void applyOption(char kind, const char* data, uint32 len);
// Apply window scale option.
void applyWindowScaleOption(uint8 scale_factor);
// Resize the send buffer with |new_size| in bytes.
void resizeSendBuffer(uint32 new_size);
// Resize the receive buffer with |new_size| in bytes. This call adjusts
// window scale factor |m_swnd_scale| accordingly.
void resizeReceiveBuffer(uint32 new_size);
IPseudoTcpNotify* m_notify;
enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown;
int m_error;
// TCB data
TcpState m_state;
uint32 m_conv;
bool m_bReadEnable, m_bWriteEnable, m_bOutgoing;
uint32 m_lasttraffic;
// Incoming data
typedef std::list<RSegment> RList;
RList m_rlist;
uint32 m_rbuf_len, m_rcv_nxt, m_rcv_wnd, m_lastrecv;
uint8 m_rwnd_scale; // Window scale factor.
rtc::FifoBuffer m_rbuf;
// Outgoing data
SList m_slist;
uint32 m_sbuf_len, m_snd_nxt, m_snd_wnd, m_lastsend, m_snd_una;
uint8 m_swnd_scale; // Window scale factor.
rtc::FifoBuffer m_sbuf;
// Maximum segment size, estimated protocol level, largest segment sent
uint32 m_mss, m_msslevel, m_largest, m_mtu_advise;
// Retransmit timer
uint32 m_rto_base;
// Timestamp tracking
uint32 m_ts_recent, m_ts_lastack;
// Round-trip calculation
uint32 m_rx_rttvar, m_rx_srtt, m_rx_rto;
// Congestion avoidance, Fast retransmit/recovery, Delayed ACKs
uint32 m_ssthresh, m_cwnd;
uint8 m_dup_acks;
uint32 m_recover;
uint32 m_t_ack;
// Configuration options
bool m_use_nagling;
uint32 m_ack_delay;
// This is used by unit tests to test backward compatibility of
// PseudoTcp implementations that don't support window scaling.
bool m_support_wnd_scale;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_PSEUDOTCP_H_

View File

@ -1,858 +0,0 @@
/*
* libjingle
* Copyright 2011 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 <vector>
#include "webrtc/p2p/base/pseudotcp.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/messagehandler.h"
#include "webrtc/base/stream.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/timeutils.h"
using cricket::PseudoTcp;
static const int kConnectTimeoutMs = 10000; // ~3 * default RTO of 3000ms
static const int kTransferTimeoutMs = 15000;
static const int kBlockSize = 4096;
class PseudoTcpForTest : public cricket::PseudoTcp {
public:
PseudoTcpForTest(cricket::IPseudoTcpNotify* notify, uint32 conv)
: PseudoTcp(notify, conv) {
}
bool isReceiveBufferFull() const {
return PseudoTcp::isReceiveBufferFull();
}
void disableWindowScale() {
PseudoTcp::disableWindowScale();
}
};
class PseudoTcpTestBase : public testing::Test,
public rtc::MessageHandler,
public cricket::IPseudoTcpNotify {
public:
PseudoTcpTestBase()
: local_(this, 1),
remote_(this, 1),
have_connected_(false),
have_disconnected_(false),
local_mtu_(65535),
remote_mtu_(65535),
delay_(0),
loss_(0) {
// Set use of the test RNG to get predictable loss patterns.
rtc::SetRandomTestMode(true);
}
~PseudoTcpTestBase() {
// Put it back for the next test.
rtc::SetRandomTestMode(false);
}
void SetLocalMtu(int mtu) {
local_.NotifyMTU(mtu);
local_mtu_ = mtu;
}
void SetRemoteMtu(int mtu) {
remote_.NotifyMTU(mtu);
remote_mtu_ = mtu;
}
void SetDelay(int delay) {
delay_ = delay;
}
void SetLoss(int percent) {
loss_ = percent;
}
void SetOptNagling(bool enable_nagles) {
local_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
remote_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
}
void SetOptAckDelay(int ack_delay) {
local_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
remote_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
}
void SetOptSndBuf(int size) {
local_.SetOption(PseudoTcp::OPT_SNDBUF, size);
remote_.SetOption(PseudoTcp::OPT_SNDBUF, size);
}
void SetRemoteOptRcvBuf(int size) {
remote_.SetOption(PseudoTcp::OPT_RCVBUF, size);
}
void SetLocalOptRcvBuf(int size) {
local_.SetOption(PseudoTcp::OPT_RCVBUF, size);
}
void DisableRemoteWindowScale() {
remote_.disableWindowScale();
}
void DisableLocalWindowScale() {
local_.disableWindowScale();
}
protected:
int Connect() {
int ret = local_.Connect();
if (ret == 0) {
UpdateLocalClock();
}
return ret;
}
void Close() {
local_.Close(false);
UpdateLocalClock();
}
enum { MSG_LPACKET, MSG_RPACKET, MSG_LCLOCK, MSG_RCLOCK, MSG_IOCOMPLETE,
MSG_WRITE};
virtual void OnTcpOpen(PseudoTcp* tcp) {
// Consider ourselves connected when the local side gets OnTcpOpen.
// OnTcpWriteable isn't fired at open, so we trigger it now.
LOG(LS_VERBOSE) << "Opened";
if (tcp == &local_) {
have_connected_ = true;
OnTcpWriteable(tcp);
}
}
// Test derived from the base should override
// virtual void OnTcpReadable(PseudoTcp* tcp)
// and
// virtual void OnTcpWritable(PseudoTcp* tcp)
virtual void OnTcpClosed(PseudoTcp* tcp, uint32 error) {
// Consider ourselves closed when the remote side gets OnTcpClosed.
// TODO: OnTcpClosed is only ever notified in case of error in
// the current implementation. Solicited close is not (yet) supported.
LOG(LS_VERBOSE) << "Closed";
EXPECT_EQ(0U, error);
if (tcp == &remote_) {
have_disconnected_ = true;
}
}
virtual WriteResult TcpWritePacket(PseudoTcp* tcp,
const char* buffer, size_t len) {
// Randomly drop the desired percentage of packets.
// Also drop packets that are larger than the configured MTU.
if (rtc::CreateRandomId() % 100 < static_cast<uint32>(loss_)) {
LOG(LS_VERBOSE) << "Randomly dropping packet, size=" << len;
} else if (len > static_cast<size_t>(
rtc::_min(local_mtu_, remote_mtu_))) {
LOG(LS_VERBOSE) << "Dropping packet that exceeds path MTU, size=" << len;
} else {
int id = (tcp == &local_) ? MSG_RPACKET : MSG_LPACKET;
std::string packet(buffer, len);
rtc::Thread::Current()->PostDelayed(delay_, this, id,
rtc::WrapMessageData(packet));
}
return WR_SUCCESS;
}
void UpdateLocalClock() { UpdateClock(&local_, MSG_LCLOCK); }
void UpdateRemoteClock() { UpdateClock(&remote_, MSG_RCLOCK); }
void UpdateClock(PseudoTcp* tcp, uint32 message) {
long interval = 0; // NOLINT
tcp->GetNextClock(PseudoTcp::Now(), interval);
interval = rtc::_max<int>(interval, 0L); // sometimes interval is < 0
rtc::Thread::Current()->Clear(this, message);
rtc::Thread::Current()->PostDelayed(interval, this, message);
}
virtual void OnMessage(rtc::Message* message) {
switch (message->message_id) {
case MSG_LPACKET: {
const std::string& s(
rtc::UseMessageData<std::string>(message->pdata));
local_.NotifyPacket(s.c_str(), s.size());
UpdateLocalClock();
break;
}
case MSG_RPACKET: {
const std::string& s(
rtc::UseMessageData<std::string>(message->pdata));
remote_.NotifyPacket(s.c_str(), s.size());
UpdateRemoteClock();
break;
}
case MSG_LCLOCK:
local_.NotifyClock(PseudoTcp::Now());
UpdateLocalClock();
break;
case MSG_RCLOCK:
remote_.NotifyClock(PseudoTcp::Now());
UpdateRemoteClock();
break;
default:
break;
}
delete message->pdata;
}
PseudoTcpForTest local_;
PseudoTcpForTest remote_;
rtc::MemoryStream send_stream_;
rtc::MemoryStream recv_stream_;
bool have_connected_;
bool have_disconnected_;
int local_mtu_;
int remote_mtu_;
int delay_;
int loss_;
};
class PseudoTcpTest : public PseudoTcpTestBase {
public:
void TestTransfer(int size) {
uint32 start, elapsed;
size_t received;
// Create some dummy data to send.
send_stream_.ReserveSize(size);
for (int i = 0; i < size; ++i) {
char ch = static_cast<char>(i);
send_stream_.Write(&ch, 1, NULL, NULL);
}
send_stream_.Rewind();
// Prepare the receive stream.
recv_stream_.ReserveSize(size);
// Connect and wait until connected.
start = rtc::Time();
EXPECT_EQ(0, Connect());
EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
// Sending will start from OnTcpWriteable and complete when all data has
// been received.
EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
elapsed = rtc::TimeSince(start);
recv_stream_.GetSize(&received);
// Ensure we closed down OK and we got the right data.
// TODO: Ensure the errors are cleared properly.
//EXPECT_EQ(0, local_.GetError());
//EXPECT_EQ(0, remote_.GetError());
EXPECT_EQ(static_cast<size_t>(size), received);
EXPECT_EQ(0, memcmp(send_stream_.GetBuffer(),
recv_stream_.GetBuffer(), size));
LOG(LS_INFO) << "Transferred " << received << " bytes in " << elapsed
<< " ms (" << size * 8 / elapsed << " Kbps)";
}
private:
// IPseudoTcpNotify interface
virtual void OnTcpReadable(PseudoTcp* tcp) {
// Stream bytes to the recv stream as they arrive.
if (tcp == &remote_) {
ReadData();
// TODO: OnTcpClosed() is currently only notified on error -
// there is no on-the-wire equivalent of TCP FIN.
// So we fake the notification when all the data has been read.
size_t received, required;
recv_stream_.GetPosition(&received);
send_stream_.GetSize(&required);
if (received == required)
OnTcpClosed(&remote_, 0);
}
}
virtual void OnTcpWriteable(PseudoTcp* tcp) {
// Write bytes from the send stream when we can.
// Shut down when we've sent everything.
if (tcp == &local_) {
LOG(LS_VERBOSE) << "Flow Control Lifted";
bool done;
WriteData(&done);
if (done) {
Close();
}
}
}
void ReadData() {
char block[kBlockSize];
size_t position;
int rcvd;
do {
rcvd = remote_.Recv(block, sizeof(block));
if (rcvd != -1) {
recv_stream_.Write(block, rcvd, NULL, NULL);
recv_stream_.GetPosition(&position);
LOG(LS_VERBOSE) << "Received: " << position;
}
} while (rcvd > 0);
}
void WriteData(bool* done) {
size_t position, tosend;
int sent;
char block[kBlockSize];
do {
send_stream_.GetPosition(&position);
if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
rtc::SR_EOS) {
sent = local_.Send(block, tosend);
UpdateLocalClock();
if (sent != -1) {
send_stream_.SetPosition(position + sent);
LOG(LS_VERBOSE) << "Sent: " << position + sent;
} else {
send_stream_.SetPosition(position);
LOG(LS_VERBOSE) << "Flow Controlled";
}
} else {
sent = static_cast<int>(tosend = 0);
}
} while (sent > 0);
*done = (tosend == 0);
}
private:
rtc::MemoryStream send_stream_;
rtc::MemoryStream recv_stream_;
};
class PseudoTcpTestPingPong : public PseudoTcpTestBase {
public:
PseudoTcpTestPingPong()
: iterations_remaining_(0),
sender_(NULL),
receiver_(NULL),
bytes_per_send_(0) {
}
void SetBytesPerSend(int bytes) {
bytes_per_send_ = bytes;
}
void TestPingPong(int size, int iterations) {
uint32 start, elapsed;
iterations_remaining_ = iterations;
receiver_ = &remote_;
sender_ = &local_;
// Create some dummy data to send.
send_stream_.ReserveSize(size);
for (int i = 0; i < size; ++i) {
char ch = static_cast<char>(i);
send_stream_.Write(&ch, 1, NULL, NULL);
}
send_stream_.Rewind();
// Prepare the receive stream.
recv_stream_.ReserveSize(size);
// Connect and wait until connected.
start = rtc::Time();
EXPECT_EQ(0, Connect());
EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
// Sending will start from OnTcpWriteable and stop when the required
// number of iterations have completed.
EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
elapsed = rtc::TimeSince(start);
LOG(LS_INFO) << "Performed " << iterations << " pings in "
<< elapsed << " ms";
}
private:
// IPseudoTcpNotify interface
virtual void OnTcpReadable(PseudoTcp* tcp) {
if (tcp != receiver_) {
LOG_F(LS_ERROR) << "unexpected OnTcpReadable";
return;
}
// Stream bytes to the recv stream as they arrive.
ReadData();
// If we've received the desired amount of data, rewind things
// and send it back the other way!
size_t position, desired;
recv_stream_.GetPosition(&position);
send_stream_.GetSize(&desired);
if (position == desired) {
if (receiver_ == &local_ && --iterations_remaining_ == 0) {
Close();
// TODO: Fake OnTcpClosed() on the receiver for now.
OnTcpClosed(&remote_, 0);
return;
}
PseudoTcp* tmp = receiver_;
receiver_ = sender_;
sender_ = tmp;
recv_stream_.Rewind();
send_stream_.Rewind();
OnTcpWriteable(sender_);
}
}
virtual void OnTcpWriteable(PseudoTcp* tcp) {
if (tcp != sender_)
return;
// Write bytes from the send stream when we can.
// Shut down when we've sent everything.
LOG(LS_VERBOSE) << "Flow Control Lifted";
WriteData();
}
void ReadData() {
char block[kBlockSize];
size_t position;
int rcvd;
do {
rcvd = receiver_->Recv(block, sizeof(block));
if (rcvd != -1) {
recv_stream_.Write(block, rcvd, NULL, NULL);
recv_stream_.GetPosition(&position);
LOG(LS_VERBOSE) << "Received: " << position;
}
} while (rcvd > 0);
}
void WriteData() {
size_t position, tosend;
int sent;
char block[kBlockSize];
do {
send_stream_.GetPosition(&position);
tosend = bytes_per_send_ ? bytes_per_send_ : sizeof(block);
if (send_stream_.Read(block, tosend, &tosend, NULL) !=
rtc::SR_EOS) {
sent = sender_->Send(block, tosend);
UpdateLocalClock();
if (sent != -1) {
send_stream_.SetPosition(position + sent);
LOG(LS_VERBOSE) << "Sent: " << position + sent;
} else {
send_stream_.SetPosition(position);
LOG(LS_VERBOSE) << "Flow Controlled";
}
} else {
sent = static_cast<int>(tosend = 0);
}
} while (sent > 0);
}
private:
int iterations_remaining_;
PseudoTcp* sender_;
PseudoTcp* receiver_;
int bytes_per_send_;
};
// Fill the receiver window until it is full, drain it and then
// fill it with the same amount. This is to test that receiver window
// contracts and enlarges correctly.
class PseudoTcpTestReceiveWindow : public PseudoTcpTestBase {
public:
// Not all the data are transfered, |size| just need to be big enough
// to fill up the receiver window twice.
void TestTransfer(int size) {
// Create some dummy data to send.
send_stream_.ReserveSize(size);
for (int i = 0; i < size; ++i) {
char ch = static_cast<char>(i);
send_stream_.Write(&ch, 1, NULL, NULL);
}
send_stream_.Rewind();
// Prepare the receive stream.
recv_stream_.ReserveSize(size);
// Connect and wait until connected.
EXPECT_EQ(0, Connect());
EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
rtc::Thread::Current()->Post(this, MSG_WRITE);
EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
ASSERT_EQ(2u, send_position_.size());
ASSERT_EQ(2u, recv_position_.size());
const size_t estimated_recv_window = EstimateReceiveWindowSize();
// The difference in consecutive send positions should equal the
// receive window size or match very closely. This verifies that receive
// window is open after receiver drained all the data.
const size_t send_position_diff = send_position_[1] - send_position_[0];
EXPECT_GE(1024u, estimated_recv_window - send_position_diff);
// Receiver drained the receive window twice.
EXPECT_EQ(2 * estimated_recv_window, recv_position_[1]);
}
virtual void OnMessage(rtc::Message* message) {
int message_id = message->message_id;
PseudoTcpTestBase::OnMessage(message);
switch (message_id) {
case MSG_WRITE: {
WriteData();
break;
}
default:
break;
}
}
uint32 EstimateReceiveWindowSize() const {
return static_cast<uint32>(recv_position_[0]);
}
uint32 EstimateSendWindowSize() const {
return static_cast<uint32>(send_position_[0] - recv_position_[0]);
}
private:
// IPseudoTcpNotify interface
virtual void OnTcpReadable(PseudoTcp* tcp) {
}
virtual void OnTcpWriteable(PseudoTcp* tcp) {
}
void ReadUntilIOPending() {
char block[kBlockSize];
size_t position;
int rcvd;
do {
rcvd = remote_.Recv(block, sizeof(block));
if (rcvd != -1) {
recv_stream_.Write(block, rcvd, NULL, NULL);
recv_stream_.GetPosition(&position);
LOG(LS_VERBOSE) << "Received: " << position;
}
} while (rcvd > 0);
recv_stream_.GetPosition(&position);
recv_position_.push_back(position);
// Disconnect if we have done two transfers.
if (recv_position_.size() == 2u) {
Close();
OnTcpClosed(&remote_, 0);
} else {
WriteData();
}
}
void WriteData() {
size_t position, tosend;
int sent;
char block[kBlockSize];
do {
send_stream_.GetPosition(&position);
if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
rtc::SR_EOS) {
sent = local_.Send(block, tosend);
UpdateLocalClock();
if (sent != -1) {
send_stream_.SetPosition(position + sent);
LOG(LS_VERBOSE) << "Sent: " << position + sent;
} else {
send_stream_.SetPosition(position);
LOG(LS_VERBOSE) << "Flow Controlled";
}
} else {
sent = static_cast<int>(tosend = 0);
}
} while (sent > 0);
// At this point, we've filled up the available space in the send queue.
int message_queue_size =
static_cast<int>(rtc::Thread::Current()->size());
// The message queue will always have at least 2 messages, an RCLOCK and
// an LCLOCK, since they are added back on the delay queue at the same time
// they are pulled off and therefore are never really removed.
if (message_queue_size > 2) {
// If there are non-clock messages remaining, attempt to continue sending
// after giving those messages time to process, which should free up the
// send buffer.
rtc::Thread::Current()->PostDelayed(10, this, MSG_WRITE);
} else {
if (!remote_.isReceiveBufferFull()) {
LOG(LS_ERROR) << "This shouldn't happen - the send buffer is full, "
<< "the receive buffer is not, and there are no "
<< "remaining messages to process.";
}
send_stream_.GetPosition(&position);
send_position_.push_back(position);
// Drain the receiver buffer.
ReadUntilIOPending();
}
}
private:
rtc::MemoryStream send_stream_;
rtc::MemoryStream recv_stream_;
std::vector<size_t> send_position_;
std::vector<size_t> recv_position_;
};
// Basic end-to-end data transfer tests
// Test the normal case of sending data from one side to the other.
TEST_F(PseudoTcpTest, TestSend) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
TestTransfer(1000000);
}
// Test sending data with a 50 ms RTT. Transmission should take longer due
// to a slower ramp-up in send rate.
TEST_F(PseudoTcpTest, TestSendWithDelay) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetDelay(50);
TestTransfer(1000000);
}
// Test sending data with packet loss. Transmission should take much longer due
// to send back-off when loss occurs.
TEST_F(PseudoTcpTest, TestSendWithLoss) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetLoss(10);
TestTransfer(100000); // less data so test runs faster
}
// Test sending data with a 50 ms RTT and 10% packet loss. Transmission should
// take much longer due to send back-off and slower detection of loss.
TEST_F(PseudoTcpTest, TestSendWithDelayAndLoss) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetDelay(50);
SetLoss(10);
TestTransfer(100000); // less data so test runs faster
}
// Test sending data with 10% packet loss and Nagling disabled. Transmission
// should take about the same time as with Nagling enabled.
TEST_F(PseudoTcpTest, TestSendWithLossAndOptNaglingOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetLoss(10);
SetOptNagling(false);
TestTransfer(100000); // less data so test runs faster
}
// Test sending data with 10% packet loss and Delayed ACK disabled.
// Transmission should be slightly faster than with it enabled.
TEST_F(PseudoTcpTest, TestSendWithLossAndOptAckDelayOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetLoss(10);
SetOptAckDelay(0);
TestTransfer(100000);
}
// Test sending data with 50ms delay and Nagling disabled.
TEST_F(PseudoTcpTest, TestSendWithDelayAndOptNaglingOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetDelay(50);
SetOptNagling(false);
TestTransfer(100000); // less data so test runs faster
}
// Test sending data with 50ms delay and Delayed ACK disabled.
TEST_F(PseudoTcpTest, TestSendWithDelayAndOptAckDelayOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetDelay(50);
SetOptAckDelay(0);
TestTransfer(100000); // less data so test runs faster
}
// Test a large receive buffer with a sender that doesn't support scaling.
TEST_F(PseudoTcpTest, TestSendRemoteNoWindowScale) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetLocalOptRcvBuf(100000);
DisableRemoteWindowScale();
TestTransfer(1000000);
}
// Test a large sender-side receive buffer with a receiver that doesn't support
// scaling.
TEST_F(PseudoTcpTest, TestSendLocalNoWindowScale) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetRemoteOptRcvBuf(100000);
DisableLocalWindowScale();
TestTransfer(1000000);
}
// Test when both sides use window scaling.
TEST_F(PseudoTcpTest, TestSendBothUseWindowScale) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetRemoteOptRcvBuf(100000);
SetLocalOptRcvBuf(100000);
TestTransfer(1000000);
}
// Test using a large window scale value.
TEST_F(PseudoTcpTest, TestSendLargeInFlight) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetRemoteOptRcvBuf(100000);
SetLocalOptRcvBuf(100000);
SetOptSndBuf(150000);
TestTransfer(1000000);
}
TEST_F(PseudoTcpTest, TestSendBothUseLargeWindowScale) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetRemoteOptRcvBuf(1000000);
SetLocalOptRcvBuf(1000000);
TestTransfer(10000000);
}
// Test using a small receive buffer.
TEST_F(PseudoTcpTest, TestSendSmallReceiveBuffer) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetRemoteOptRcvBuf(10000);
SetLocalOptRcvBuf(10000);
TestTransfer(1000000);
}
// Test using a very small receive buffer.
TEST_F(PseudoTcpTest, TestSendVerySmallReceiveBuffer) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetRemoteOptRcvBuf(100);
SetLocalOptRcvBuf(100);
TestTransfer(100000);
}
// Ping-pong (request/response) tests
// Test sending <= 1x MTU of data in each ping/pong. Should take <10ms.
TEST_F(PseudoTcpTestPingPong, TestPingPong1xMtu) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
TestPingPong(100, 100);
}
// Test sending 2x-3x MTU of data in each ping/pong. Should take <10ms.
TEST_F(PseudoTcpTestPingPong, TestPingPong3xMtu) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
TestPingPong(400, 100);
}
// Test sending 1x-2x MTU of data in each ping/pong.
// Should take ~1s, due to interaction between Nagling and Delayed ACK.
TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtu) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
TestPingPong(2000, 5);
}
// Test sending 1x-2x MTU of data in each ping/pong with Delayed ACK off.
// Should take <10ms.
TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithAckDelayOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptAckDelay(0);
TestPingPong(2000, 100);
}
// Test sending 1x-2x MTU of data in each ping/pong with Nagling off.
// Should take <10ms.
TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithNaglingOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptNagling(false);
TestPingPong(2000, 5);
}
// Test sending a ping as pair of short (non-full) segments.
// Should take ~1s, due to Delayed ACK interaction with Nagling.
TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegments) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptAckDelay(5000);
SetBytesPerSend(50); // i.e. two Send calls per payload
TestPingPong(100, 5);
}
// Test sending ping as a pair of short (non-full) segments, with Nagling off.
// Should take <10ms.
TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithNaglingOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptNagling(false);
SetBytesPerSend(50); // i.e. two Send calls per payload
TestPingPong(100, 5);
}
// Test sending <= 1x MTU of data ping/pong, in two segments, no Delayed ACK.
// Should take ~1s.
TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithAckDelayOff) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetBytesPerSend(50); // i.e. two Send calls per payload
SetOptAckDelay(0);
TestPingPong(100, 5);
}
// Test that receive window expands and contract correctly.
TEST_F(PseudoTcpTestReceiveWindow, TestReceiveWindow) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptNagling(false);
SetOptAckDelay(0);
TestTransfer(1024 * 1000);
}
// Test setting send window size to a very small value.
TEST_F(PseudoTcpTestReceiveWindow, TestSetVerySmallSendWindowSize) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptNagling(false);
SetOptAckDelay(0);
SetOptSndBuf(900);
TestTransfer(1024 * 1000);
EXPECT_EQ(900u, EstimateSendWindowSize());
}
// Test setting receive window size to a value other than default.
TEST_F(PseudoTcpTestReceiveWindow, TestSetReceiveWindowSize) {
SetLocalMtu(1500);
SetRemoteMtu(1500);
SetOptNagling(false);
SetOptAckDelay(0);
SetRemoteOptRcvBuf(100000);
SetLocalOptRcvBuf(100000);
TestTransfer(1024 * 1000);
EXPECT_EQ(100000u, EstimateReceiveWindowSize());
}
/* Test sending data with mismatched MTUs. We should detect this and reduce
// our packet size accordingly.
// TODO: This doesn't actually work right now. The current code
// doesn't detect if the MTU is set too high on either side.
TEST_F(PseudoTcpTest, TestSendWithMismatchedMtus) {
SetLocalMtu(1500);
SetRemoteMtu(1280);
TestTransfer(1000000);
}
*/

View File

@ -1,132 +0,0 @@
/*
* 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 <string>
#include <vector>
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/parsing.h"
#include "webrtc/p2p/base/rawtransport.h"
#include "webrtc/p2p/base/rawtransportchannel.h"
#include "webrtc/p2p/base/sessionmanager.h"
#include "webrtc/libjingle/xmllite/qname.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/base/common.h"
#if defined(FEATURE_ENABLE_PSTN)
namespace cricket {
RawTransport::RawTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
PortAllocator* allocator)
: Transport(signaling_thread, worker_thread,
content_name, NS_GINGLE_RAW, allocator) {
}
RawTransport::~RawTransport() {
DestroyAllChannels();
}
bool RawTransport::ParseCandidates(SignalingProtocol protocol,
const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidates* candidates,
ParseError* error) {
for (const buzz::XmlElement* cand_elem = elem->FirstElement();
cand_elem != NULL;
cand_elem = cand_elem->NextElement()) {
if (cand_elem->Name() == QN_GINGLE_RAW_CHANNEL) {
if (!cand_elem->HasAttr(buzz::QN_NAME)) {
return BadParse("no channel name given", error);
}
if (type() != cand_elem->Attr(buzz::QN_NAME)) {
return BadParse("channel named does not exist", error);
}
rtc::SocketAddress addr;
if (!ParseRawAddress(cand_elem, &addr, error))
return false;
Candidate candidate;
candidate.set_component(1);
candidate.set_address(addr);
candidates->push_back(candidate);
}
}
return true;
}
bool RawTransport::WriteCandidates(SignalingProtocol protocol,
const Candidates& candidates,
const CandidateTranslator* translator,
XmlElements* candidate_elems,
WriteError* error) {
for (std::vector<Candidate>::const_iterator
cand = candidates.begin();
cand != candidates.end();
++cand) {
ASSERT(cand->component() == 1);
ASSERT(cand->protocol() == "udp");
rtc::SocketAddress addr = cand->address();
buzz::XmlElement* elem = new buzz::XmlElement(QN_GINGLE_RAW_CHANNEL);
elem->SetAttr(buzz::QN_NAME, type());
elem->SetAttr(QN_ADDRESS, addr.ipaddr().ToString());
elem->SetAttr(QN_PORT, addr.PortAsString());
candidate_elems->push_back(elem);
}
return true;
}
bool RawTransport::ParseRawAddress(const buzz::XmlElement* elem,
rtc::SocketAddress* addr,
ParseError* error) {
// Make sure the required attributes exist
if (!elem->HasAttr(QN_ADDRESS) ||
!elem->HasAttr(QN_PORT)) {
return BadParse("channel missing required attribute", error);
}
// Parse the address.
if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, addr, error))
return false;
return true;
}
TransportChannelImpl* RawTransport::CreateTransportChannel(int component) {
return new RawTransportChannel(content_name(), component, this,
worker_thread(),
port_allocator());
}
void RawTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
delete channel;
}
} // namespace cricket
#endif // defined(FEATURE_ENABLE_PSTN)

View File

@ -1,81 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_RAWTRANSPORT_H_
#define WEBRTC_P2P_BASE_RAWTRANSPORT_H_
#include <string>
#include "webrtc/p2p/base/transport.h"
#if defined(FEATURE_ENABLE_PSTN)
namespace cricket {
// Implements a transport that only sends raw packets, no STUN. As a result,
// it cannot do pings to determine connectivity, so it only uses a single port
// that it thinks will work.
class RawTransport : public Transport, public TransportParser {
public:
RawTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
PortAllocator* allocator);
virtual ~RawTransport();
virtual bool ParseCandidates(SignalingProtocol protocol,
const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidates* candidates,
ParseError* error);
virtual bool WriteCandidates(SignalingProtocol protocol,
const Candidates& candidates,
const CandidateTranslator* translator,
XmlElements* candidate_elems,
WriteError* error);
protected:
// Creates and destroys raw channels.
virtual TransportChannelImpl* CreateTransportChannel(int component);
virtual void DestroyTransportChannel(TransportChannelImpl* channel);
private:
// Parses the given element, which should describe the address to use for a
// given channel. This will return false and signal an error if the address
// or channel name is bad.
bool ParseRawAddress(const buzz::XmlElement* elem,
rtc::SocketAddress* addr,
ParseError* error);
friend class RawTransportChannel; // For ParseAddress.
DISALLOW_EVIL_CONSTRUCTORS(RawTransport);
};
} // namespace cricket
#endif // defined(FEATURE_ENABLE_PSTN)
#endif // WEBRTC_P2P_BASE_RAWTRANSPORT_H_

View File

@ -1,277 +0,0 @@
/*
* 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 "webrtc/p2p/base/rawtransportchannel.h"
#include <string>
#include <vector>
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/p2p/base/rawtransport.h"
#include "webrtc/p2p/base/relayport.h"
#include "webrtc/p2p/base/sessionmanager.h"
#include "webrtc/p2p/base/stunport.h"
#include "webrtc/libjingle/xmllite/qname.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/base/common.h"
#if defined(FEATURE_ENABLE_PSTN)
namespace {
const uint32 MSG_DESTROY_RTC_UNUSED_PORTS = 1;
} // namespace
namespace cricket {
RawTransportChannel::RawTransportChannel(const std::string& content_name,
int component,
RawTransport* transport,
rtc::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,
const rtc::PacketOptions& options,
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_, options, true);
}
int RawTransportChannel::SetOption(rtc::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_ = rtc::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 rtc::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_RTC_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 rtc::SocketAddress& addr) {
ASSERT(port_ == port);
SignalReadPacket(this, data, size, rtc::CreatePacketTime(0), 0);
}
void RawTransportChannel::OnMessage(rtc::Message* msg) {
ASSERT(msg->message_id == MSG_DESTROY_RTC_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)

View File

@ -1,206 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_RAWTRANSPORTCHANNEL_H_
#define WEBRTC_P2P_BASE_RAWTRANSPORTCHANNEL_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/rawtransport.h"
#include "webrtc/p2p/base/transportchannelimpl.h"
#include "webrtc/base/messagequeue.h"
#if defined(FEATURE_ENABLE_PSTN)
namespace rtc {
class Thread;
}
namespace cricket {
class Connection;
class PortAllocator;
class PortAllocatorSession;
class PortInterface;
class RelayPort;
class StunPort;
// Implements a channel that just sends bare packets once we have received the
// address of the other side. We pick a single address to send them based on
// a simple investigation of NAT type.
class RawTransportChannel : public TransportChannelImpl,
public rtc::MessageHandler {
public:
RawTransportChannel(const std::string& content_name,
int component,
RawTransport* transport,
rtc::Thread *worker_thread,
PortAllocator *allocator);
virtual ~RawTransportChannel();
// Implementation of normal channel packet sending.
virtual int SendPacket(const char *data, size_t len,
const rtc::PacketOptions& options, int flags);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetError();
// Implements TransportChannelImpl.
virtual Transport* GetTransport() { return raw_transport_; }
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {}
virtual void SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {}
// Creates an allocator session to start figuring out which type of
// port we should send to the other client. This will send
// SignalAvailableCandidate once we have decided.
virtual void Connect();
// Resets state back to unconnected.
virtual void Reset();
// We don't actually worry about signaling since we can't send new candidates.
virtual void OnSignalingReady() {}
// Handles a message setting the remote address. We are writable once we
// have this since we now know where to send.
virtual void OnCandidate(const Candidate& candidate);
void OnRemoteAddress(const rtc::SocketAddress& remote_address);
// Below ICE specific virtual methods not implemented.
virtual IceRole GetIceRole() const { return ICEROLE_UNKNOWN; }
virtual void SetIceRole(IceRole role) {}
virtual void SetIceTiebreaker(uint64 tiebreaker) {}
virtual bool GetIceProtocolType(IceProtocolType* type) const { return false; }
virtual void SetIceProtocolType(IceProtocolType type) {}
virtual void SetIceUfrag(const std::string& ice_ufrag) {}
virtual void SetIcePwd(const std::string& ice_pwd) {}
virtual void SetRemoteIceMode(IceMode mode) {}
virtual size_t GetConnectionCount() const { return 1; }
virtual bool GetStats(ConnectionInfos* infos) {
return false;
}
// DTLS methods.
virtual bool IsDtlsActive() const { return false; }
// Default implementation.
virtual bool GetSslRole(rtc::SSLRole* role) const {
return false;
}
virtual bool SetSslRole(rtc::SSLRole role) {
return false;
}
// Set up the ciphers to use for DTLS-SRTP.
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) {
return false;
}
// Find out which DTLS-SRTP cipher was negotiated
virtual bool GetSrtpCipher(std::string* cipher) {
return false;
}
// Returns false because the channel is not DTLS.
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
return false;
}
virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const {
return false;
}
// Allows key material to be extracted for external encryption.
virtual bool ExportKeyingMaterial(
const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len) {
return false;
}
virtual bool SetLocalIdentity(rtc::SSLIdentity* identity) {
return false;
}
// Set DTLS Remote fingerprint. Must be after local identity set.
virtual bool SetRemoteFingerprint(
const std::string& digest_alg,
const uint8* digest,
size_t digest_len) {
return false;
}
private:
RawTransport* raw_transport_;
rtc::Thread *worker_thread_;
PortAllocator* allocator_;
PortAllocatorSession* allocator_session_;
StunPort* stun_port_;
RelayPort* relay_port_;
PortInterface* port_;
bool use_relay_;
rtc::SocketAddress remote_address_;
// Called when the allocator creates another port.
void OnPortReady(PortAllocatorSession* session, PortInterface* port);
// Called when one of the ports we are using has determined its address.
void OnCandidatesReady(PortAllocatorSession *session,
const std::vector<Candidate>& candidates);
// Called once we have chosen the port to use for communication with the
// other client. This will send its address and prepare the port for use.
void SetPort(PortInterface* port);
// Called once we have a port and a remote address. This will set mark the
// channel as writable and signal the route to the client.
void SetWritable();
// Called when we receive a packet from the other client.
void OnReadPacket(PortInterface* port, const char* data, size_t size,
const rtc::SocketAddress& addr);
// Handles a message to destroy unused ports.
virtual void OnMessage(rtc::Message *msg);
DISALLOW_EVIL_CONSTRUCTORS(RawTransportChannel);
};
} // namespace cricket
#endif // defined(FEATURE_ENABLE_PSTN)
#endif // WEBRTC_P2P_BASE_RAWTRANSPORTCHANNEL_H_

View File

@ -1,835 +0,0 @@
/*
* 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 "webrtc/p2p/base/relayport.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
namespace cricket {
static const uint32 kMessageConnectTimeout = 1;
static const int kKeepAliveDelay = 10 * 60 * 1000;
static const int kRetryTimeout = 50 * 1000; // ICE says 50 secs
// How long to wait for a socket to connect to remote host in milliseconds
// before trying another connection.
static const int kSoftConnectTimeoutMs = 3 * 1000;
// Handles a connection to one address/port/protocol combination for a
// particular RelayEntry.
class RelayConnection : public sigslot::has_slots<> {
public:
RelayConnection(const ProtocolAddress* protocol_address,
rtc::AsyncPacketSocket* socket,
rtc::Thread* thread);
~RelayConnection();
rtc::AsyncPacketSocket* socket() const { return socket_; }
const ProtocolAddress* protocol_address() {
return protocol_address_;
}
rtc::SocketAddress GetAddress() const {
return protocol_address_->address;
}
ProtocolType GetProtocol() const {
return protocol_address_->proto;
}
int SetSocketOption(rtc::Socket::Option opt, int value);
// Validates a response to a STUN allocate request.
bool CheckResponse(StunMessage* msg);
// Sends data to the relay server.
int Send(const void* pv, size_t cb, const rtc::PacketOptions& options);
// Sends a STUN allocate request message to the relay server.
void SendAllocateRequest(RelayEntry* entry, int delay);
// Return the latest error generated by the socket.
int GetError() { return socket_->GetError(); }
// Called on behalf of a StunRequest to write data to the socket. This is
// already STUN intended for the server, so no wrapping is necessary.
void OnSendPacket(const void* data, size_t size, StunRequest* req);
private:
rtc::AsyncPacketSocket* socket_;
const ProtocolAddress* protocol_address_;
StunRequestManager *request_manager_;
};
// Manages a number of connections to the relayserver, one for each
// available protocol. We aim to use each connection for only a
// specific destination address so that we can avoid wrapping every
// packet in a STUN send / data indication.
class RelayEntry : public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
RelayEntry(RelayPort* port, const rtc::SocketAddress& ext_addr);
~RelayEntry();
RelayPort* port() { return port_; }
const rtc::SocketAddress& address() const { return ext_addr_; }
void set_address(const rtc::SocketAddress& addr) { ext_addr_ = addr; }
bool connected() const { return connected_; }
bool locked() const { return locked_; }
// Returns the last error on the socket of this entry.
int GetError();
// Returns the most preferred connection of the given
// ones. Connections are rated based on protocol in the order of:
// UDP, TCP and SSLTCP, where UDP is the most preferred protocol
static RelayConnection* GetBestConnection(RelayConnection* conn1,
RelayConnection* conn2);
// Sends the STUN requests to the server to initiate this connection.
void Connect();
// Called when this entry becomes connected. The address given is the one
// exposed to the outside world on the relay server.
void OnConnect(const rtc::SocketAddress& mapped_addr,
RelayConnection* socket);
// Sends a packet to the given destination address using the socket of this
// entry. This will wrap the packet in STUN if necessary.
int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options);
// Schedules a keep-alive allocate request.
void ScheduleKeepAlive();
void SetServerIndex(size_t sindex) { server_index_ = sindex; }
// Sets this option on the socket of each connection.
int SetSocketOption(rtc::Socket::Option opt, int value);
size_t ServerIndex() const { return server_index_; }
// Try a different server address
void HandleConnectFailure(rtc::AsyncPacketSocket* socket);
// Implementation of the MessageHandler Interface.
virtual void OnMessage(rtc::Message *pmsg);
private:
RelayPort* port_;
rtc::SocketAddress ext_addr_;
size_t server_index_;
bool connected_;
bool locked_;
RelayConnection* current_connection_;
// Called when a TCP connection is established or fails
void OnSocketConnect(rtc::AsyncPacketSocket* socket);
void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
// Called when a packet is received on this socket.
void OnReadPacket(
rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
// Called when the socket is currently able to send.
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
// Sends the given data on the socket to the server with no wrapping. This
// returns the number of bytes written or -1 if an error occurred.
int SendPacket(const void* data, size_t size,
const rtc::PacketOptions& options);
};
// Handles an allocate request for a particular RelayEntry.
class AllocateRequest : public StunRequest {
public:
AllocateRequest(RelayEntry* entry, RelayConnection* connection);
virtual ~AllocateRequest() {}
virtual void Prepare(StunMessage* request);
virtual int GetNextDelay();
virtual void OnResponse(StunMessage* response);
virtual void OnErrorResponse(StunMessage* response);
virtual void OnTimeout();
private:
RelayEntry* entry_;
RelayConnection* connection_;
uint32 start_time_;
};
RelayPort::RelayPort(
rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port, const std::string& username,
const std::string& password)
: Port(thread, RELAY_PORT_TYPE, factory, network, ip, min_port, max_port,
username, password),
ready_(false),
error_(0) {
entries_.push_back(
new RelayEntry(this, rtc::SocketAddress()));
// TODO: set local preference value for TCP based candidates.
}
RelayPort::~RelayPort() {
for (size_t i = 0; i < entries_.size(); ++i)
delete entries_[i];
thread()->Clear(this);
}
void RelayPort::AddServerAddress(const ProtocolAddress& addr) {
// Since HTTP proxies usually only allow 443,
// let's up the priority on PROTO_SSLTCP
if (addr.proto == PROTO_SSLTCP &&
(proxy().type == rtc::PROXY_HTTPS ||
proxy().type == rtc::PROXY_UNKNOWN)) {
server_addr_.push_front(addr);
} else {
server_addr_.push_back(addr);
}
}
void RelayPort::AddExternalAddress(const ProtocolAddress& addr) {
std::string proto_name = ProtoToString(addr.proto);
for (std::vector<ProtocolAddress>::iterator it = external_addr_.begin();
it != external_addr_.end(); ++it) {
if ((it->address == addr.address) && (it->proto == addr.proto)) {
LOG(INFO) << "Redundant relay address: " << proto_name
<< " @ " << addr.address.ToSensitiveString();
return;
}
}
external_addr_.push_back(addr);
}
void RelayPort::SetReady() {
if (!ready_) {
std::vector<ProtocolAddress>::iterator iter;
for (iter = external_addr_.begin();
iter != external_addr_.end(); ++iter) {
std::string proto_name = ProtoToString(iter->proto);
// In case of Gturn, related address is set to null socket address.
// This is due to as mapped address stun attribute is used for allocated
// address.
AddAddress(iter->address, iter->address, rtc::SocketAddress(),
proto_name, "", RELAY_PORT_TYPE,
ICE_TYPE_PREFERENCE_RELAY, 0, false);
}
ready_ = true;
SignalPortComplete(this);
}
}
const ProtocolAddress * RelayPort::ServerAddress(size_t index) const {
if (index < server_addr_.size())
return &server_addr_[index];
return NULL;
}
bool RelayPort::HasMagicCookie(const char* data, size_t size) {
if (size < 24 + sizeof(TURN_MAGIC_COOKIE_VALUE)) {
return false;
} else {
return memcmp(data + 24,
TURN_MAGIC_COOKIE_VALUE,
sizeof(TURN_MAGIC_COOKIE_VALUE)) == 0;
}
}
void RelayPort::PrepareAddress() {
// We initiate a connect on the first entry. If this completes, it will fill
// in the server address as the address of this port.
ASSERT(entries_.size() == 1);
entries_[0]->Connect();
ready_ = false;
}
Connection* RelayPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
// We only create conns to non-udp sockets if they are incoming on this port
if ((address.protocol() != UDP_PROTOCOL_NAME) &&
(origin != ORIGIN_THIS_PORT)) {
return 0;
}
// We don't support loopback on relays
if (address.type() == Type()) {
return 0;
}
if (!IsCompatibleAddress(address.address())) {
return 0;
}
size_t index = 0;
for (size_t i = 0; i < Candidates().size(); ++i) {
const Candidate& local = Candidates()[i];
if (local.protocol() == address.protocol()) {
index = i;
break;
}
}
Connection * conn = new ProxyConnection(this, index, address);
AddConnection(conn);
return conn;
}
int RelayPort::SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload) {
// Try to find an entry for this specific address. Note that the first entry
// created was not given an address initially, so it can be set to the first
// address that comes along.
RelayEntry* entry = 0;
for (size_t i = 0; i < entries_.size(); ++i) {
if (entries_[i]->address().IsNil() && payload) {
entry = entries_[i];
entry->set_address(addr);
break;
} else if (entries_[i]->address() == addr) {
entry = entries_[i];
break;
}
}
// If we did not find one, then we make a new one. This will not be useable
// until it becomes connected, however.
if (!entry && payload) {
entry = new RelayEntry(this, addr);
if (!entries_.empty()) {
entry->SetServerIndex(entries_[0]->ServerIndex());
}
entry->Connect();
entries_.push_back(entry);
}
// If the entry is connected, then we can send on it (though wrapping may
// still be necessary). Otherwise, we can't yet use this connection, so we
// default to the first one.
if (!entry || !entry->connected()) {
ASSERT(!entries_.empty());
entry = entries_[0];
if (!entry->connected()) {
error_ = EWOULDBLOCK;
return SOCKET_ERROR;
}
}
// Send the actual contents to the server using the usual mechanism.
int sent = entry->SendTo(data, size, addr, options);
if (sent <= 0) {
ASSERT(sent < 0);
error_ = entry->GetError();
return SOCKET_ERROR;
}
// The caller of the function is expecting the number of user data bytes,
// rather than the size of the packet.
return static_cast<int>(size);
}
int RelayPort::SetOption(rtc::Socket::Option opt, int value) {
int result = 0;
for (size_t i = 0; i < entries_.size(); ++i) {
if (entries_[i]->SetSocketOption(opt, value) < 0) {
result = -1;
error_ = entries_[i]->GetError();
}
}
options_.push_back(OptionValue(opt, value));
return result;
}
int RelayPort::GetOption(rtc::Socket::Option opt, int* value) {
std::vector<OptionValue>::iterator it;
for (it = options_.begin(); it < options_.end(); ++it) {
if (it->first == opt) {
*value = it->second;
return 0;
}
}
return SOCKET_ERROR;
}
int RelayPort::GetError() {
return error_;
}
void RelayPort::OnReadPacket(
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
ProtocolType proto,
const rtc::PacketTime& packet_time) {
if (Connection* conn = GetConnection(remote_addr)) {
conn->OnReadPacket(data, size, packet_time);
} else {
Port::OnReadPacket(data, size, remote_addr, proto);
}
}
RelayConnection::RelayConnection(const ProtocolAddress* protocol_address,
rtc::AsyncPacketSocket* socket,
rtc::Thread* thread)
: socket_(socket),
protocol_address_(protocol_address) {
request_manager_ = new StunRequestManager(thread);
request_manager_->SignalSendPacket.connect(this,
&RelayConnection::OnSendPacket);
}
RelayConnection::~RelayConnection() {
delete request_manager_;
delete socket_;
}
int RelayConnection::SetSocketOption(rtc::Socket::Option opt,
int value) {
if (socket_) {
return socket_->SetOption(opt, value);
}
return 0;
}
bool RelayConnection::CheckResponse(StunMessage* msg) {
return request_manager_->CheckResponse(msg);
}
void RelayConnection::OnSendPacket(const void* data, size_t size,
StunRequest* req) {
// TODO(mallinath) Find a way to get DSCP value from Port.
rtc::PacketOptions options; // Default dscp set to NO_CHANGE.
int sent = socket_->SendTo(data, size, GetAddress(), options);
if (sent <= 0) {
LOG(LS_VERBOSE) << "OnSendPacket: failed sending to " << GetAddress() <<
strerror(socket_->GetError());
ASSERT(sent < 0);
}
}
int RelayConnection::Send(const void* pv, size_t cb,
const rtc::PacketOptions& options) {
return socket_->SendTo(pv, cb, GetAddress(), options);
}
void RelayConnection::SendAllocateRequest(RelayEntry* entry, int delay) {
request_manager_->SendDelayed(new AllocateRequest(entry, this), delay);
}
RelayEntry::RelayEntry(RelayPort* port,
const rtc::SocketAddress& ext_addr)
: port_(port), ext_addr_(ext_addr),
server_index_(0), connected_(false), locked_(false),
current_connection_(NULL) {
}
RelayEntry::~RelayEntry() {
// Remove all RelayConnections and dispose sockets.
delete current_connection_;
current_connection_ = NULL;
}
void RelayEntry::Connect() {
// If we're already connected, return.
if (connected_)
return;
// If we've exhausted all options, bail out.
const ProtocolAddress* ra = port()->ServerAddress(server_index_);
if (!ra) {
LOG(LS_WARNING) << "No more relay addresses left to try";
return;
}
// Remove any previous connection.
if (current_connection_) {
port()->thread()->Dispose(current_connection_);
current_connection_ = NULL;
}
// Try to set up our new socket.
LOG(LS_INFO) << "Connecting to relay via " << ProtoToString(ra->proto) <<
" @ " << ra->address.ToSensitiveString();
rtc::AsyncPacketSocket* socket = NULL;
if (ra->proto == PROTO_UDP) {
// UDP sockets are simple.
socket = port_->socket_factory()->CreateUdpSocket(
rtc::SocketAddress(port_->ip(), 0),
port_->min_port(), port_->max_port());
} else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) {
int opts = (ra->proto == PROTO_SSLTCP) ?
rtc::PacketSocketFactory::OPT_SSLTCP : 0;
socket = port_->socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(port_->ip(), 0), ra->address,
port_->proxy(), port_->user_agent(), opts);
} else {
LOG(LS_WARNING) << "Unknown protocol (" << ra->proto << ")";
}
if (!socket) {
LOG(LS_WARNING) << "Socket creation failed";
}
// If we failed to get a socket, move on to the next protocol.
if (!socket) {
port()->thread()->Post(this, kMessageConnectTimeout);
return;
}
// Otherwise, create the new connection and configure any socket options.
socket->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
socket->SignalReadyToSend.connect(this, &RelayEntry::OnReadyToSend);
current_connection_ = new RelayConnection(ra, socket, port()->thread());
for (size_t i = 0; i < port_->options().size(); ++i) {
current_connection_->SetSocketOption(port_->options()[i].first,
port_->options()[i].second);
}
// If we're trying UDP, start binding requests.
// If we're trying TCP, wait for connection with a fixed timeout.
if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) {
socket->SignalClose.connect(this, &RelayEntry::OnSocketClose);
socket->SignalConnect.connect(this, &RelayEntry::OnSocketConnect);
port()->thread()->PostDelayed(kSoftConnectTimeoutMs, this,
kMessageConnectTimeout);
} else {
current_connection_->SendAllocateRequest(this, 0);
}
}
int RelayEntry::GetError() {
if (current_connection_ != NULL) {
return current_connection_->GetError();
}
return 0;
}
RelayConnection* RelayEntry::GetBestConnection(RelayConnection* conn1,
RelayConnection* conn2) {
return conn1->GetProtocol() <= conn2->GetProtocol() ? conn1 : conn2;
}
void RelayEntry::OnConnect(const rtc::SocketAddress& mapped_addr,
RelayConnection* connection) {
// We are connected, notify our parent.
ProtocolType proto = PROTO_UDP;
LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto)
<< " @ " << mapped_addr.ToSensitiveString();
connected_ = true;
port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto));
port_->SetReady();
}
int RelayEntry::SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options) {
// If this connection is locked to the address given, then we can send the
// packet with no wrapper.
if (locked_ && (ext_addr_ == addr))
return SendPacket(data, size, options);
// Otherwise, we must wrap the given data in a STUN SEND request so that we
// can communicate the destination address to the server.
//
// Note that we do not use a StunRequest here. This is because there is
// likely no reason to resend this packet. If it is late, we just drop it.
// The next send to this address will try again.
RelayMessage request;
request.SetType(STUN_SEND_REQUEST);
StunByteStringAttribute* magic_cookie_attr =
StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
magic_cookie_attr->CopyBytes(TURN_MAGIC_COOKIE_VALUE,
sizeof(TURN_MAGIC_COOKIE_VALUE));
VERIFY(request.AddAttribute(magic_cookie_attr));
StunByteStringAttribute* username_attr =
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
username_attr->CopyBytes(port_->username_fragment().c_str(),
port_->username_fragment().size());
VERIFY(request.AddAttribute(username_attr));
StunAddressAttribute* addr_attr =
StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
addr_attr->SetIP(addr.ipaddr());
addr_attr->SetPort(addr.port());
VERIFY(request.AddAttribute(addr_attr));
// Attempt to lock
if (ext_addr_ == addr) {
StunUInt32Attribute* options_attr =
StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS);
options_attr->SetValue(0x1);
VERIFY(request.AddAttribute(options_attr));
}
StunByteStringAttribute* data_attr =
StunAttribute::CreateByteString(STUN_ATTR_DATA);
data_attr->CopyBytes(data, size);
VERIFY(request.AddAttribute(data_attr));
// TODO: compute the HMAC.
rtc::ByteBuffer buf;
request.Write(&buf);
return SendPacket(buf.Data(), buf.Length(), options);
}
void RelayEntry::ScheduleKeepAlive() {
if (current_connection_) {
current_connection_->SendAllocateRequest(this, kKeepAliveDelay);
}
}
int RelayEntry::SetSocketOption(rtc::Socket::Option opt, int value) {
// Set the option on all available sockets.
int socket_error = 0;
if (current_connection_) {
socket_error = current_connection_->SetSocketOption(opt, value);
}
return socket_error;
}
void RelayEntry::HandleConnectFailure(
rtc::AsyncPacketSocket* socket) {
// Make sure it's the current connection that has failed, it might
// be an old socked that has not yet been disposed.
if (!socket ||
(current_connection_ && socket == current_connection_->socket())) {
if (current_connection_)
port()->SignalConnectFailure(current_connection_->protocol_address());
// Try to connect to the next server address.
server_index_ += 1;
Connect();
}
}
void RelayEntry::OnMessage(rtc::Message *pmsg) {
ASSERT(pmsg->message_id == kMessageConnectTimeout);
if (current_connection_) {
const ProtocolAddress* ra = current_connection_->protocol_address();
LOG(LS_WARNING) << "Relay " << ra->proto << " connection to " <<
ra->address << " timed out";
// Currently we connect to each server address in sequence. If we
// have more addresses to try, treat this is an error and move on to
// the next address, otherwise give this connection more time and
// await the real timeout.
//
// TODO: Connect to servers in parallel to speed up connect time
// and to avoid giving up too early.
port_->SignalSoftTimeout(ra);
HandleConnectFailure(current_connection_->socket());
} else {
HandleConnectFailure(NULL);
}
}
void RelayEntry::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
LOG(INFO) << "relay tcp connected to " <<
socket->GetRemoteAddress().ToSensitiveString();
if (current_connection_ != NULL) {
current_connection_->SendAllocateRequest(this, 0);
}
}
void RelayEntry::OnSocketClose(rtc::AsyncPacketSocket* socket,
int error) {
PLOG(LERROR, error) << "Relay connection failed: socket closed";
HandleConnectFailure(socket);
}
void RelayEntry::OnReadPacket(
rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
// ASSERT(remote_addr == port_->server_addr());
// TODO: are we worried about this?
if (current_connection_ == NULL || socket != current_connection_->socket()) {
// This packet comes from an unknown address.
LOG(WARNING) << "Dropping packet: unknown address";
return;
}
// If the magic cookie is not present, then this is an unwrapped packet sent
// by the server, The actual remote address is the one we recorded.
if (!port_->HasMagicCookie(data, size)) {
if (locked_) {
port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP, packet_time);
} else {
LOG(WARNING) << "Dropping packet: entry not locked";
}
return;
}
rtc::ByteBuffer buf(data, size);
RelayMessage msg;
if (!msg.Read(&buf)) {
LOG(INFO) << "Incoming packet was not STUN";
return;
}
// The incoming packet should be a STUN ALLOCATE response, SEND response, or
// DATA indication.
if (current_connection_->CheckResponse(&msg)) {
return;
} else if (msg.type() == STUN_SEND_RESPONSE) {
if (const StunUInt32Attribute* options_attr =
msg.GetUInt32(STUN_ATTR_OPTIONS)) {
if (options_attr->value() & 0x1) {
locked_ = true;
}
}
return;
} else if (msg.type() != STUN_DATA_INDICATION) {
LOG(INFO) << "Received BAD stun type from server: " << msg.type();
return;
}
// This must be a data indication.
const StunAddressAttribute* addr_attr =
msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
if (!addr_attr) {
LOG(INFO) << "Data indication has no source address";
return;
} else if (addr_attr->family() != 1) {
LOG(INFO) << "Source address has bad family";
return;
}
rtc::SocketAddress remote_addr2(addr_attr->ipaddr(), addr_attr->port());
const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA);
if (!data_attr) {
LOG(INFO) << "Data indication has no data";
return;
}
// Process the actual data and remote address in the normal manner.
port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2,
PROTO_UDP, packet_time);
}
void RelayEntry::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
if (connected()) {
port_->OnReadyToSend();
}
}
int RelayEntry::SendPacket(const void* data, size_t size,
const rtc::PacketOptions& options) {
int sent = 0;
if (current_connection_) {
// We are connected, no need to send packets anywere else than to
// the current connection.
sent = current_connection_->Send(data, size, options);
}
return sent;
}
AllocateRequest::AllocateRequest(RelayEntry* entry,
RelayConnection* connection)
: StunRequest(new RelayMessage()),
entry_(entry),
connection_(connection) {
start_time_ = rtc::Time();
}
void AllocateRequest::Prepare(StunMessage* request) {
request->SetType(STUN_ALLOCATE_REQUEST);
StunByteStringAttribute* username_attr =
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
username_attr->CopyBytes(
entry_->port()->username_fragment().c_str(),
entry_->port()->username_fragment().size());
VERIFY(request->AddAttribute(username_attr));
}
int AllocateRequest::GetNextDelay() {
int delay = 100 * rtc::_max(1 << count_, 2);
count_ += 1;
if (count_ == 5)
timeout_ = true;
return delay;
}
void AllocateRequest::OnResponse(StunMessage* response) {
const StunAddressAttribute* addr_attr =
response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
if (!addr_attr) {
LOG(INFO) << "Allocate response missing mapped address.";
} else if (addr_attr->family() != 1) {
LOG(INFO) << "Mapped address has bad family";
} else {
rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
entry_->OnConnect(addr, connection_);
}
// We will do a keep-alive regardless of whether this request suceeds.
// This should have almost no impact on network usage.
entry_->ScheduleKeepAlive();
}
void AllocateRequest::OnErrorResponse(StunMessage* response) {
const StunErrorCodeAttribute* attr = response->GetErrorCode();
if (!attr) {
LOG(INFO) << "Bad allocate response error code";
} else {
LOG(INFO) << "Allocate error response:"
<< " code=" << attr->code()
<< " reason='" << attr->reason() << "'";
}
if (rtc::TimeSince(start_time_) <= kRetryTimeout)
entry_->ScheduleKeepAlive();
}
void AllocateRequest::OnTimeout() {
LOG(INFO) << "Allocate request timed out";
entry_->HandleConnectFailure(connection_->socket());
}
} // namespace cricket

View File

@ -1,118 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_RELAYPORT_H_
#define WEBRTC_P2P_BASE_RELAYPORT_H_
#include <deque>
#include <string>
#include <utility>
#include <vector>
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/stunrequest.h"
namespace cricket {
class RelayEntry;
class RelayConnection;
// Communicates using an allocated port on the relay server. For each
// remote candidate that we try to send data to a RelayEntry instance
// is created. The RelayEntry will try to reach the remote destination
// by connecting to all available server addresses in a pre defined
// order with a small delay in between. When a connection is
// successful all other connection attemts are aborted.
class RelayPort : public Port {
public:
typedef std::pair<rtc::Socket::Option, int> OptionValue;
// RelayPort doesn't yet do anything fancy in the ctor.
static RelayPort* Create(
rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port, const std::string& username,
const std::string& password) {
return new RelayPort(thread, factory, network, ip, min_port, max_port,
username, password);
}
virtual ~RelayPort();
void AddServerAddress(const ProtocolAddress& addr);
void AddExternalAddress(const ProtocolAddress& addr);
const std::vector<OptionValue>& options() const { return options_; }
bool HasMagicCookie(const char* data, size_t size);
virtual void PrepareAddress();
virtual Connection* CreateConnection(const Candidate& address,
CandidateOrigin origin);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetOption(rtc::Socket::Option opt, int* value);
virtual int GetError();
const ProtocolAddress * ServerAddress(size_t index) const;
bool IsReady() { return ready_; }
// Used for testing.
sigslot::signal1<const ProtocolAddress*> SignalConnectFailure;
sigslot::signal1<const ProtocolAddress*> SignalSoftTimeout;
protected:
RelayPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network*, const rtc::IPAddress& ip,
int min_port, int max_port, const std::string& username,
const std::string& password);
bool Init();
void SetReady();
virtual int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload);
// Dispatches the given packet to the port or connection as appropriate.
void OnReadPacket(const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
ProtocolType proto,
const rtc::PacketTime& packet_time);
private:
friend class RelayEntry;
std::deque<ProtocolAddress> server_addr_;
std::vector<ProtocolAddress> external_addr_;
bool ready_;
std::vector<RelayEntry*> entries_;
std::vector<OptionValue> options_;
int error_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_RELAYPORT_H_

View File

@ -1,289 +0,0 @@
/*
* libjingle
* Copyright 2009 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 "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/relayport.h"
#include "webrtc/p2p/base/relayserver.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/socketadapters.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/virtualsocketserver.h"
using rtc::SocketAddress;
static const SocketAddress kLocalAddress = SocketAddress("192.168.1.2", 0);
static const SocketAddress kRelayUdpAddr = SocketAddress("99.99.99.1", 5000);
static const SocketAddress kRelayTcpAddr = SocketAddress("99.99.99.2", 5001);
static const SocketAddress kRelaySslAddr = SocketAddress("99.99.99.3", 443);
static const SocketAddress kRelayExtAddr = SocketAddress("99.99.99.3", 5002);
static const int kTimeoutMs = 1000;
static const int kMaxTimeoutMs = 5000;
// Tests connecting a RelayPort to a fake relay server
// (cricket::RelayServer) using all currently available protocols. The
// network layer is faked out by using a VirtualSocketServer for
// creating sockets. The test will monitor the current state of the
// RelayPort and created sockets by listening for signals such as,
// SignalConnectFailure, SignalConnectTimeout, SignalSocketClosed and
// SignalReadPacket.
class RelayPortTest : public testing::Test,
public sigslot::has_slots<> {
public:
RelayPortTest()
: main_(rtc::Thread::Current()),
physical_socket_server_(new rtc::PhysicalSocketServer),
virtual_socket_server_(new rtc::VirtualSocketServer(
physical_socket_server_.get())),
ss_scope_(virtual_socket_server_.get()),
network_("unittest", "unittest", rtc::IPAddress(INADDR_ANY), 32),
socket_factory_(rtc::Thread::Current()),
username_(rtc::CreateRandomString(16)),
password_(rtc::CreateRandomString(16)),
relay_port_(cricket::RelayPort::Create(main_, &socket_factory_,
&network_,
kLocalAddress.ipaddr(),
0, 0, username_, password_)),
relay_server_(new cricket::RelayServer(main_)) {
}
void OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
received_packet_count_[socket]++;
}
void OnConnectFailure(const cricket::ProtocolAddress* addr) {
failed_connections_.push_back(*addr);
}
void OnSoftTimeout(const cricket::ProtocolAddress* addr) {
soft_timedout_connections_.push_back(*addr);
}
protected:
virtual void SetUp() {
// The relay server needs an external socket to work properly.
rtc::AsyncUDPSocket* ext_socket =
CreateAsyncUdpSocket(kRelayExtAddr);
relay_server_->AddExternalSocket(ext_socket);
// Listen for failures.
relay_port_->SignalConnectFailure.
connect(this, &RelayPortTest::OnConnectFailure);
// Listen for soft timeouts.
relay_port_->SignalSoftTimeout.
connect(this, &RelayPortTest::OnSoftTimeout);
}
// Udp has the highest 'goodness' value of the three different
// protocols used for connecting to the relay server. As soon as
// PrepareAddress is called, the RelayPort will start trying to
// connect to the given UDP address. As soon as a response to the
// sent STUN allocate request message has been received, the
// RelayPort will consider the connection to be complete and will
// abort any other connection attempts.
void TestConnectUdp() {
// Add a UDP socket to the relay server.
rtc::AsyncUDPSocket* internal_udp_socket =
CreateAsyncUdpSocket(kRelayUdpAddr);
rtc::AsyncSocket* server_socket = CreateServerSocket(kRelayTcpAddr);
relay_server_->AddInternalSocket(internal_udp_socket);
relay_server_->AddInternalServerSocket(server_socket, cricket::PROTO_TCP);
// Now add our relay addresses to the relay port and let it start.
relay_port_->AddServerAddress(
cricket::ProtocolAddress(kRelayUdpAddr, cricket::PROTO_UDP));
relay_port_->AddServerAddress(
cricket::ProtocolAddress(kRelayTcpAddr, cricket::PROTO_TCP));
relay_port_->PrepareAddress();
// Should be connected.
EXPECT_TRUE_WAIT(relay_port_->IsReady(), kTimeoutMs);
// Make sure that we are happy with UDP, ie. not continuing with
// TCP, SSLTCP, etc.
WAIT(relay_server_->HasConnection(kRelayTcpAddr), kTimeoutMs);
// Should have only one connection.
EXPECT_EQ(1, relay_server_->GetConnectionCount());
// Should be the UDP address.
EXPECT_TRUE(relay_server_->HasConnection(kRelayUdpAddr));
}
// TCP has the second best 'goodness' value, and as soon as UDP
// connection has failed, the RelayPort will attempt to connect via
// TCP. Here we add a fake UDP address together with a real TCP
// address to simulate an UDP failure. As soon as UDP has failed the
// RelayPort will try the TCP adress and succed.
void TestConnectTcp() {
// Create a fake UDP address for relay port to simulate a failure.
cricket::ProtocolAddress fake_protocol_address =
cricket::ProtocolAddress(kRelayUdpAddr, cricket::PROTO_UDP);
// Create a server socket for the RelayServer.
rtc::AsyncSocket* server_socket = CreateServerSocket(kRelayTcpAddr);
relay_server_->AddInternalServerSocket(server_socket, cricket::PROTO_TCP);
// Add server addresses to the relay port and let it start.
relay_port_->AddServerAddress(
cricket::ProtocolAddress(fake_protocol_address));
relay_port_->AddServerAddress(
cricket::ProtocolAddress(kRelayTcpAddr, cricket::PROTO_TCP));
relay_port_->PrepareAddress();
EXPECT_FALSE(relay_port_->IsReady());
// Should have timed out in 200 + 200 + 400 + 800 + 1600 ms.
EXPECT_TRUE_WAIT(HasFailed(&fake_protocol_address), 3600);
// Wait until relayport is ready.
EXPECT_TRUE_WAIT(relay_port_->IsReady(), kMaxTimeoutMs);
// Should have only one connection.
EXPECT_EQ(1, relay_server_->GetConnectionCount());
// Should be the TCP address.
EXPECT_TRUE(relay_server_->HasConnection(kRelayTcpAddr));
}
void TestConnectSslTcp() {
// Create a fake TCP address for relay port to simulate a failure.
// We skip UDP here since transition from UDP to TCP has been
// tested above.
cricket::ProtocolAddress fake_protocol_address =
cricket::ProtocolAddress(kRelayTcpAddr, cricket::PROTO_TCP);
// Create a ssl server socket for the RelayServer.
rtc::AsyncSocket* ssl_server_socket =
CreateServerSocket(kRelaySslAddr);
relay_server_->AddInternalServerSocket(ssl_server_socket,
cricket::PROTO_SSLTCP);
// Create a tcp server socket that listens on the fake address so
// the relay port can attempt to connect to it.
rtc::scoped_ptr<rtc::AsyncSocket> tcp_server_socket(
CreateServerSocket(kRelayTcpAddr));
// Add server addresses to the relay port and let it start.
relay_port_->AddServerAddress(fake_protocol_address);
relay_port_->AddServerAddress(
cricket::ProtocolAddress(kRelaySslAddr, cricket::PROTO_SSLTCP));
relay_port_->PrepareAddress();
EXPECT_FALSE(relay_port_->IsReady());
// Should have timed out in 3000 ms(relayport.cc, kSoftConnectTimeoutMs).
EXPECT_TRUE_WAIT_MARGIN(HasTimedOut(&fake_protocol_address), 3000, 100);
// Wait until relayport is ready.
EXPECT_TRUE_WAIT(relay_port_->IsReady(), kMaxTimeoutMs);
// Should have only one connection.
EXPECT_EQ(1, relay_server_->GetConnectionCount());
// Should be the SSLTCP address.
EXPECT_TRUE(relay_server_->HasConnection(kRelaySslAddr));
}
private:
rtc::AsyncUDPSocket* CreateAsyncUdpSocket(const SocketAddress addr) {
rtc::AsyncSocket* socket =
virtual_socket_server_->CreateAsyncSocket(SOCK_DGRAM);
rtc::AsyncUDPSocket* packet_socket =
rtc::AsyncUDPSocket::Create(socket, addr);
EXPECT_TRUE(packet_socket != NULL);
packet_socket->SignalReadPacket.connect(this, &RelayPortTest::OnReadPacket);
return packet_socket;
}
rtc::AsyncSocket* CreateServerSocket(const SocketAddress addr) {
rtc::AsyncSocket* socket =
virtual_socket_server_->CreateAsyncSocket(SOCK_STREAM);
EXPECT_GE(socket->Bind(addr), 0);
EXPECT_GE(socket->Listen(5), 0);
return socket;
}
bool HasFailed(cricket::ProtocolAddress* addr) {
for (size_t i = 0; i < failed_connections_.size(); i++) {
if (failed_connections_[i].address == addr->address &&
failed_connections_[i].proto == addr->proto) {
return true;
}
}
return false;
}
bool HasTimedOut(cricket::ProtocolAddress* addr) {
for (size_t i = 0; i < soft_timedout_connections_.size(); i++) {
if (soft_timedout_connections_[i].address == addr->address &&
soft_timedout_connections_[i].proto == addr->proto) {
return true;
}
}
return false;
}
typedef std::map<rtc::AsyncPacketSocket*, int> PacketMap;
rtc::Thread* main_;
rtc::scoped_ptr<rtc::PhysicalSocketServer>
physical_socket_server_;
rtc::scoped_ptr<rtc::VirtualSocketServer> virtual_socket_server_;
rtc::SocketServerScope ss_scope_;
rtc::Network network_;
rtc::BasicPacketSocketFactory socket_factory_;
std::string username_;
std::string password_;
rtc::scoped_ptr<cricket::RelayPort> relay_port_;
rtc::scoped_ptr<cricket::RelayServer> relay_server_;
std::vector<cricket::ProtocolAddress> failed_connections_;
std::vector<cricket::ProtocolAddress> soft_timedout_connections_;
PacketMap received_packet_count_;
};
TEST_F(RelayPortTest, ConnectUdp) {
TestConnectUdp();
}
TEST_F(RelayPortTest, ConnectTcp) {
TestConnectTcp();
}
TEST_F(RelayPortTest, ConnectSslTcp) {
TestConnectSslTcp();
}

View File

@ -1,763 +0,0 @@
/*
* 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 "webrtc/p2p/base/relayserver.h"
#ifdef POSIX
#include <errno.h>
#endif // POSIX
#include <algorithm>
#include "webrtc/base/asynctcpsocket.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/socketadapters.h"
namespace cricket {
// By default, we require a ping every 90 seconds.
const int MAX_LIFETIME = 15 * 60 * 1000;
// The number of bytes in each of the usernames we use.
const uint32 USERNAME_LENGTH = 16;
// Calls SendTo on the given socket and logs any bad results.
void Send(rtc::AsyncPacketSocket* socket, const char* bytes, size_t size,
const rtc::SocketAddress& addr) {
rtc::PacketOptions options;
int result = socket->SendTo(bytes, size, addr, options);
if (result < static_cast<int>(size)) {
LOG(LS_ERROR) << "SendTo wrote only " << result << " of " << size
<< " bytes";
} else if (result < 0) {
LOG_ERR(LS_ERROR) << "SendTo";
}
}
// Sends the given STUN message on the given socket.
void SendStun(const StunMessage& msg,
rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& addr) {
rtc::ByteBuffer buf;
msg.Write(&buf);
Send(socket, buf.Data(), buf.Length(), addr);
}
// Constructs a STUN error response and sends it on the given socket.
void SendStunError(const StunMessage& msg, rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& remote_addr, int error_code,
const char* error_desc, const std::string& magic_cookie) {
RelayMessage err_msg;
err_msg.SetType(GetStunErrorResponseType(msg.type()));
err_msg.SetTransactionID(msg.transaction_id());
StunByteStringAttribute* magic_cookie_attr =
StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
if (magic_cookie.size() == 0) {
magic_cookie_attr->CopyBytes(cricket::TURN_MAGIC_COOKIE_VALUE,
sizeof(cricket::TURN_MAGIC_COOKIE_VALUE));
} else {
magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size());
}
err_msg.AddAttribute(magic_cookie_attr);
StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
err_code->SetClass(error_code / 100);
err_code->SetNumber(error_code % 100);
err_code->SetReason(error_desc);
err_msg.AddAttribute(err_code);
SendStun(err_msg, socket, remote_addr);
}
RelayServer::RelayServer(rtc::Thread* thread)
: thread_(thread), log_bindings_(true) {
}
RelayServer::~RelayServer() {
// Deleting the binding will cause it to be removed from the map.
while (!bindings_.empty())
delete bindings_.begin()->second;
for (size_t i = 0; i < internal_sockets_.size(); ++i)
delete internal_sockets_[i];
for (size_t i = 0; i < external_sockets_.size(); ++i)
delete external_sockets_[i];
for (size_t i = 0; i < removed_sockets_.size(); ++i)
delete removed_sockets_[i];
while (!server_sockets_.empty()) {
rtc::AsyncSocket* socket = server_sockets_.begin()->first;
server_sockets_.erase(server_sockets_.begin()->first);
delete socket;
}
}
void RelayServer::AddInternalSocket(rtc::AsyncPacketSocket* socket) {
ASSERT(internal_sockets_.end() ==
std::find(internal_sockets_.begin(), internal_sockets_.end(), socket));
internal_sockets_.push_back(socket);
socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket);
}
void RelayServer::RemoveInternalSocket(rtc::AsyncPacketSocket* socket) {
SocketList::iterator iter =
std::find(internal_sockets_.begin(), internal_sockets_.end(), socket);
ASSERT(iter != internal_sockets_.end());
internal_sockets_.erase(iter);
removed_sockets_.push_back(socket);
socket->SignalReadPacket.disconnect(this);
}
void RelayServer::AddExternalSocket(rtc::AsyncPacketSocket* socket) {
ASSERT(external_sockets_.end() ==
std::find(external_sockets_.begin(), external_sockets_.end(), socket));
external_sockets_.push_back(socket);
socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket);
}
void RelayServer::RemoveExternalSocket(rtc::AsyncPacketSocket* socket) {
SocketList::iterator iter =
std::find(external_sockets_.begin(), external_sockets_.end(), socket);
ASSERT(iter != external_sockets_.end());
external_sockets_.erase(iter);
removed_sockets_.push_back(socket);
socket->SignalReadPacket.disconnect(this);
}
void RelayServer::AddInternalServerSocket(rtc::AsyncSocket* socket,
cricket::ProtocolType proto) {
ASSERT(server_sockets_.end() ==
server_sockets_.find(socket));
server_sockets_[socket] = proto;
socket->SignalReadEvent.connect(this, &RelayServer::OnReadEvent);
}
void RelayServer::RemoveInternalServerSocket(
rtc::AsyncSocket* socket) {
ServerSocketMap::iterator iter = server_sockets_.find(socket);
ASSERT(iter != server_sockets_.end());
server_sockets_.erase(iter);
socket->SignalReadEvent.disconnect(this);
}
int RelayServer::GetConnectionCount() const {
return static_cast<int>(connections_.size());
}
rtc::SocketAddressPair RelayServer::GetConnection(int connection) const {
int i = 0;
for (ConnectionMap::const_iterator it = connections_.begin();
it != connections_.end(); ++it) {
if (i == connection) {
return it->second->addr_pair();
}
++i;
}
return rtc::SocketAddressPair();
}
bool RelayServer::HasConnection(const rtc::SocketAddress& address) const {
for (ConnectionMap::const_iterator it = connections_.begin();
it != connections_.end(); ++it) {
if (it->second->addr_pair().destination() == address) {
return true;
}
}
return false;
}
void RelayServer::OnReadEvent(rtc::AsyncSocket* socket) {
ASSERT(server_sockets_.find(socket) != server_sockets_.end());
AcceptConnection(socket);
}
void RelayServer::OnInternalPacket(
rtc::AsyncPacketSocket* socket, const char* bytes, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
// Get the address of the connection we just received on.
rtc::SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
ASSERT(!ap.destination().IsNil());
// If this did not come from an existing connection, it should be a STUN
// allocate request.
ConnectionMap::iterator piter = connections_.find(ap);
if (piter == connections_.end()) {
HandleStunAllocate(bytes, size, ap, socket);
return;
}
RelayServerConnection* int_conn = piter->second;
// Handle STUN requests to the server itself.
if (int_conn->binding()->HasMagicCookie(bytes, size)) {
HandleStun(int_conn, bytes, size);
return;
}
// Otherwise, this is a non-wrapped packet that we are to forward. Make sure
// that this connection has been locked. (Otherwise, we would not know what
// address to forward to.)
if (!int_conn->locked()) {
LOG(LS_WARNING) << "Dropping packet: connection not locked";
return;
}
// Forward this to the destination address into the connection.
RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection(
int_conn->default_destination());
if (ext_conn && ext_conn->locked()) {
// TODO: Check the HMAC.
ext_conn->Send(bytes, size);
} else {
// This happens very often and is not an error.
LOG(LS_INFO) << "Dropping packet: no external connection";
}
}
void RelayServer::OnExternalPacket(
rtc::AsyncPacketSocket* socket, const char* bytes, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
// Get the address of the connection we just received on.
rtc::SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
ASSERT(!ap.destination().IsNil());
// If this connection already exists, then forward the traffic.
ConnectionMap::iterator piter = connections_.find(ap);
if (piter != connections_.end()) {
// TODO: Check the HMAC.
RelayServerConnection* ext_conn = piter->second;
RelayServerConnection* int_conn =
ext_conn->binding()->GetInternalConnection(
ext_conn->addr_pair().source());
ASSERT(int_conn != NULL);
int_conn->Send(bytes, size, ext_conn->addr_pair().source());
ext_conn->Lock(); // allow outgoing packets
return;
}
// The first packet should always be a STUN / TURN packet. If it isn't, then
// we should just ignore this packet.
RelayMessage msg;
rtc::ByteBuffer buf(bytes, size);
if (!msg.Read(&buf)) {
LOG(LS_WARNING) << "Dropping packet: first packet not STUN";
return;
}
// The initial packet should have a username (which identifies the binding).
const StunByteStringAttribute* username_attr =
msg.GetByteString(STUN_ATTR_USERNAME);
if (!username_attr) {
LOG(LS_WARNING) << "Dropping packet: no username";
return;
}
uint32 length = rtc::_min(static_cast<uint32>(username_attr->length()),
USERNAME_LENGTH);
std::string username(username_attr->bytes(), length);
// TODO: Check the HMAC.
// The binding should already be present.
BindingMap::iterator biter = bindings_.find(username);
if (biter == bindings_.end()) {
LOG(LS_WARNING) << "Dropping packet: no binding with username";
return;
}
// Add this authenticted connection to the binding.
RelayServerConnection* ext_conn =
new RelayServerConnection(biter->second, ap, socket);
ext_conn->binding()->AddExternalConnection(ext_conn);
AddConnection(ext_conn);
// We always know where external packets should be forwarded, so we can lock
// them from the beginning.
ext_conn->Lock();
// Send this message on the appropriate internal connection.
RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection(
ext_conn->addr_pair().source());
ASSERT(int_conn != NULL);
int_conn->Send(bytes, size, ext_conn->addr_pair().source());
}
bool RelayServer::HandleStun(
const char* bytes, size_t size, const rtc::SocketAddress& remote_addr,
rtc::AsyncPacketSocket* socket, std::string* username,
StunMessage* msg) {
// Parse this into a stun message. Eat the message if this fails.
rtc::ByteBuffer buf(bytes, size);
if (!msg->Read(&buf)) {
return false;
}
// The initial packet should have a username (which identifies the binding).
const StunByteStringAttribute* username_attr =
msg->GetByteString(STUN_ATTR_USERNAME);
if (!username_attr) {
SendStunError(*msg, socket, remote_addr, 432, "Missing Username", "");
return false;
}
// Record the username if requested.
if (username)
username->append(username_attr->bytes(), username_attr->length());
// TODO: Check for unknown attributes (<= 0x7fff)
return true;
}
void RelayServer::HandleStunAllocate(
const char* bytes, size_t size, const rtc::SocketAddressPair& ap,
rtc::AsyncPacketSocket* socket) {
// Make sure this is a valid STUN request.
RelayMessage request;
std::string username;
if (!HandleStun(bytes, size, ap.source(), socket, &username, &request))
return;
// Make sure this is a an allocate request.
if (request.type() != STUN_ALLOCATE_REQUEST) {
SendStunError(request,
socket,
ap.source(),
600,
"Operation Not Supported",
"");
return;
}
// TODO: Check the HMAC.
// Find or create the binding for this username.
RelayServerBinding* binding;
BindingMap::iterator biter = bindings_.find(username);
if (biter != bindings_.end()) {
binding = biter->second;
} else {
// NOTE: In the future, bindings will be created by the bot only. This
// else-branch will then disappear.
// Compute the appropriate lifetime for this binding.
uint32 lifetime = MAX_LIFETIME;
const StunUInt32Attribute* lifetime_attr =
request.GetUInt32(STUN_ATTR_LIFETIME);
if (lifetime_attr)
lifetime = rtc::_min(lifetime, lifetime_attr->value() * 1000);
binding = new RelayServerBinding(this, username, "0", lifetime);
binding->SignalTimeout.connect(this, &RelayServer::OnTimeout);
bindings_[username] = binding;
if (log_bindings_) {
LOG(LS_INFO) << "Added new binding " << username << ", "
<< bindings_.size() << " total";
}
}
// Add this connection to the binding. It starts out unlocked.
RelayServerConnection* int_conn =
new RelayServerConnection(binding, ap, socket);
binding->AddInternalConnection(int_conn);
AddConnection(int_conn);
// Now that we have a connection, this other method takes over.
HandleStunAllocate(int_conn, request);
}
void RelayServer::HandleStun(
RelayServerConnection* int_conn, const char* bytes, size_t size) {
// Make sure this is a valid STUN request.
RelayMessage request;
std::string username;
if (!HandleStun(bytes, size, int_conn->addr_pair().source(),
int_conn->socket(), &username, &request))
return;
// Make sure the username is the one were were expecting.
if (username != int_conn->binding()->username()) {
int_conn->SendStunError(request, 430, "Stale Credentials");
return;
}
// TODO: Check the HMAC.
// Send this request to the appropriate handler.
if (request.type() == STUN_SEND_REQUEST)
HandleStunSend(int_conn, request);
else if (request.type() == STUN_ALLOCATE_REQUEST)
HandleStunAllocate(int_conn, request);
else
int_conn->SendStunError(request, 600, "Operation Not Supported");
}
void RelayServer::HandleStunAllocate(
RelayServerConnection* int_conn, const StunMessage& request) {
// Create a response message that includes an address with which external
// clients can communicate.
RelayMessage response;
response.SetType(STUN_ALLOCATE_RESPONSE);
response.SetTransactionID(request.transaction_id());
StunByteStringAttribute* magic_cookie_attr =
StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(),
int_conn->binding()->magic_cookie().size());
response.AddAttribute(magic_cookie_attr);
size_t index = rand() % external_sockets_.size();
rtc::SocketAddress ext_addr =
external_sockets_[index]->GetLocalAddress();
StunAddressAttribute* addr_attr =
StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
addr_attr->SetIP(ext_addr.ipaddr());
addr_attr->SetPort(ext_addr.port());
response.AddAttribute(addr_attr);
StunUInt32Attribute* res_lifetime_attr =
StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME);
res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000);
response.AddAttribute(res_lifetime_attr);
// TODO: Support transport-prefs (preallocate RTCP port).
// TODO: Support bandwidth restrictions.
// TODO: Add message integrity check.
// Send a response to the caller.
int_conn->SendStun(response);
}
void RelayServer::HandleStunSend(
RelayServerConnection* int_conn, const StunMessage& request) {
const StunAddressAttribute* addr_attr =
request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS);
if (!addr_attr) {
int_conn->SendStunError(request, 400, "Bad Request");
return;
}
const StunByteStringAttribute* data_attr =
request.GetByteString(STUN_ATTR_DATA);
if (!data_attr) {
int_conn->SendStunError(request, 400, "Bad Request");
return;
}
rtc::SocketAddress ext_addr(addr_attr->ipaddr(), addr_attr->port());
RelayServerConnection* ext_conn =
int_conn->binding()->GetExternalConnection(ext_addr);
if (!ext_conn) {
// Create a new connection to establish the relationship with this binding.
ASSERT(external_sockets_.size() == 1);
rtc::AsyncPacketSocket* socket = external_sockets_[0];
rtc::SocketAddressPair ap(ext_addr, socket->GetLocalAddress());
ext_conn = new RelayServerConnection(int_conn->binding(), ap, socket);
ext_conn->binding()->AddExternalConnection(ext_conn);
AddConnection(ext_conn);
}
// If this connection has pinged us, then allow outgoing traffic.
if (ext_conn->locked())
ext_conn->Send(data_attr->bytes(), data_attr->length());
const StunUInt32Attribute* options_attr =
request.GetUInt32(STUN_ATTR_OPTIONS);
if (options_attr && (options_attr->value() & 0x01)) {
int_conn->set_default_destination(ext_addr);
int_conn->Lock();
RelayMessage response;
response.SetType(STUN_SEND_RESPONSE);
response.SetTransactionID(request.transaction_id());
StunByteStringAttribute* magic_cookie_attr =
StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(),
int_conn->binding()->magic_cookie().size());
response.AddAttribute(magic_cookie_attr);
StunUInt32Attribute* options2_attr =
StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS);
options2_attr->SetValue(0x01);
response.AddAttribute(options2_attr);
int_conn->SendStun(response);
}
}
void RelayServer::AddConnection(RelayServerConnection* conn) {
ASSERT(connections_.find(conn->addr_pair()) == connections_.end());
connections_[conn->addr_pair()] = conn;
}
void RelayServer::RemoveConnection(RelayServerConnection* conn) {
ConnectionMap::iterator iter = connections_.find(conn->addr_pair());
ASSERT(iter != connections_.end());
connections_.erase(iter);
}
void RelayServer::RemoveBinding(RelayServerBinding* binding) {
BindingMap::iterator iter = bindings_.find(binding->username());
ASSERT(iter != bindings_.end());
bindings_.erase(iter);
if (log_bindings_) {
LOG(LS_INFO) << "Removed binding " << binding->username() << ", "
<< bindings_.size() << " remaining";
}
}
void RelayServer::OnMessage(rtc::Message *pmsg) {
#if ENABLE_DEBUG
static const uint32 kMessageAcceptConnection = 1;
ASSERT(pmsg->message_id == kMessageAcceptConnection);
#endif
rtc::MessageData* data = pmsg->pdata;
rtc::AsyncSocket* socket =
static_cast <rtc::TypedMessageData<rtc::AsyncSocket*>*>
(data)->data();
AcceptConnection(socket);
delete data;
}
void RelayServer::OnTimeout(RelayServerBinding* binding) {
// This call will result in all of the necessary clean-up. We can't call
// delete here, because you can't delete an object that is signaling you.
thread_->Dispose(binding);
}
void RelayServer::AcceptConnection(rtc::AsyncSocket* server_socket) {
// Check if someone is trying to connect to us.
rtc::SocketAddress accept_addr;
rtc::AsyncSocket* accepted_socket =
server_socket->Accept(&accept_addr);
if (accepted_socket != NULL) {
// We had someone trying to connect, now check which protocol to
// use and create a packet socket.
ASSERT(server_sockets_[server_socket] == cricket::PROTO_TCP ||
server_sockets_[server_socket] == cricket::PROTO_SSLTCP);
if (server_sockets_[server_socket] == cricket::PROTO_SSLTCP) {
accepted_socket = new rtc::AsyncSSLServerSocket(accepted_socket);
}
rtc::AsyncTCPSocket* tcp_socket =
new rtc::AsyncTCPSocket(accepted_socket, false);
// Finally add the socket so it can start communicating with the client.
AddInternalSocket(tcp_socket);
}
}
RelayServerConnection::RelayServerConnection(
RelayServerBinding* binding, const rtc::SocketAddressPair& addrs,
rtc::AsyncPacketSocket* socket)
: binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) {
// The creation of a new connection constitutes a use of the binding.
binding_->NoteUsed();
}
RelayServerConnection::~RelayServerConnection() {
// Remove this connection from the server's map (if it exists there).
binding_->server()->RemoveConnection(this);
}
void RelayServerConnection::Send(const char* data, size_t size) {
// Note that the binding has been used again.
binding_->NoteUsed();
cricket::Send(socket_, data, size, addr_pair_.source());
}
void RelayServerConnection::Send(
const char* data, size_t size, const rtc::SocketAddress& from_addr) {
// If the from address is known to the client, we don't need to send it.
if (locked() && (from_addr == default_dest_)) {
Send(data, size);
return;
}
// Wrap the given data in a data-indication packet.
RelayMessage msg;
msg.SetType(STUN_DATA_INDICATION);
StunByteStringAttribute* magic_cookie_attr =
StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(),
binding_->magic_cookie().size());
msg.AddAttribute(magic_cookie_attr);
StunAddressAttribute* addr_attr =
StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2);
addr_attr->SetIP(from_addr.ipaddr());
addr_attr->SetPort(from_addr.port());
msg.AddAttribute(addr_attr);
StunByteStringAttribute* data_attr =
StunAttribute::CreateByteString(STUN_ATTR_DATA);
ASSERT(size <= 65536);
data_attr->CopyBytes(data, uint16(size));
msg.AddAttribute(data_attr);
SendStun(msg);
}
void RelayServerConnection::SendStun(const StunMessage& msg) {
// Note that the binding has been used again.
binding_->NoteUsed();
cricket::SendStun(msg, socket_, addr_pair_.source());
}
void RelayServerConnection::SendStunError(
const StunMessage& request, int error_code, const char* error_desc) {
// An error does not indicate use. If no legitimate use off the binding
// occurs, we want it to be cleaned up even if errors are still occuring.
cricket::SendStunError(
request, socket_, addr_pair_.source(), error_code, error_desc,
binding_->magic_cookie());
}
void RelayServerConnection::Lock() {
locked_ = true;
}
void RelayServerConnection::Unlock() {
locked_ = false;
}
// IDs used for posted messages:
const uint32 MSG_LIFETIME_TIMER = 1;
RelayServerBinding::RelayServerBinding(
RelayServer* server, const std::string& username,
const std::string& password, uint32 lifetime)
: server_(server), username_(username), password_(password),
lifetime_(lifetime) {
// For now, every connection uses the standard magic cookie value.
magic_cookie_.append(
reinterpret_cast<const char*>(TURN_MAGIC_COOKIE_VALUE),
sizeof(TURN_MAGIC_COOKIE_VALUE));
// Initialize the last-used time to now.
NoteUsed();
// Set the first timeout check.
server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER);
}
RelayServerBinding::~RelayServerBinding() {
// Clear the outstanding timeout check.
server_->thread()->Clear(this);
// Clean up all of the connections.
for (size_t i = 0; i < internal_connections_.size(); ++i)
delete internal_connections_[i];
for (size_t i = 0; i < external_connections_.size(); ++i)
delete external_connections_[i];
// Remove this binding from the server's map.
server_->RemoveBinding(this);
}
void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) {
internal_connections_.push_back(conn);
}
void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) {
external_connections_.push_back(conn);
}
void RelayServerBinding::NoteUsed() {
last_used_ = rtc::Time();
}
bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const {
if (size < 24 + magic_cookie_.size()) {
return false;
} else {
return memcmp(bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()) == 0;
}
}
RelayServerConnection* RelayServerBinding::GetInternalConnection(
const rtc::SocketAddress& ext_addr) {
// Look for an internal connection that is locked to this address.
for (size_t i = 0; i < internal_connections_.size(); ++i) {
if (internal_connections_[i]->locked() &&
(ext_addr == internal_connections_[i]->default_destination()))
return internal_connections_[i];
}
// If one was not found, we send to the first connection.
ASSERT(internal_connections_.size() > 0);
return internal_connections_[0];
}
RelayServerConnection* RelayServerBinding::GetExternalConnection(
const rtc::SocketAddress& ext_addr) {
for (size_t i = 0; i < external_connections_.size(); ++i) {
if (ext_addr == external_connections_[i]->addr_pair().source())
return external_connections_[i];
}
return 0;
}
void RelayServerBinding::OnMessage(rtc::Message *pmsg) {
if (pmsg->message_id == MSG_LIFETIME_TIMER) {
ASSERT(!pmsg->pdata);
// If the lifetime timeout has been exceeded, then send a signal.
// Otherwise, just keep waiting.
if (rtc::Time() >= last_used_ + lifetime_) {
LOG(LS_INFO) << "Expiring binding " << username_;
SignalTimeout(this);
} else {
server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER);
}
} else {
ASSERT(false);
}
}
} // namespace cricket

View File

@ -1,252 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_RELAYSERVER_H_
#define WEBRTC_P2P_BASE_RELAYSERVER_H_
#include <map>
#include <string>
#include <vector>
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/stun.h"
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/socketaddresspair.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/timeutils.h"
namespace cricket {
class RelayServerBinding;
class RelayServerConnection;
// Relays traffic between connections to the server that are "bound" together.
// All connections created with the same username/password are bound together.
class RelayServer : public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
// Creates a server, which will use this thread to post messages to itself.
explicit RelayServer(rtc::Thread* thread);
~RelayServer();
rtc::Thread* thread() { return thread_; }
// Indicates whether we will print updates of the number of bindings.
bool log_bindings() const { return log_bindings_; }
void set_log_bindings(bool log_bindings) { log_bindings_ = log_bindings; }
// Updates the set of sockets that the server uses to talk to "internal"
// clients. These are clients that do the "port allocations".
void AddInternalSocket(rtc::AsyncPacketSocket* socket);
void RemoveInternalSocket(rtc::AsyncPacketSocket* socket);
// Updates the set of sockets that the server uses to talk to "external"
// clients. These are the clients that do not do allocations. They do not
// know that these addresses represent a relay server.
void AddExternalSocket(rtc::AsyncPacketSocket* socket);
void RemoveExternalSocket(rtc::AsyncPacketSocket* socket);
// Starts listening for connections on this sockets. When someone
// tries to connect, the connection will be accepted and a new
// internal socket will be added.
void AddInternalServerSocket(rtc::AsyncSocket* socket,
cricket::ProtocolType proto);
// Removes this server socket from the list.
void RemoveInternalServerSocket(rtc::AsyncSocket* socket);
// Methods for testing and debuging.
int GetConnectionCount() const;
rtc::SocketAddressPair GetConnection(int connection) const;
bool HasConnection(const rtc::SocketAddress& address) const;
private:
typedef std::vector<rtc::AsyncPacketSocket*> SocketList;
typedef std::map<rtc::AsyncSocket*,
cricket::ProtocolType> ServerSocketMap;
typedef std::map<std::string, RelayServerBinding*> BindingMap;
typedef std::map<rtc::SocketAddressPair,
RelayServerConnection*> ConnectionMap;
rtc::Thread* thread_;
bool log_bindings_;
SocketList internal_sockets_;
SocketList external_sockets_;
SocketList removed_sockets_;
ServerSocketMap server_sockets_;
BindingMap bindings_;
ConnectionMap connections_;
// Called when a packet is received by the server on one of its sockets.
void OnInternalPacket(rtc::AsyncPacketSocket* socket,
const char* bytes, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
void OnExternalPacket(rtc::AsyncPacketSocket* socket,
const char* bytes, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
void OnReadEvent(rtc::AsyncSocket* socket);
// Processes the relevant STUN request types from the client.
bool HandleStun(const char* bytes, size_t size,
const rtc::SocketAddress& remote_addr,
rtc::AsyncPacketSocket* socket,
std::string* username, StunMessage* msg);
void HandleStunAllocate(const char* bytes, size_t size,
const rtc::SocketAddressPair& ap,
rtc::AsyncPacketSocket* socket);
void HandleStun(RelayServerConnection* int_conn, const char* bytes,
size_t size);
void HandleStunAllocate(RelayServerConnection* int_conn,
const StunMessage& msg);
void HandleStunSend(RelayServerConnection* int_conn, const StunMessage& msg);
// Adds/Removes the a connection or binding.
void AddConnection(RelayServerConnection* conn);
void RemoveConnection(RelayServerConnection* conn);
void RemoveBinding(RelayServerBinding* binding);
// Handle messages in our worker thread.
void OnMessage(rtc::Message *pmsg);
// Called when the timer for checking lifetime times out.
void OnTimeout(RelayServerBinding* binding);
// Accept connections on this server socket.
void AcceptConnection(rtc::AsyncSocket* server_socket);
friend class RelayServerConnection;
friend class RelayServerBinding;
};
// Maintains information about a connection to the server. Each connection is
// part of one and only one binding.
class RelayServerConnection {
public:
RelayServerConnection(RelayServerBinding* binding,
const rtc::SocketAddressPair& addrs,
rtc::AsyncPacketSocket* socket);
~RelayServerConnection();
RelayServerBinding* binding() { return binding_; }
rtc::AsyncPacketSocket* socket() { return socket_; }
// Returns a pair where the source is the remote address and the destination
// is the local address.
const rtc::SocketAddressPair& addr_pair() { return addr_pair_; }
// Sends a packet to the connected client. If an address is provided, then
// we make sure the internal client receives it, wrapping if necessary.
void Send(const char* data, size_t size);
void Send(const char* data, size_t size,
const rtc::SocketAddress& ext_addr);
// Sends a STUN message to the connected client with no wrapping.
void SendStun(const StunMessage& msg);
void SendStunError(const StunMessage& request, int code, const char* desc);
// A locked connection is one for which we know the intended destination of
// any raw packet received.
bool locked() const { return locked_; }
void Lock();
void Unlock();
// Records the address that raw packets should be forwarded to (for internal
// packets only; for external, we already know where they go).
const rtc::SocketAddress& default_destination() const {
return default_dest_;
}
void set_default_destination(const rtc::SocketAddress& addr) {
default_dest_ = addr;
}
private:
RelayServerBinding* binding_;
rtc::SocketAddressPair addr_pair_;
rtc::AsyncPacketSocket* socket_;
bool locked_;
rtc::SocketAddress default_dest_;
};
// Records a set of internal and external connections that we relay between,
// or in other words, that are "bound" together.
class RelayServerBinding : public rtc::MessageHandler {
public:
RelayServerBinding(
RelayServer* server, const std::string& username,
const std::string& password, uint32 lifetime);
virtual ~RelayServerBinding();
RelayServer* server() { return server_; }
uint32 lifetime() { return lifetime_; }
const std::string& username() { return username_; }
const std::string& password() { return password_; }
const std::string& magic_cookie() { return magic_cookie_; }
// Adds/Removes a connection into the binding.
void AddInternalConnection(RelayServerConnection* conn);
void AddExternalConnection(RelayServerConnection* conn);
// We keep track of the use of each binding. If we detect that it was not
// used for longer than the lifetime, then we send a signal.
void NoteUsed();
sigslot::signal1<RelayServerBinding*> SignalTimeout;
// Determines whether the given packet has the magic cookie present (in the
// right place).
bool HasMagicCookie(const char* bytes, size_t size) const;
// Determines the connection to use to send packets to or from the given
// external address.
RelayServerConnection* GetInternalConnection(
const rtc::SocketAddress& ext_addr);
RelayServerConnection* GetExternalConnection(
const rtc::SocketAddress& ext_addr);
// MessageHandler:
void OnMessage(rtc::Message *pmsg);
private:
RelayServer* server_;
std::string username_;
std::string password_;
std::string magic_cookie_;
std::vector<RelayServerConnection*> internal_connections_;
std::vector<RelayServerConnection*> external_connections_;
uint32 lifetime_;
uint32 last_used_;
// TODO: bandwidth
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_RELAYSERVER_H_

View File

@ -1,536 +0,0 @@
/*
* libjingle
* Copyright 2004 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 <string>
#include "webrtc/p2p/base/relayserver.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/testclient.h"
#include "webrtc/base/thread.h"
using rtc::SocketAddress;
using namespace cricket;
static const uint32 LIFETIME = 4; // seconds
static const SocketAddress server_int_addr("127.0.0.1", 5000);
static const SocketAddress server_ext_addr("127.0.0.1", 5001);
static const SocketAddress client1_addr("127.0.0.1", 6000 + (rand() % 1000));
static const SocketAddress client2_addr("127.0.0.1", 7000 + (rand() % 1000));
static const char* bad = "this is a completely nonsensical message whose only "
"purpose is to make the parser go 'ack'. it doesn't "
"look anything like a normal stun message";
static const char* msg1 = "spamspamspamspamspamspamspambakedbeansspam";
static const char* msg2 = "Lobster Thermidor a Crevette with a mornay sauce...";
class RelayServerTest : public testing::Test {
public:
RelayServerTest()
: main_(rtc::Thread::Current()), ss_(main_->socketserver()),
username_(rtc::CreateRandomString(12)),
password_(rtc::CreateRandomString(12)) {
}
protected:
virtual void SetUp() {
server_.reset(new RelayServer(main_));
server_->AddInternalSocket(
rtc::AsyncUDPSocket::Create(ss_, server_int_addr));
server_->AddExternalSocket(
rtc::AsyncUDPSocket::Create(ss_, server_ext_addr));
client1_.reset(new rtc::TestClient(
rtc::AsyncUDPSocket::Create(ss_, client1_addr)));
client2_.reset(new rtc::TestClient(
rtc::AsyncUDPSocket::Create(ss_, client2_addr)));
}
void Allocate() {
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_ALLOCATE_REQUEST));
AddUsernameAttr(req.get(), username_);
AddLifetimeAttr(req.get(), LIFETIME);
Send1(req.get());
delete Receive1();
}
void Bind() {
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_BINDING_REQUEST));
AddUsernameAttr(req.get(), username_);
Send2(req.get());
delete Receive1();
}
void Send1(const StunMessage* msg) {
rtc::ByteBuffer buf;
msg->Write(&buf);
SendRaw1(buf.Data(), static_cast<int>(buf.Length()));
}
void Send2(const StunMessage* msg) {
rtc::ByteBuffer buf;
msg->Write(&buf);
SendRaw2(buf.Data(), static_cast<int>(buf.Length()));
}
void SendRaw1(const char* data, int len) {
return Send(client1_.get(), data, len, server_int_addr);
}
void SendRaw2(const char* data, int len) {
return Send(client2_.get(), data, len, server_ext_addr);
}
void Send(rtc::TestClient* client, const char* data,
int len, const SocketAddress& addr) {
client->SendTo(data, len, addr);
}
StunMessage* Receive1() {
return Receive(client1_.get());
}
StunMessage* Receive2() {
return Receive(client2_.get());
}
std::string ReceiveRaw1() {
return ReceiveRaw(client1_.get());
}
std::string ReceiveRaw2() {
return ReceiveRaw(client2_.get());
}
StunMessage* Receive(rtc::TestClient* client) {
StunMessage* msg = NULL;
rtc::TestClient::Packet* packet = client->NextPacket();
if (packet) {
rtc::ByteBuffer buf(packet->buf, packet->size);
msg = new RelayMessage();
msg->Read(&buf);
delete packet;
}
return msg;
}
std::string ReceiveRaw(rtc::TestClient* client) {
std::string raw;
rtc::TestClient::Packet* packet = client->NextPacket();
if (packet) {
raw = std::string(packet->buf, packet->size);
delete packet;
}
return raw;
}
static StunMessage* CreateStunMessage(int type) {
StunMessage* msg = new RelayMessage();
msg->SetType(type);
msg->SetTransactionID(
rtc::CreateRandomString(kStunTransactionIdLength));
return msg;
}
static void AddMagicCookieAttr(StunMessage* msg) {
StunByteStringAttribute* attr =
StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
attr->CopyBytes(TURN_MAGIC_COOKIE_VALUE, sizeof(TURN_MAGIC_COOKIE_VALUE));
msg->AddAttribute(attr);
}
static void AddUsernameAttr(StunMessage* msg, const std::string& val) {
StunByteStringAttribute* attr =
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
attr->CopyBytes(val.c_str(), val.size());
msg->AddAttribute(attr);
}
static void AddLifetimeAttr(StunMessage* msg, int val) {
StunUInt32Attribute* attr =
StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME);
attr->SetValue(val);
msg->AddAttribute(attr);
}
static void AddDestinationAttr(StunMessage* msg, const SocketAddress& addr) {
StunAddressAttribute* attr =
StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
attr->SetIP(addr.ipaddr());
attr->SetPort(addr.port());
msg->AddAttribute(attr);
}
rtc::Thread* main_;
rtc::SocketServer* ss_;
rtc::scoped_ptr<RelayServer> server_;
rtc::scoped_ptr<rtc::TestClient> client1_;
rtc::scoped_ptr<rtc::TestClient> client2_;
std::string username_;
std::string password_;
};
// Send a complete nonsense message and verify that it is eaten.
TEST_F(RelayServerTest, TestBadRequest) {
rtc::scoped_ptr<StunMessage> res;
SendRaw1(bad, static_cast<int>(strlen(bad)));
res.reset(Receive1());
ASSERT_TRUE(!res);
}
// Send an allocate request without a username and verify it is rejected.
TEST_F(RelayServerTest, TestAllocateNoUsername) {
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_ALLOCATE_REQUEST)), res;
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_ALLOCATE_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(4, err->eclass());
EXPECT_EQ(32, err->number());
EXPECT_EQ("Missing Username", err->reason());
}
// Send a binding request and verify that it is rejected.
TEST_F(RelayServerTest, TestBindingRequest) {
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_BINDING_REQUEST)), res;
AddUsernameAttr(req.get(), username_);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(6, err->eclass());
EXPECT_EQ(0, err->number());
EXPECT_EQ("Operation Not Supported", err->reason());
}
// Send an allocate request and verify that it is accepted.
TEST_F(RelayServerTest, TestAllocate) {
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_ALLOCATE_REQUEST)), res;
AddUsernameAttr(req.get(), username_);
AddLifetimeAttr(req.get(), LIFETIME);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_ALLOCATE_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunAddressAttribute* mapped_addr =
res->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
ASSERT_TRUE(mapped_addr != NULL);
EXPECT_EQ(1, mapped_addr->family());
EXPECT_EQ(server_ext_addr.port(), mapped_addr->port());
EXPECT_EQ(server_ext_addr.ipaddr(), mapped_addr->ipaddr());
const StunUInt32Attribute* res_lifetime_attr =
res->GetUInt32(STUN_ATTR_LIFETIME);
ASSERT_TRUE(res_lifetime_attr != NULL);
EXPECT_EQ(LIFETIME, res_lifetime_attr->value());
}
// Send a second allocate request and verify that it is also accepted, though
// the lifetime should be ignored.
TEST_F(RelayServerTest, TestReallocate) {
Allocate();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_ALLOCATE_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), username_);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_ALLOCATE_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunAddressAttribute* mapped_addr =
res->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
ASSERT_TRUE(mapped_addr != NULL);
EXPECT_EQ(1, mapped_addr->family());
EXPECT_EQ(server_ext_addr.port(), mapped_addr->port());
EXPECT_EQ(server_ext_addr.ipaddr(), mapped_addr->ipaddr());
const StunUInt32Attribute* lifetime_attr =
res->GetUInt32(STUN_ATTR_LIFETIME);
ASSERT_TRUE(lifetime_attr != NULL);
EXPECT_EQ(LIFETIME, lifetime_attr->value());
}
// Send a request from another client and see that it arrives at the first
// client in the binding.
TEST_F(RelayServerTest, TestRemoteBind) {
Allocate();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_BINDING_REQUEST)), res;
AddUsernameAttr(req.get(), username_);
Send2(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_DATA_INDICATION, res->type());
const StunByteStringAttribute* recv_data =
res->GetByteString(STUN_ATTR_DATA);
ASSERT_TRUE(recv_data != NULL);
rtc::ByteBuffer buf(recv_data->bytes(), recv_data->length());
rtc::scoped_ptr<StunMessage> res2(new StunMessage());
EXPECT_TRUE(res2->Read(&buf));
EXPECT_EQ(STUN_BINDING_REQUEST, res2->type());
EXPECT_EQ(req->transaction_id(), res2->transaction_id());
const StunAddressAttribute* src_addr =
res->GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
ASSERT_TRUE(src_addr != NULL);
EXPECT_EQ(1, src_addr->family());
EXPECT_EQ(client2_addr.ipaddr(), src_addr->ipaddr());
EXPECT_EQ(client2_addr.port(), src_addr->port());
EXPECT_TRUE(Receive2() == NULL);
}
// Send a complete nonsense message to the established connection and verify
// that it is dropped by the server.
TEST_F(RelayServerTest, TestRemoteBadRequest) {
Allocate();
Bind();
SendRaw1(bad, static_cast<int>(strlen(bad)));
EXPECT_TRUE(Receive1() == NULL);
EXPECT_TRUE(Receive2() == NULL);
}
// Send a send request without a username and verify it is rejected.
TEST_F(RelayServerTest, TestSendRequestMissingUsername) {
Allocate();
Bind();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_SEND_REQUEST)), res;
AddMagicCookieAttr(req.get());
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_SEND_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(4, err->eclass());
EXPECT_EQ(32, err->number());
EXPECT_EQ("Missing Username", err->reason());
}
// Send a send request with the wrong username and verify it is rejected.
TEST_F(RelayServerTest, TestSendRequestBadUsername) {
Allocate();
Bind();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_SEND_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), "foobarbizbaz");
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_SEND_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(4, err->eclass());
EXPECT_EQ(30, err->number());
EXPECT_EQ("Stale Credentials", err->reason());
}
// Send a send request without a destination address and verify that it is
// rejected.
TEST_F(RelayServerTest, TestSendRequestNoDestinationAddress) {
Allocate();
Bind();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_SEND_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), username_);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_SEND_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(4, err->eclass());
EXPECT_EQ(0, err->number());
EXPECT_EQ("Bad Request", err->reason());
}
// Send a send request without data and verify that it is rejected.
TEST_F(RelayServerTest, TestSendRequestNoData) {
Allocate();
Bind();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_SEND_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), username_);
AddDestinationAttr(req.get(), client2_addr);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_SEND_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(4, err->eclass());
EXPECT_EQ(00, err->number());
EXPECT_EQ("Bad Request", err->reason());
}
// Send a binding request after an allocate and verify that it is rejected.
TEST_F(RelayServerTest, TestSendRequestWrongType) {
Allocate();
Bind();
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_BINDING_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), username_);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, res->type());
EXPECT_EQ(req->transaction_id(), res->transaction_id());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(6, err->eclass());
EXPECT_EQ(0, err->number());
EXPECT_EQ("Operation Not Supported", err->reason());
}
// Verify that we can send traffic back and forth between the clients after a
// successful allocate and bind.
TEST_F(RelayServerTest, TestSendRaw) {
Allocate();
Bind();
for (int i = 0; i < 10; i++) {
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_SEND_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), username_);
AddDestinationAttr(req.get(), client2_addr);
StunByteStringAttribute* send_data =
StunAttribute::CreateByteString(STUN_ATTR_DATA);
send_data->CopyBytes(msg1);
req->AddAttribute(send_data);
Send1(req.get());
EXPECT_EQ(msg1, ReceiveRaw2());
SendRaw2(msg2, static_cast<int>(strlen(msg2)));
res.reset(Receive1());
ASSERT_TRUE(res);
EXPECT_EQ(STUN_DATA_INDICATION, res->type());
const StunAddressAttribute* src_addr =
res->GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
ASSERT_TRUE(src_addr != NULL);
EXPECT_EQ(1, src_addr->family());
EXPECT_EQ(client2_addr.ipaddr(), src_addr->ipaddr());
EXPECT_EQ(client2_addr.port(), src_addr->port());
const StunByteStringAttribute* recv_data =
res->GetByteString(STUN_ATTR_DATA);
ASSERT_TRUE(recv_data != NULL);
EXPECT_EQ(strlen(msg2), recv_data->length());
EXPECT_EQ(0, memcmp(msg2, recv_data->bytes(), recv_data->length()));
}
}
// Verify that a binding expires properly, and rejects send requests.
TEST_F(RelayServerTest, TestExpiration) {
Allocate();
Bind();
// Wait twice the lifetime to make sure the server has expired the binding.
rtc::Thread::Current()->ProcessMessages((LIFETIME * 2) * 1000);
rtc::scoped_ptr<StunMessage> req(
CreateStunMessage(STUN_SEND_REQUEST)), res;
AddMagicCookieAttr(req.get());
AddUsernameAttr(req.get(), username_);
AddDestinationAttr(req.get(), client2_addr);
StunByteStringAttribute* data_attr =
StunAttribute::CreateByteString(STUN_ATTR_DATA);
data_attr->CopyBytes(msg1);
req->AddAttribute(data_attr);
Send1(req.get());
res.reset(Receive1());
ASSERT_TRUE(res.get() != NULL);
EXPECT_EQ(STUN_SEND_ERROR_RESPONSE, res->type());
const StunErrorCodeAttribute* err = res->GetErrorCode();
ASSERT_TRUE(err != NULL);
EXPECT_EQ(6, err->eclass());
EXPECT_EQ(0, err->number());
EXPECT_EQ("Operation Not Supported", err->reason());
// Also verify that traffic from the external client is ignored.
SendRaw2(msg2, static_cast<int>(strlen(msg2)));
EXPECT_TRUE(ReceiveRaw1().empty());
}

File diff suppressed because it is too large Load Diff

View File

@ -1,747 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_SESSION_H_
#define WEBRTC_P2P_BASE_SESSION_H_
#include <list>
#include <map>
#include <string>
#include <vector>
#include "webrtc/p2p/base/parsing.h"
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/sessionclient.h"
#include "webrtc/p2p/base/sessionmanager.h"
#include "webrtc/p2p/base/sessionmessages.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/base/socketaddress.h"
namespace cricket {
class BaseSession;
class P2PTransportChannel;
class Transport;
class TransportChannel;
class TransportChannelProxy;
class TransportChannelImpl;
typedef rtc::RefCountedObject<rtc::scoped_ptr<Transport> >
TransportWrapper;
// Used for errors that will send back a specific error message to the
// remote peer. We add "type" to the errors because it's needed for
// SignalErrorMessage.
struct MessageError : ParseError {
buzz::QName type;
// if unset, assume type is a parse error
MessageError() : ParseError(), type(buzz::QN_STANZA_BAD_REQUEST) {}
void SetType(const buzz::QName type) {
this->type = type;
}
};
// Used for errors that may be returned by public session methods that
// can fail.
// TODO: Use this error in Session::Initiate and
// Session::Accept.
struct SessionError : WriteError {
};
// Bundles a Transport and ChannelMap together. ChannelMap is used to
// create transport channels before receiving or sending a session
// initiate, and for speculatively connecting channels. Previously, a
// session had one ChannelMap and transport. Now, with multiple
// transports per session, we need multiple ChannelMaps as well.
typedef std::map<int, TransportChannelProxy*> ChannelMap;
class TransportProxy : public sigslot::has_slots<>,
public CandidateTranslator {
public:
TransportProxy(
rtc::Thread* worker_thread,
const std::string& sid,
const std::string& content_name,
TransportWrapper* transport)
: worker_thread_(worker_thread),
sid_(sid),
content_name_(content_name),
transport_(transport),
connecting_(false),
negotiated_(false),
sent_candidates_(false),
candidates_allocated_(false),
local_description_set_(false),
remote_description_set_(false) {
transport_->get()->SignalCandidatesReady.connect(
this, &TransportProxy::OnTransportCandidatesReady);
}
~TransportProxy();
const std::string& content_name() const { return content_name_; }
// TODO(juberti): It's not good form to expose the object you're wrapping,
// since callers can mutate it. Can we make this return a const Transport*?
Transport* impl() const { return transport_->get(); }
const std::string& type() const;
bool negotiated() const { return negotiated_; }
const Candidates& sent_candidates() const { return sent_candidates_; }
const Candidates& unsent_candidates() const { return unsent_candidates_; }
bool candidates_allocated() const { return candidates_allocated_; }
void set_candidates_allocated(bool allocated) {
candidates_allocated_ = allocated;
}
TransportChannel* GetChannel(int component);
TransportChannel* CreateChannel(const std::string& channel_name,
int component);
bool HasChannel(int component);
void DestroyChannel(int component);
void AddSentCandidates(const Candidates& candidates);
void AddUnsentCandidates(const Candidates& candidates);
void ClearSentCandidates() { sent_candidates_.clear(); }
void ClearUnsentCandidates() { unsent_candidates_.clear(); }
// Start the connection process for any channels, creating impls if needed.
void ConnectChannels();
// Hook up impls to the proxy channels. Doesn't change connect state.
void CompleteNegotiation();
// Mux this proxy onto the specified proxy's transport.
bool SetupMux(TransportProxy* proxy);
// Simple functions that thunk down to the same functions on Transport.
void SetIceRole(IceRole role);
void SetIdentity(rtc::SSLIdentity* identity);
bool SetLocalTransportDescription(const TransportDescription& description,
ContentAction action,
std::string* error_desc);
bool SetRemoteTransportDescription(const TransportDescription& description,
ContentAction action,
std::string* error_desc);
void OnSignalingReady();
bool OnRemoteCandidates(const Candidates& candidates, std::string* error);
// CandidateTranslator methods.
virtual bool GetChannelNameFromComponent(
int component, std::string* channel_name) const;
virtual bool GetComponentFromChannelName(
const std::string& channel_name, int* component) const;
// Called when a transport signals that it has new candidates.
void OnTransportCandidatesReady(cricket::Transport* transport,
const Candidates& candidates) {
SignalCandidatesReady(this, candidates);
}
bool local_description_set() const {
return local_description_set_;
}
bool remote_description_set() const {
return remote_description_set_;
}
// Handles sending of ready candidates and receiving of remote candidates.
sigslot::signal2<TransportProxy*,
const std::vector<Candidate>&> SignalCandidatesReady;
private:
TransportChannelProxy* GetChannelProxy(int component) const;
TransportChannelProxy* GetChannelProxyByName(const std::string& name) const;
TransportChannelImpl* GetOrCreateChannelProxyImpl(int component);
TransportChannelImpl* GetOrCreateChannelProxyImpl_w(int component);
// Manipulators of transportchannelimpl in channel proxy.
void SetupChannelProxy(int component,
TransportChannelProxy* proxy);
void SetupChannelProxy_w(int component,
TransportChannelProxy* proxy);
void ReplaceChannelProxyImpl(TransportChannelProxy* proxy,
TransportChannelImpl* impl);
void ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy,
TransportChannelImpl* impl);
rtc::Thread* const worker_thread_;
const std::string sid_;
const std::string content_name_;
rtc::scoped_refptr<TransportWrapper> transport_;
bool connecting_;
bool negotiated_;
ChannelMap channels_;
Candidates sent_candidates_;
Candidates unsent_candidates_;
bool candidates_allocated_;
bool local_description_set_;
bool remote_description_set_;
};
typedef std::map<std::string, TransportProxy*> TransportMap;
// Statistics for all the transports of this session.
typedef std::map<std::string, TransportStats> TransportStatsMap;
typedef std::map<std::string, std::string> ProxyTransportMap;
struct SessionStats {
ProxyTransportMap proxy_to_transport;
TransportStatsMap transport_stats;
};
// A BaseSession manages general session state. This includes negotiation
// of both the application-level and network-level protocols: the former
// defines what will be sent and the latter defines how it will be sent. Each
// network-level protocol is represented by a Transport object. Each Transport
// participates in the network-level negotiation. The individual streams of
// packets are represented by TransportChannels. The application-level protocol
// is represented by SessionDecription objects.
class BaseSession : public sigslot::has_slots<>,
public rtc::MessageHandler {
public:
enum {
MSG_TIMEOUT = 0,
MSG_ERROR,
MSG_STATE,
};
enum State {
STATE_INIT = 0,
STATE_SENTINITIATE, // sent initiate, waiting for Accept or Reject
STATE_RECEIVEDINITIATE, // received an initiate. Call Accept or Reject
STATE_SENTPRACCEPT, // sent provisional Accept
STATE_SENTACCEPT, // sent accept. begin connecting transport
STATE_RECEIVEDPRACCEPT, // received provisional Accept, waiting for Accept
STATE_RECEIVEDACCEPT, // received accept. begin connecting transport
STATE_SENTMODIFY, // sent modify, waiting for Accept or Reject
STATE_RECEIVEDMODIFY, // received modify, call Accept or Reject
STATE_SENTREJECT, // sent reject after receiving initiate
STATE_RECEIVEDREJECT, // received reject after sending initiate
STATE_SENTREDIRECT, // sent direct after receiving initiate
STATE_SENTTERMINATE, // sent terminate (any time / either side)
STATE_RECEIVEDTERMINATE, // received terminate (any time / either side)
STATE_INPROGRESS, // session accepted and in progress
STATE_DEINIT, // session is being destroyed
};
enum Error {
ERROR_NONE = 0, // no error
ERROR_TIME = 1, // no response to signaling
ERROR_RESPONSE = 2, // error during signaling
ERROR_NETWORK = 3, // network error, could not allocate network resources
ERROR_CONTENT = 4, // channel errors in SetLocalContent/SetRemoteContent
ERROR_TRANSPORT = 5, // transport error of some kind
};
// Convert State to a readable string.
static std::string StateToString(State state);
BaseSession(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
PortAllocator* port_allocator,
const std::string& sid,
const std::string& content_type,
bool initiator);
virtual ~BaseSession();
// These are const to allow them to be called from const methods.
rtc::Thread* signaling_thread() const { return signaling_thread_; }
rtc::Thread* worker_thread() const { return worker_thread_; }
PortAllocator* port_allocator() const { return port_allocator_; }
// The ID of this session.
const std::string& id() const { return sid_; }
// TODO(juberti): This data is largely redundant, as it can now be obtained
// from local/remote_description(). Remove these functions and members.
// Returns the XML namespace identifying the type of this session.
const std::string& content_type() const { return content_type_; }
// Returns the XML namespace identifying the transport used for this session.
const std::string& transport_type() const { return transport_type_; }
// Indicates whether we initiated this session.
bool initiator() const { return initiator_; }
// Returns the application-level description given by our client.
// If we are the recipient, this will be NULL until we send an accept.
const SessionDescription* local_description() const;
// Returns the application-level description given by the other client.
// If we are the initiator, this will be NULL until we receive an accept.
const SessionDescription* remote_description() const;
SessionDescription* remote_description();
// Takes ownership of SessionDescription*
void set_local_description(const SessionDescription* sdesc);
// Takes ownership of SessionDescription*
void set_remote_description(SessionDescription* sdesc);
const SessionDescription* initiator_description() const;
// Returns the current state of the session. See the enum above for details.
// Each time the state changes, we will fire this signal.
State state() const { return state_; }
sigslot::signal2<BaseSession* , State> SignalState;
// Returns the last error in the session. See the enum above for details.
// Each time the an error occurs, we will fire this signal.
Error error() const { return error_; }
const std::string& error_desc() const { return error_desc_; }
sigslot::signal2<BaseSession* , Error> SignalError;
// Updates the state, signaling if necessary.
virtual void SetState(State state);
// Updates the error state, signaling if necessary.
// TODO(ronghuawu): remove the SetError method that doesn't take |error_desc|.
virtual void SetError(Error error, const std::string& error_desc);
// Fired when the remote description is updated, with the updated
// contents.
sigslot::signal2<BaseSession* , const ContentInfos&>
SignalRemoteDescriptionUpdate;
// Fired when SetState is called (regardless if there's a state change), which
// indicates the session description might have be updated.
sigslot::signal2<BaseSession*, ContentAction> SignalNewLocalDescription;
// Fired when SetState is called (regardless if there's a state change), which
// indicates the session description might have be updated.
sigslot::signal2<BaseSession*, ContentAction> SignalNewRemoteDescription;
// Returns the transport that has been negotiated or NULL if
// negotiation is still in progress.
virtual Transport* GetTransport(const std::string& content_name);
// Creates a new channel with the given names. This method may be called
// immediately after creating the session. However, the actual
// implementation may not be fixed until transport negotiation completes.
// This will usually be called from the worker thread, but that
// shouldn't be an issue since the main thread will be blocked in
// Send when doing so.
virtual TransportChannel* CreateChannel(const std::string& content_name,
const std::string& channel_name,
int component);
// Returns the channel with the given names.
virtual TransportChannel* GetChannel(const std::string& content_name,
int component);
// Destroys the channel with the given names.
// This will usually be called from the worker thread, but that
// shouldn't be an issue since the main thread will be blocked in
// Send when doing so.
virtual void DestroyChannel(const std::string& content_name,
int component);
// Returns stats for all channels of all transports.
// This avoids exposing the internal structures used to track them.
virtual bool GetStats(SessionStats* stats);
rtc::SSLIdentity* identity() { return identity_; }
protected:
// Specifies the identity to use in this session.
bool SetIdentity(rtc::SSLIdentity* identity);
bool PushdownTransportDescription(ContentSource source,
ContentAction action,
std::string* error_desc);
void set_initiator(bool initiator) { initiator_ = initiator; }
const TransportMap& transport_proxies() const { return transports_; }
// Get a TransportProxy by content_name or transport. NULL if not found.
TransportProxy* GetTransportProxy(const std::string& content_name);
TransportProxy* GetTransportProxy(const Transport* transport);
TransportProxy* GetFirstTransportProxy();
void DestroyTransportProxy(const std::string& content_name);
// TransportProxy is owned by session. Return proxy just for convenience.
TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
// Creates the actual transport object. Overridable for testing.
virtual Transport* CreateTransport(const std::string& content_name);
void OnSignalingReady();
void SpeculativelyConnectAllTransportChannels();
// Helper method to provide remote candidates to the transport.
bool OnRemoteCandidates(const std::string& content_name,
const Candidates& candidates,
std::string* error);
// This method will mux transport channels by content_name.
// First content is used for muxing.
bool MaybeEnableMuxingSupport();
// Called when a transport requests signaling.
virtual void OnTransportRequestSignaling(Transport* transport) {
}
// Called when the first channel of a transport begins connecting. We use
// this to start a timer, to make sure that the connection completes in a
// reasonable amount of time.
virtual void OnTransportConnecting(Transport* transport) {
}
// Called when a transport changes its writable state. We track this to make
// sure that the transport becomes writable within a reasonable amount of
// time. If this does not occur, we signal an error.
virtual void OnTransportWritable(Transport* transport) {
}
virtual void OnTransportReadable(Transport* transport) {
}
// Called when a transport has found its steady-state connections.
virtual void OnTransportCompleted(Transport* transport) {
}
// Called when a transport has failed permanently.
virtual void OnTransportFailed(Transport* transport) {
}
// Called when a transport signals that it has new candidates.
virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy,
const Candidates& candidates) {
}
// Called when a transport signals that it found an error in an incoming
// message.
virtual void OnTransportSendError(Transport* transport,
const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info) {
}
virtual void OnTransportRouteChange(
Transport* transport,
int component,
const cricket::Candidate& remote_candidate) {
}
virtual void OnTransportCandidatesAllocationDone(Transport* transport);
// Called when all transport channels allocated required candidates.
// This method should be used as an indication of candidates gathering process
// is completed and application can now send local candidates list to remote.
virtual void OnCandidatesAllocationDone() {
}
// Handles the ice role change callback from Transport. This must be
// propagated to all the transports.
virtual void OnRoleConflict();
// Handles messages posted to us.
virtual void OnMessage(rtc::Message *pmsg);
protected:
State state_;
Error error_;
std::string error_desc_;
private:
// Helper methods to push local and remote transport descriptions.
bool PushdownLocalTransportDescription(
const SessionDescription* sdesc, ContentAction action,
std::string* error_desc);
bool PushdownRemoteTransportDescription(
const SessionDescription* sdesc, ContentAction action,
std::string* error_desc);
bool IsCandidateAllocationDone() const;
void MaybeCandidateAllocationDone();
// This method will delete the Transport and TransportChannelImpls and
// replace those with the selected Transport objects. Selection is done
// based on the content_name and in this case first MediaContent information
// is used for mux.
bool SetSelectedProxy(const std::string& content_name,
const ContentGroup* muxed_group);
// Log session state.
void LogState(State old_state, State new_state);
// Returns true and the TransportInfo of the given |content_name|
// from |description|. Returns false if it's not available.
static bool GetTransportDescription(const SessionDescription* description,
const std::string& content_name,
TransportDescription* info);
// Fires the new description signal according to the current state.
void SignalNewDescription();
// Gets the ContentAction and ContentSource according to the session state.
bool GetContentAction(ContentAction* action, ContentSource* source);
rtc::Thread* const signaling_thread_;
rtc::Thread* const worker_thread_;
PortAllocator* const port_allocator_;
const std::string sid_;
const std::string content_type_;
const std::string transport_type_;
bool initiator_;
rtc::SSLIdentity* identity_;
rtc::scoped_ptr<const SessionDescription> local_description_;
rtc::scoped_ptr<SessionDescription> remote_description_;
uint64 ice_tiebreaker_;
// This flag will be set to true after the first role switch. This flag
// will enable us to stop any role switch during the call.
bool role_switch_;
TransportMap transports_;
};
// A specific Session created by the SessionManager, using XMPP for protocol.
class Session : public BaseSession {
public:
// Returns the manager that created and owns this session.
SessionManager* session_manager() const { return session_manager_; }
// Returns the client that is handling the application data of this session.
SessionClient* client() const { return client_; }
// Returns the JID of this client.
const std::string& local_name() const { return local_name_; }
// Returns the JID of the other peer in this session.
const std::string& remote_name() const { return remote_name_; }
// Set the JID of the other peer in this session.
// Typically the remote_name_ is set when the session is initiated.
// However, sometimes (e.g when a proxy is used) the peer name is
// known after the BaseSession has been initiated and it must be updated
// explicitly.
void set_remote_name(const std::string& name) { remote_name_ = name; }
// Set the JID of the initiator of this session. Allows for the overriding
// of the initiator to be a third-party, eg. the MUC JID when creating p2p
// sessions.
void set_initiator_name(const std::string& name) { initiator_name_ = name; }
// Indicates the JID of the entity who initiated this session.
// In special cases, may be different than both local_name and remote_name.
const std::string& initiator_name() const { return initiator_name_; }
SignalingProtocol current_protocol() const { return current_protocol_; }
void set_current_protocol(SignalingProtocol protocol) {
current_protocol_ = protocol;
}
// Updates the error state, signaling if necessary.
virtual void SetError(Error error, const std::string& error_desc);
// When the session needs to send signaling messages, it beings by requesting
// signaling. The client should handle this by calling OnSignalingReady once
// it is ready to send the messages.
// (These are called only by SessionManager.)
sigslot::signal1<Session*> SignalRequestSignaling;
void OnSignalingReady() { BaseSession::OnSignalingReady(); }
// Takes ownership of session description.
// TODO: Add an error argument to pass back to the caller.
bool Initiate(const std::string& to,
const SessionDescription* sdesc);
// When we receive an initiate, we create a session in the
// RECEIVEDINITIATE state and respond by accepting or rejecting.
// Takes ownership of session description.
// TODO: Add an error argument to pass back to the caller.
bool Accept(const SessionDescription* sdesc);
bool Reject(const std::string& reason);
bool Terminate() {
return TerminateWithReason(STR_TERMINATE_SUCCESS);
}
bool TerminateWithReason(const std::string& reason);
// Fired whenever we receive a terminate message along with a reason
sigslot::signal2<Session*, const std::string&> SignalReceivedTerminateReason;
// The two clients in the session may also send one another
// arbitrary XML messages, which are called "info" messages. Sending
// takes ownership of the given elements. The signal does not; the
// parent element will be deleted after the signal.
bool SendInfoMessage(const XmlElements& elems,
const std::string& remote_name);
bool SendDescriptionInfoMessage(const ContentInfos& contents);
sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage;
private:
// Creates or destroys a session. (These are called only SessionManager.)
Session(SessionManager *session_manager,
const std::string& local_name, const std::string& initiator_name,
const std::string& sid, const std::string& content_type,
SessionClient* client);
~Session();
// For each transport info, create a transport proxy. Can fail for
// incompatible transport types.
bool CreateTransportProxies(const TransportInfos& tinfos,
SessionError* error);
bool OnRemoteCandidates(const TransportInfos& tinfos,
ParseError* error);
// Returns a TransportInfo without candidates for each content name.
// Uses the transport_type_ of the session.
TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const;
// Maps passed to serialization functions.
TransportParserMap GetTransportParsers();
ContentParserMap GetContentParsers();
CandidateTranslatorMap GetCandidateTranslators();
virtual void OnTransportRequestSignaling(Transport* transport);
virtual void OnTransportConnecting(Transport* transport);
virtual void OnTransportWritable(Transport* transport);
virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy,
const Candidates& candidates);
virtual void OnTransportSendError(Transport* transport,
const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info);
virtual void OnMessage(rtc::Message *pmsg);
// Send various kinds of session messages.
bool SendInitiateMessage(const SessionDescription* sdesc,
SessionError* error);
bool SendAcceptMessage(const SessionDescription* sdesc, SessionError* error);
bool SendRejectMessage(const std::string& reason, SessionError* error);
bool SendTerminateMessage(const std::string& reason, SessionError* error);
bool SendTransportInfoMessage(const TransportInfo& tinfo,
SessionError* error);
bool SendTransportInfoMessage(const TransportProxy* transproxy,
const Candidates& candidates,
SessionError* error);
bool ResendAllTransportInfoMessages(SessionError* error);
bool SendAllUnsentTransportInfoMessages(SessionError* error);
// All versions of SendMessage send a message of the given type to
// the other client. Can pass either a set of elements or an
// "action", which must have a WriteSessionAction method to go along
// with it. Sending with an action supports sending a "hybrid"
// message. Sending with elements must be sent as Jingle or Gingle.
// When passing elems, must be either Jingle or Gingle protocol.
// Takes ownership of action_elems.
bool SendMessage(ActionType type, const XmlElements& action_elems,
SessionError* error);
// Sends a messge, but overrides the remote name.
bool SendMessage(ActionType type, const XmlElements& action_elems,
const std::string& remote_name,
SessionError* error);
// When passing an action, may be Hybrid protocol.
template <typename Action>
bool SendMessage(ActionType type, const Action& action,
SessionError* error);
// Helper methods to write the session message stanza.
template <typename Action>
bool WriteActionMessage(ActionType type, const Action& action,
buzz::XmlElement* stanza, WriteError* error);
template <typename Action>
bool WriteActionMessage(SignalingProtocol protocol,
ActionType type, const Action& action,
buzz::XmlElement* stanza, WriteError* error);
// Sending messages in hybrid form requires being able to write them
// on a per-protocol basis with a common method signature, which all
// of these have.
bool WriteSessionAction(SignalingProtocol protocol,
const SessionInitiate& init,
XmlElements* elems, WriteError* error);
bool WriteSessionAction(SignalingProtocol protocol,
const TransportInfo& tinfo,
XmlElements* elems, WriteError* error);
bool WriteSessionAction(SignalingProtocol protocol,
const SessionTerminate& term,
XmlElements* elems, WriteError* error);
// Sends a message back to the other client indicating that we have received
// and accepted their message.
void SendAcknowledgementMessage(const buzz::XmlElement* stanza);
// Once signaling is ready, the session will use this signal to request the
// sending of each message. When messages are received by the other client,
// they should be handed to OnIncomingMessage.
// (These are called only by SessionManager.)
sigslot::signal2<Session* , const buzz::XmlElement*> SignalOutgoingMessage;
void OnIncomingMessage(const SessionMessage& msg);
void OnIncomingResponse(const buzz::XmlElement* orig_stanza,
const buzz::XmlElement* response_stanza,
const SessionMessage& msg);
void OnInitiateAcked();
void OnFailedSend(const buzz::XmlElement* orig_stanza,
const buzz::XmlElement* error_stanza);
// Invoked when an error is found in an incoming message. This is translated
// into the appropriate XMPP response by SessionManager.
sigslot::signal6<BaseSession*,
const buzz::XmlElement*,
const buzz::QName&,
const std::string&,
const std::string&,
const buzz::XmlElement*> SignalErrorMessage;
// Handlers for the various types of messages. These functions may take
// pointers to the whole stanza or to just the session element.
bool OnInitiateMessage(const SessionMessage& msg, MessageError* error);
bool OnAcceptMessage(const SessionMessage& msg, MessageError* error);
bool OnRejectMessage(const SessionMessage& msg, MessageError* error);
bool OnInfoMessage(const SessionMessage& msg);
bool OnTerminateMessage(const SessionMessage& msg, MessageError* error);
bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error);
bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error);
bool OnDescriptionInfoMessage(const SessionMessage& msg, MessageError* error);
bool OnRedirectError(const SessionRedirect& redirect, SessionError* error);
// Verifies that we are in the appropriate state to receive this message.
bool CheckState(State state, MessageError* error);
SessionManager* session_manager_;
bool initiate_acked_;
std::string local_name_;
std::string initiator_name_;
std::string remote_name_;
SessionClient* client_;
TransportParser* transport_parser_;
// Keeps track of what protocol we are speaking.
SignalingProtocol current_protocol_;
friend class SessionManager; // For access to constructor, destructor,
// and signaling related methods.
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_SESSION_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_SESSIONCLIENT_H_
#define WEBRTC_P2P_BASE_SESSIONCLIENT_H_
#include "webrtc/p2p/base/constants.h"
namespace buzz {
class XmlElement;
}
namespace cricket {
struct ParseError;
class Session;
class ContentDescription;
class ContentParser {
public:
virtual bool ParseContent(SignalingProtocol protocol,
const buzz::XmlElement* elem,
ContentDescription** content,
ParseError* error) = 0;
// If not IsWriteable, then a given content should be "skipped" when
// writing in the given protocol, as if it didn't exist. We assume
// most things are writeable. We do this to avoid strange cases
// like data contents in Gingle, which aren't writable.
virtual bool IsWritable(SignalingProtocol protocol,
const ContentDescription* content) {
return true;
}
virtual bool WriteContent(SignalingProtocol protocol,
const ContentDescription* content,
buzz::XmlElement** elem,
WriteError* error) = 0;
virtual ~ContentParser() {}
};
// A SessionClient exists in 1-1 relation with each session. The implementor
// of this interface is the one that understands *what* the two sides are
// trying to send to one another. The lower-level layers only know how to send
// data; they do not know what is being sent.
class SessionClient : public ContentParser {
public:
// Notifies the client of the creation / destruction of sessions of this type.
//
// IMPORTANT: The SessionClient, in its handling of OnSessionCreate, must
// create whatever channels are indicate in the description. This is because
// the remote client may already be attempting to connect those channels. If
// we do not create our channel right away, then connection may fail or be
// delayed.
virtual void OnSessionCreate(Session* session, bool received_initiate) = 0;
virtual void OnSessionDestroy(Session* session) = 0;
virtual bool ParseContent(SignalingProtocol protocol,
const buzz::XmlElement* elem,
ContentDescription** content,
ParseError* error) = 0;
virtual bool WriteContent(SignalingProtocol protocol,
const ContentDescription* content,
buzz::XmlElement** elem,
WriteError* error) = 0;
protected:
// The SessionClient interface explicitly does not include destructor
virtual ~SessionClient() { }
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_SESSIONCLIENT_H_

View File

@ -1,239 +0,0 @@
/*
* libjingle
* Copyright 2010, 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 "webrtc/p2p/base/sessiondescription.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
namespace cricket {
ContentInfo* FindContentInfoByName(
ContentInfos& contents, const std::string& name) {
for (ContentInfos::iterator content = contents.begin();
content != contents.end(); ++content) {
if (content->name == name) {
return &(*content);
}
}
return NULL;
}
const ContentInfo* FindContentInfoByName(
const ContentInfos& contents, const std::string& name) {
for (ContentInfos::const_iterator content = contents.begin();
content != contents.end(); ++content) {
if (content->name == name) {
return &(*content);
}
}
return NULL;
}
const ContentInfo* FindContentInfoByType(
const ContentInfos& contents, const std::string& type) {
for (ContentInfos::const_iterator content = contents.begin();
content != contents.end(); ++content) {
if (content->type == type) {
return &(*content);
}
}
return NULL;
}
const std::string* ContentGroup::FirstContentName() const {
return (!content_names_.empty()) ? &(*content_names_.begin()) : NULL;
}
bool ContentGroup::HasContentName(const std::string& content_name) const {
return (std::find(content_names_.begin(), content_names_.end(),
content_name) != content_names_.end());
}
void ContentGroup::AddContentName(const std::string& content_name) {
if (!HasContentName(content_name)) {
content_names_.push_back(content_name);
}
}
bool ContentGroup::RemoveContentName(const std::string& content_name) {
ContentNames::iterator iter = std::find(
content_names_.begin(), content_names_.end(), content_name);
if (iter == content_names_.end()) {
return false;
}
content_names_.erase(iter);
return true;
}
SessionDescription* SessionDescription::Copy() const {
SessionDescription* copy = new SessionDescription(*this);
// Copy all ContentDescriptions.
for (ContentInfos::iterator content = copy->contents_.begin();
content != copy->contents().end(); ++content) {
content->description = content->description->Copy();
}
return copy;
}
const ContentInfo* SessionDescription::GetContentByName(
const std::string& name) const {
return FindContentInfoByName(contents_, name);
}
ContentInfo* SessionDescription::GetContentByName(
const std::string& name) {
return FindContentInfoByName(contents_, name);
}
const ContentDescription* SessionDescription::GetContentDescriptionByName(
const std::string& name) const {
const ContentInfo* cinfo = FindContentInfoByName(contents_, name);
if (cinfo == NULL) {
return NULL;
}
return cinfo->description;
}
ContentDescription* SessionDescription::GetContentDescriptionByName(
const std::string& name) {
ContentInfo* cinfo = FindContentInfoByName(contents_, name);
if (cinfo == NULL) {
return NULL;
}
return cinfo->description;
}
const ContentInfo* SessionDescription::FirstContentByType(
const std::string& type) const {
return FindContentInfoByType(contents_, type);
}
const ContentInfo* SessionDescription::FirstContent() const {
return (contents_.empty()) ? NULL : &(*contents_.begin());
}
void SessionDescription::AddContent(const std::string& name,
const std::string& type,
ContentDescription* description) {
contents_.push_back(ContentInfo(name, type, description));
}
void SessionDescription::AddContent(const std::string& name,
const std::string& type,
bool rejected,
ContentDescription* description) {
contents_.push_back(ContentInfo(name, type, rejected, description));
}
bool SessionDescription::RemoveContentByName(const std::string& name) {
for (ContentInfos::iterator content = contents_.begin();
content != contents_.end(); ++content) {
if (content->name == name) {
delete content->description;
contents_.erase(content);
return true;
}
}
return false;
}
bool SessionDescription::AddTransportInfo(const TransportInfo& transport_info) {
if (GetTransportInfoByName(transport_info.content_name) != NULL) {
return false;
}
transport_infos_.push_back(transport_info);
return true;
}
bool SessionDescription::RemoveTransportInfoByName(const std::string& name) {
for (TransportInfos::iterator transport_info = transport_infos_.begin();
transport_info != transport_infos_.end(); ++transport_info) {
if (transport_info->content_name == name) {
transport_infos_.erase(transport_info);
return true;
}
}
return false;
}
const TransportInfo* SessionDescription::GetTransportInfoByName(
const std::string& name) const {
for (TransportInfos::const_iterator iter = transport_infos_.begin();
iter != transport_infos_.end(); ++iter) {
if (iter->content_name == name) {
return &(*iter);
}
}
return NULL;
}
TransportInfo* SessionDescription::GetTransportInfoByName(
const std::string& name) {
for (TransportInfos::iterator iter = transport_infos_.begin();
iter != transport_infos_.end(); ++iter) {
if (iter->content_name == name) {
return &(*iter);
}
}
return NULL;
}
void SessionDescription::RemoveGroupByName(const std::string& name) {
for (ContentGroups::iterator iter = content_groups_.begin();
iter != content_groups_.end(); ++iter) {
if (iter->semantics() == name) {
content_groups_.erase(iter);
break;
}
}
}
bool SessionDescription::HasGroup(const std::string& name) const {
for (ContentGroups::const_iterator iter = content_groups_.begin();
iter != content_groups_.end(); ++iter) {
if (iter->semantics() == name) {
return true;
}
}
return false;
}
const ContentGroup* SessionDescription::GetGroupByName(
const std::string& name) const {
for (ContentGroups::const_iterator iter = content_groups_.begin();
iter != content_groups_.end(); ++iter) {
if (iter->semantics() == name) {
return &(*iter);
}
}
return NULL;
}
} // namespace cricket

View File

@ -1,202 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_SESSIONDESCRIPTION_H_
#define WEBRTC_P2P_BASE_SESSIONDESCRIPTION_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/transportinfo.h"
#include "webrtc/base/constructormagic.h"
namespace cricket {
// Describes a session content. Individual content types inherit from
// this class. Analagous to a <jingle><content><description> or
// <session><description>.
class ContentDescription {
public:
virtual ~ContentDescription() {}
virtual ContentDescription* Copy() const = 0;
};
// Analagous to a <jingle><content> or <session><description>.
// name = name of <content name="...">
// type = xmlns of <content>
struct ContentInfo {
ContentInfo() : description(NULL) {}
ContentInfo(const std::string& name,
const std::string& type,
ContentDescription* description) :
name(name), type(type), rejected(false), description(description) {}
ContentInfo(const std::string& name,
const std::string& type,
bool rejected,
ContentDescription* description) :
name(name), type(type), rejected(rejected), description(description) {}
std::string name;
std::string type;
bool rejected;
ContentDescription* description;
};
typedef std::vector<std::string> ContentNames;
// This class provides a mechanism to aggregate different media contents into a
// group. This group can also be shared with the peers in a pre-defined format.
// GroupInfo should be populated only with the |content_name| of the
// MediaDescription.
class ContentGroup {
public:
explicit ContentGroup(const std::string& semantics) :
semantics_(semantics) {}
const std::string& semantics() const { return semantics_; }
const ContentNames& content_names() const { return content_names_; }
const std::string* FirstContentName() const;
bool HasContentName(const std::string& content_name) const;
void AddContentName(const std::string& content_name);
bool RemoveContentName(const std::string& content_name);
private:
std::string semantics_;
ContentNames content_names_;
};
typedef std::vector<ContentInfo> ContentInfos;
typedef std::vector<ContentGroup> ContentGroups;
const ContentInfo* FindContentInfoByName(
const ContentInfos& contents, const std::string& name);
const ContentInfo* FindContentInfoByType(
const ContentInfos& contents, const std::string& type);
// Describes a collection of contents, each with its own name and
// type. Analogous to a <jingle> or <session> stanza. Assumes that
// contents are unique be name, but doesn't enforce that.
class SessionDescription {
public:
SessionDescription() {}
explicit SessionDescription(const ContentInfos& contents) :
contents_(contents) {}
SessionDescription(const ContentInfos& contents,
const ContentGroups& groups) :
contents_(contents),
content_groups_(groups) {}
SessionDescription(const ContentInfos& contents,
const TransportInfos& transports,
const ContentGroups& groups) :
contents_(contents),
transport_infos_(transports),
content_groups_(groups) {}
~SessionDescription() {
for (ContentInfos::iterator content = contents_.begin();
content != contents_.end(); ++content) {
delete content->description;
}
}
SessionDescription* Copy() const;
// Content accessors.
const ContentInfos& contents() const { return contents_; }
ContentInfos& contents() { return contents_; }
const ContentInfo* GetContentByName(const std::string& name) const;
ContentInfo* GetContentByName(const std::string& name);
const ContentDescription* GetContentDescriptionByName(
const std::string& name) const;
ContentDescription* GetContentDescriptionByName(const std::string& name);
const ContentInfo* FirstContentByType(const std::string& type) const;
const ContentInfo* FirstContent() const;
// Content mutators.
// Adds a content to this description. Takes ownership of ContentDescription*.
void AddContent(const std::string& name,
const std::string& type,
ContentDescription* description);
void AddContent(const std::string& name,
const std::string& type,
bool rejected,
ContentDescription* description);
bool RemoveContentByName(const std::string& name);
// Transport accessors.
const TransportInfos& transport_infos() const { return transport_infos_; }
TransportInfos& transport_infos() { return transport_infos_; }
const TransportInfo* GetTransportInfoByName(
const std::string& name) const;
TransportInfo* GetTransportInfoByName(const std::string& name);
const TransportDescription* GetTransportDescriptionByName(
const std::string& name) const {
const TransportInfo* tinfo = GetTransportInfoByName(name);
return tinfo ? &tinfo->description : NULL;
}
// Transport mutators.
void set_transport_infos(const TransportInfos& transport_infos) {
transport_infos_ = transport_infos;
}
// Adds a TransportInfo to this description.
// Returns false if a TransportInfo with the same name already exists.
bool AddTransportInfo(const TransportInfo& transport_info);
bool RemoveTransportInfoByName(const std::string& name);
// Group accessors.
const ContentGroups& groups() const { return content_groups_; }
const ContentGroup* GetGroupByName(const std::string& name) const;
bool HasGroup(const std::string& name) const;
// Group mutators.
void AddGroup(const ContentGroup& group) { content_groups_.push_back(group); }
// Remove the first group with the same semantics specified by |name|.
void RemoveGroupByName(const std::string& name);
private:
ContentInfos contents_;
TransportInfos transport_infos_;
ContentGroups content_groups_;
};
// Indicates whether a ContentDescription was an offer or an answer, as
// described in http://www.ietf.org/rfc/rfc3264.txt. CA_UPDATE
// indicates a jingle update message which contains a subset of a full
// session description
enum ContentAction {
CA_OFFER, CA_PRANSWER, CA_ANSWER, CA_UPDATE
};
// Indicates whether a ContentDescription was sent by the local client
// or received from the remote client.
enum ContentSource {
CS_LOCAL, CS_REMOTE
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_SESSIONDESCRIPTION_H_

View File

@ -1,37 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_SESSIONID_H_
#define WEBRTC_P2P_BASE_SESSIONID_H_
// TODO: Remove this file.
namespace cricket {
} // namespace cricket
#endif // WEBRTC_P2P_BASE_SESSIONID_H_

View File

@ -1,326 +0,0 @@
/*
* 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 "webrtc/p2p/base/sessionmanager.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/session.h"
#include "webrtc/p2p/base/sessionmessages.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/libjingle/xmpp/jid.h"
#include "webrtc/base/common.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/stringencode.h"
namespace cricket {
SessionManager::SessionManager(PortAllocator *allocator,
rtc::Thread *worker) {
allocator_ = allocator;
signaling_thread_ = rtc::Thread::Current();
if (worker == NULL) {
worker_thread_ = rtc::Thread::Current();
} else {
worker_thread_ = worker;
}
timeout_ = 50;
}
SessionManager::~SessionManager() {
// Note: Session::Terminate occurs asynchronously, so it's too late to
// delete them now. They better be all gone.
ASSERT(session_map_.empty());
// TerminateAll();
SignalDestroyed();
}
void SessionManager::AddClient(const std::string& content_type,
SessionClient* client) {
ASSERT(client_map_.find(content_type) == client_map_.end());
client_map_[content_type] = client;
}
void SessionManager::RemoveClient(const std::string& content_type) {
ClientMap::iterator iter = client_map_.find(content_type);
ASSERT(iter != client_map_.end());
client_map_.erase(iter);
}
SessionClient* SessionManager::GetClient(const std::string& content_type) {
ClientMap::iterator iter = client_map_.find(content_type);
return (iter != client_map_.end()) ? iter->second : NULL;
}
Session* SessionManager::CreateSession(const std::string& local_name,
const std::string& content_type) {
std::string id;
return CreateSession(id, local_name, content_type);
}
Session* SessionManager::CreateSession(const std::string& id,
const std::string& local_name,
const std::string& content_type) {
std::string sid =
id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id;
return CreateSession(local_name, local_name, sid, content_type, false);
}
Session* SessionManager::CreateSession(
const std::string& local_name, const std::string& initiator_name,
const std::string& sid, const std::string& content_type,
bool received_initiate) {
SessionClient* client = GetClient(content_type);
ASSERT(client != NULL);
Session* session = new Session(this, local_name, initiator_name,
sid, content_type, client);
session->SetIdentity(transport_desc_factory_.identity());
session_map_[session->id()] = session;
session->SignalRequestSignaling.connect(
this, &SessionManager::OnRequestSignaling);
session->SignalOutgoingMessage.connect(
this, &SessionManager::OnOutgoingMessage);
session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
SignalSessionCreate(session, received_initiate);
session->client()->OnSessionCreate(session, received_initiate);
return session;
}
void SessionManager::DestroySession(Session* session) {
if (session != NULL) {
SessionMap::iterator it = session_map_.find(session->id());
if (it != session_map_.end()) {
SignalSessionDestroy(session);
session->client()->OnSessionDestroy(session);
session_map_.erase(it);
delete session;
}
}
}
Session* SessionManager::GetSession(const std::string& sid) {
SessionMap::iterator it = session_map_.find(sid);
if (it != session_map_.end())
return it->second;
return NULL;
}
void SessionManager::TerminateAll() {
while (session_map_.begin() != session_map_.end()) {
Session* session = session_map_.begin()->second;
session->Terminate();
}
}
bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
return cricket::IsSessionMessage(stanza);
}
Session* SessionManager::FindSession(const std::string& sid,
const std::string& remote_name) {
SessionMap::iterator iter = session_map_.find(sid);
if (iter == session_map_.end())
return NULL;
Session* session = iter->second;
if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
return NULL;
return session;
}
void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
SessionMessage msg;
ParseError error;
if (!ParseSessionMessage(stanza, &msg, &error)) {
SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
error.text, NULL);
return;
}
Session* session = FindSession(msg.sid, msg.from);
if (session) {
session->OnIncomingMessage(msg);
return;
}
if (msg.type != ACTION_SESSION_INITIATE) {
SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
"unknown session", NULL);
return;
}
std::string content_type;
if (!ParseContentType(msg.protocol, msg.action_elem,
&content_type, &error)) {
SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
error.text, NULL);
return;
}
if (!GetClient(content_type)) {
SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
"unknown content type: " + content_type, NULL);
return;
}
session = CreateSession(msg.to, msg.initiator, msg.sid,
content_type, true);
session->OnIncomingMessage(msg);
}
void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
const buzz::XmlElement* response_stanza) {
if (orig_stanza == NULL || response_stanza == NULL) {
return;
}
SessionMessage msg;
ParseError error;
if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
LOG(LS_WARNING) << "Error parsing incoming response: " << error.text
<< ":" << orig_stanza;
return;
}
Session* session = FindSession(msg.sid, msg.to);
if (!session) {
// Also try the QN_FROM in the response stanza, in case we sent the request
// to a bare JID but got the response from a full JID.
std::string ack_from = response_stanza->Attr(buzz::QN_FROM);
session = FindSession(msg.sid, ack_from);
}
if (session) {
session->OnIncomingResponse(orig_stanza, response_stanza, msg);
}
}
void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
const buzz::XmlElement* error_stanza) {
SessionMessage msg;
ParseError error;
if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
return; // TODO: log somewhere?
}
Session* session = FindSession(msg.sid, msg.to);
if (session) {
rtc::scoped_ptr<buzz::XmlElement> synthetic_error;
if (!error_stanza) {
// A failed send is semantically equivalent to an error response, so we
// can just turn the former into the latter.
synthetic_error.reset(
CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
"cancel", "Recipient did not respond", NULL));
error_stanza = synthetic_error.get();
}
session->OnFailedSend(orig_stanza, error_stanza);
}
}
void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info) {
rtc::scoped_ptr<buzz::XmlElement> msg(
CreateErrorMessage(stanza, name, type, text, extra_info));
SignalOutgoingMessage(this, msg.get());
}
buzz::XmlElement* SessionManager::CreateErrorMessage(
const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info) {
buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
iq->SetAttr(buzz::QN_TYPE, "error");
CopyXmlChildren(stanza, iq);
buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
error->SetAttr(buzz::QN_TYPE, type);
iq->AddElement(error);
// If the error name is not in the standard namespace, we have to first add
// some error from that namespace.
if (name.Namespace() != buzz::NS_STANZA) {
error->AddElement(
new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
}
error->AddElement(new buzz::XmlElement(name));
if (extra_info)
error->AddElement(new buzz::XmlElement(*extra_info));
if (text.size() > 0) {
// It's okay to always use English here. This text is for debugging
// purposes only.
buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
text_elem->SetAttr(buzz::QN_XML_LANG, "en");
text_elem->SetBodyText(text);
error->AddElement(text_elem);
}
// TODO: Should we include error codes as well for SIP compatibility?
return iq;
}
void SessionManager::OnOutgoingMessage(Session* session,
const buzz::XmlElement* stanza) {
SignalOutgoingMessage(this, stanza);
}
void SessionManager::OnErrorMessage(BaseSession* session,
const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info) {
SendErrorMessage(stanza, name, type, text, extra_info);
}
void SessionManager::OnSignalingReady() {
for (SessionMap::iterator it = session_map_.begin();
it != session_map_.end();
++it) {
it->second->OnSignalingReady();
}
}
void SessionManager::OnRequestSignaling(Session* session) {
SignalRequestSignaling();
}
} // namespace cricket

View File

@ -1,211 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_SESSIONMANAGER_H_
#define WEBRTC_P2P_BASE_SESSIONMANAGER_H_
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/transportdescriptionfactory.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/thread.h"
namespace buzz {
class QName;
class XmlElement;
}
namespace cricket {
class Session;
class BaseSession;
class SessionClient;
// SessionManager manages session instances.
class SessionManager : public sigslot::has_slots<> {
public:
SessionManager(PortAllocator *allocator,
rtc::Thread *worker_thread = NULL);
virtual ~SessionManager();
PortAllocator *port_allocator() const { return allocator_; }
rtc::Thread *worker_thread() const { return worker_thread_; }
rtc::Thread *signaling_thread() const { return signaling_thread_; }
int session_timeout() const { return timeout_; }
void set_session_timeout(int timeout) { timeout_ = timeout; }
// Set what transport protocol we want to default to.
void set_transport_protocol(TransportProtocol proto) {
transport_desc_factory_.set_protocol(proto);
}
// Control use of DTLS. An identity must be supplied if DTLS is enabled.
void set_secure(SecurePolicy policy) {
transport_desc_factory_.set_secure(policy);
}
void set_identity(rtc::SSLIdentity* identity) {
transport_desc_factory_.set_identity(identity);
}
const TransportDescriptionFactory* transport_desc_factory() const {
return &transport_desc_factory_;
}
// Registers support for the given client. If we receive an initiate
// describing a session of the given type, we will automatically create a
// Session object and notify this client. The client may then accept or
// reject the session.
void AddClient(const std::string& content_type, SessionClient* client);
void RemoveClient(const std::string& content_type);
SessionClient* GetClient(const std::string& content_type);
// Creates a new session. The given name is the JID of the client on whose
// behalf we initiate the session.
Session *CreateSession(const std::string& local_name,
const std::string& content_type);
Session *CreateSession(const std::string& id,
const std::string& local_name,
const std::string& content_type);
// Destroys the given session.
void DestroySession(Session *session);
// Returns the session with the given ID or NULL if none exists.
Session *GetSession(const std::string& sid);
// Terminates all of the sessions created by this manager.
void TerminateAll();
// These are signaled whenever the set of existing sessions changes.
sigslot::signal2<Session *, bool> SignalSessionCreate;
sigslot::signal1<Session *> SignalSessionDestroy;
// Determines whether the given stanza is intended for some session.
bool IsSessionMessage(const buzz::XmlElement* stanza);
// Given a sid, initiator, and remote_name, this finds the matching Session
Session* FindSession(const std::string& sid,
const std::string& remote_name);
// Called when we receive a stanza for which IsSessionMessage is true.
void OnIncomingMessage(const buzz::XmlElement* stanza);
// Called when we get a response to a message that we sent.
void OnIncomingResponse(const buzz::XmlElement* orig_stanza,
const buzz::XmlElement* response_stanza);
// Called if an attempted to send times out or an error is returned. In the
// timeout case error_stanza will be NULL
void OnFailedSend(const buzz::XmlElement* orig_stanza,
const buzz::XmlElement* error_stanza);
// Signalled each time a session generates a signaling message to send.
// Also signalled on errors, but with a NULL session.
sigslot::signal2<SessionManager*,
const buzz::XmlElement*> SignalOutgoingMessage;
// Signaled before sessions try to send certain signaling messages. The
// client should call OnSignalingReady once it is safe to send them. These
// steps are taken so that we don't send signaling messages trying to
// re-establish the connectivity of a session when the client cannot send
// the messages (and would probably just drop them on the floor).
//
// Note: you can connect this directly to OnSignalingReady(), if a signalling
// check is not supported.
sigslot::signal0<> SignalRequestSignaling;
void OnSignalingReady();
// Signaled when this SessionManager is deleted.
sigslot::signal0<> SignalDestroyed;
private:
typedef std::map<std::string, Session*> SessionMap;
typedef std::map<std::string, SessionClient*> ClientMap;
// Helper function for CreateSession. This is also invoked when we receive
// a message attempting to initiate a session with this client.
Session *CreateSession(const std::string& local_name,
const std::string& initiator,
const std::string& sid,
const std::string& content_type,
bool received_initiate);
// Attempts to find a registered session type whose description appears as
// a child of the session element. Such a child should be present indicating
// the application they hope to initiate.
std::string FindClient(const buzz::XmlElement* session);
// Sends a message back to the other client indicating that we found an error
// in the stanza they sent. name identifies the error, type is one of the
// standard XMPP types (cancel, continue, modify, auth, wait), and text is a
// description for debugging purposes.
void SendErrorMessage(const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info);
// Creates and returns an error message from the given components. The
// caller is responsible for deleting this.
buzz::XmlElement* CreateErrorMessage(
const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info);
// Called each time a session requests signaling.
void OnRequestSignaling(Session* session);
// Called each time a session has an outgoing message.
void OnOutgoingMessage(Session* session, const buzz::XmlElement* stanza);
// Called each time a session has an error to send.
void OnErrorMessage(BaseSession* session,
const buzz::XmlElement* stanza,
const buzz::QName& name,
const std::string& type,
const std::string& text,
const buzz::XmlElement* extra_info);
PortAllocator *allocator_;
rtc::Thread *signaling_thread_;
rtc::Thread *worker_thread_;
int timeout_;
TransportDescriptionFactory transport_desc_factory_;
SessionMap session_map_;
ClientMap client_map_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_SESSIONMANAGER_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,243 +0,0 @@
/*
* libjingle
* Copyright 2010, 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.
*/
#ifndef WEBRTC_P2P_BASE_SESSIONMESSAGES_H_
#define WEBRTC_P2P_BASE_SESSIONMESSAGES_H_
#include <map>
#include <string>
#include <vector>
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/parsing.h"
#include "webrtc/p2p/base/sessiondescription.h" // Needed to delete contents.
#include "webrtc/p2p/base/transportinfo.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/base/basictypes.h"
namespace cricket {
struct ParseError;
struct WriteError;
class Candidate;
class ContentParser;
class TransportParser;
typedef std::vector<Candidate> Candidates;
typedef std::map<std::string, ContentParser*> ContentParserMap;
typedef std::map<std::string, TransportParser*> TransportParserMap;
enum ActionType {
ACTION_UNKNOWN,
ACTION_SESSION_INITIATE,
ACTION_SESSION_INFO,
ACTION_SESSION_ACCEPT,
ACTION_SESSION_REJECT,
ACTION_SESSION_TERMINATE,
ACTION_TRANSPORT_INFO,
ACTION_TRANSPORT_ACCEPT,
ACTION_DESCRIPTION_INFO,
};
// Abstraction of a <jingle> element within an <iq> stanza, per XMPP
// standard XEP-166. Can be serialized into multiple protocols,
// including the standard (Jingle) and the draft standard (Gingle).
// In general, used to communicate actions related to a p2p session,
// such accept, initiate, terminate, etc.
struct SessionMessage {
SessionMessage() : action_elem(NULL), stanza(NULL) {}
SessionMessage(SignalingProtocol protocol, ActionType type,
const std::string& sid, const std::string& initiator) :
protocol(protocol), type(type), sid(sid), initiator(initiator),
action_elem(NULL), stanza(NULL) {}
std::string id;
std::string from;
std::string to;
SignalingProtocol protocol;
ActionType type;
std::string sid; // session id
std::string initiator;
// Used for further parsing when necessary.
// Represents <session> or <jingle>.
const buzz::XmlElement* action_elem;
// Mostly used for debugging.
const buzz::XmlElement* stanza;
};
// TODO: Break up this class so we don't have to typedef it into
// different classes.
struct ContentMessage {
ContentMessage() : owns_contents(false) {}
~ContentMessage() {
if (owns_contents) {
for (ContentInfos::iterator content = contents.begin();
content != contents.end(); content++) {
delete content->description;
}
}
}
// Caller takes ownership of contents.
ContentInfos ClearContents() {
ContentInfos out;
contents.swap(out);
owns_contents = false;
return out;
}
bool owns_contents;
ContentInfos contents;
TransportInfos transports;
ContentGroups groups;
};
typedef ContentMessage SessionInitiate;
typedef ContentMessage SessionAccept;
// Note that a DescriptionInfo does not have TransportInfos.
typedef ContentMessage DescriptionInfo;
struct SessionTerminate {
SessionTerminate() {}
explicit SessionTerminate(const std::string& reason) :
reason(reason) {}
std::string reason;
std::string debug_reason;
};
struct SessionRedirect {
std::string target;
};
// Used during parsing and writing to map component to channel name
// and back. This is primarily for converting old G-ICE candidate
// signalling to new ICE candidate classes.
class CandidateTranslator {
public:
virtual bool GetChannelNameFromComponent(
int component, std::string* channel_name) const = 0;
virtual bool GetComponentFromChannelName(
const std::string& channel_name, int* component) const = 0;
};
// Content name => translator
typedef std::map<std::string, CandidateTranslator*> CandidateTranslatorMap;
bool IsSessionMessage(const buzz::XmlElement* stanza);
bool ParseSessionMessage(const buzz::XmlElement* stanza,
SessionMessage* msg,
ParseError* error);
// Will return an error if there is more than one content type.
bool ParseContentType(SignalingProtocol protocol,
const buzz::XmlElement* action_elem,
std::string* content_type,
ParseError* error);
void WriteSessionMessage(const SessionMessage& msg,
const XmlElements& action_elems,
buzz::XmlElement* stanza);
bool ParseSessionInitiate(SignalingProtocol protocol,
const buzz::XmlElement* action_elem,
const ContentParserMap& content_parsers,
const TransportParserMap& transport_parsers,
const CandidateTranslatorMap& translators,
SessionInitiate* init,
ParseError* error);
bool WriteSessionInitiate(SignalingProtocol protocol,
const ContentInfos& contents,
const TransportInfos& tinfos,
const ContentParserMap& content_parsers,
const TransportParserMap& transport_parsers,
const CandidateTranslatorMap& translators,
const ContentGroups& groups,
XmlElements* elems,
WriteError* error);
bool ParseSessionAccept(SignalingProtocol protocol,
const buzz::XmlElement* action_elem,
const ContentParserMap& content_parsers,
const TransportParserMap& transport_parsers,
const CandidateTranslatorMap& translators,
SessionAccept* accept,
ParseError* error);
bool WriteSessionAccept(SignalingProtocol protocol,
const ContentInfos& contents,
const TransportInfos& tinfos,
const ContentParserMap& content_parsers,
const TransportParserMap& transport_parsers,
const CandidateTranslatorMap& translators,
const ContentGroups& groups,
XmlElements* elems,
WriteError* error);
bool ParseSessionTerminate(SignalingProtocol protocol,
const buzz::XmlElement* action_elem,
SessionTerminate* term,
ParseError* error);
void WriteSessionTerminate(SignalingProtocol protocol,
const SessionTerminate& term,
XmlElements* elems);
bool ParseDescriptionInfo(SignalingProtocol protocol,
const buzz::XmlElement* action_elem,
const ContentParserMap& content_parsers,
const TransportParserMap& transport_parsers,
const CandidateTranslatorMap& translators,
DescriptionInfo* description_info,
ParseError* error);
bool WriteDescriptionInfo(SignalingProtocol protocol,
const ContentInfos& contents,
const ContentParserMap& content_parsers,
XmlElements* elems,
WriteError* error);
// Since a TransportInfo is not a transport-info message, and a
// transport-info message is just a collection of TransportInfos, we
// say Parse/Write TransportInfos for transport-info messages.
bool ParseTransportInfos(SignalingProtocol protocol,
const buzz::XmlElement* action_elem,
const ContentInfos& contents,
const TransportParserMap& trans_parsers,
const CandidateTranslatorMap& translators,
TransportInfos* tinfos,
ParseError* error);
bool WriteTransportInfos(SignalingProtocol protocol,
const TransportInfos& tinfos,
const TransportParserMap& trans_parsers,
const CandidateTranslatorMap& translators,
XmlElements* elems,
WriteError* error);
// Handles both Gingle and Jingle syntax.
bool FindSessionRedirect(const buzz::XmlElement* stanza,
SessionRedirect* redirect);
} // namespace cricket
#endif // WEBRTC_P2P_BASE_SESSIONMESSAGES_H_

View File

@ -1,932 +0,0 @@
/*
* 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 "webrtc/p2p/base/stun.h"
#include <string.h>
#include "webrtc/base/byteorder.h"
#include "webrtc/base/common.h"
#include "webrtc/base/crc32.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/messagedigest.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/stringencode.h"
using rtc::ByteBuffer;
namespace cricket {
const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[] = "Try Alternate Server";
const char STUN_ERROR_REASON_BAD_REQUEST[] = "Bad Request";
const char STUN_ERROR_REASON_UNAUTHORIZED[] = "Unauthorized";
const char STUN_ERROR_REASON_FORBIDDEN[] = "Forbidden";
const char STUN_ERROR_REASON_STALE_CREDENTIALS[] = "Stale Credentials";
const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[] = "Allocation Mismatch";
const char STUN_ERROR_REASON_STALE_NONCE[] = "Stale Nonce";
const char STUN_ERROR_REASON_WRONG_CREDENTIALS[] = "Wrong Credentials";
const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[] = "Unsupported Protocol";
const char STUN_ERROR_REASON_ROLE_CONFLICT[] = "Role Conflict";
const char STUN_ERROR_REASON_SERVER_ERROR[] = "Server Error";
const char TURN_MAGIC_COOKIE_VALUE[] = { '\x72', '\xC6', '\x4B', '\xC6' };
const char EMPTY_TRANSACTION_ID[] = "0000000000000000";
const uint32 STUN_FINGERPRINT_XOR_VALUE = 0x5354554E;
// StunMessage
StunMessage::StunMessage()
: type_(0),
length_(0),
transaction_id_(EMPTY_TRANSACTION_ID) {
ASSERT(IsValidTransactionId(transaction_id_));
attrs_ = new std::vector<StunAttribute*>();
}
StunMessage::~StunMessage() {
for (size_t i = 0; i < attrs_->size(); i++)
delete (*attrs_)[i];
delete attrs_;
}
bool StunMessage::IsLegacy() const {
if (transaction_id_.size() == kStunLegacyTransactionIdLength)
return true;
ASSERT(transaction_id_.size() == kStunTransactionIdLength);
return false;
}
bool StunMessage::SetTransactionID(const std::string& str) {
if (!IsValidTransactionId(str)) {
return false;
}
transaction_id_ = str;
return true;
}
bool StunMessage::AddAttribute(StunAttribute* attr) {
// Fail any attributes that aren't valid for this type of message.
if (attr->value_type() != GetAttributeValueType(attr->type())) {
return false;
}
attrs_->push_back(attr);
attr->SetOwner(this);
size_t attr_length = attr->length();
if (attr_length % 4 != 0) {
attr_length += (4 - (attr_length % 4));
}
length_ += static_cast<uint16>(attr_length + 4);
return true;
}
const StunAddressAttribute* StunMessage::GetAddress(int type) const {
switch (type) {
case STUN_ATTR_MAPPED_ADDRESS: {
// Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
// missing.
const StunAttribute* mapped_address =
GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
if (!mapped_address)
mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
}
default:
return static_cast<const StunAddressAttribute*>(GetAttribute(type));
}
}
const StunUInt32Attribute* StunMessage::GetUInt32(int type) const {
return static_cast<const StunUInt32Attribute*>(GetAttribute(type));
}
const StunUInt64Attribute* StunMessage::GetUInt64(int type) const {
return static_cast<const StunUInt64Attribute*>(GetAttribute(type));
}
const StunByteStringAttribute* StunMessage::GetByteString(int type) const {
return static_cast<const StunByteStringAttribute*>(GetAttribute(type));
}
const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
return static_cast<const StunErrorCodeAttribute*>(
GetAttribute(STUN_ATTR_ERROR_CODE));
}
const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
return static_cast<const StunUInt16ListAttribute*>(
GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
}
// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
// procedure outlined in RFC 5389, section 15.4.
bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size,
const std::string& password) {
// Verifying the size of the message.
if ((size % 4) != 0) {
return false;
}
// Getting the message length from the STUN header.
uint16 msg_length = rtc::GetBE16(&data[2]);
if (size != (msg_length + kStunHeaderSize)) {
return false;
}
// Finding Message Integrity attribute in stun message.
size_t current_pos = kStunHeaderSize;
bool has_message_integrity_attr = false;
while (current_pos < size) {
uint16 attr_type, attr_length;
// Getting attribute type and length.
attr_type = rtc::GetBE16(&data[current_pos]);
attr_length = rtc::GetBE16(&data[current_pos + sizeof(attr_type)]);
// If M-I, sanity check it, and break out.
if (attr_type == STUN_ATTR_MESSAGE_INTEGRITY) {
if (attr_length != kStunMessageIntegritySize ||
current_pos + attr_length > size) {
return false;
}
has_message_integrity_attr = true;
break;
}
// Otherwise, skip to the next attribute.
current_pos += sizeof(attr_type) + sizeof(attr_length) + attr_length;
if ((attr_length % 4) != 0) {
current_pos += (4 - (attr_length % 4));
}
}
if (!has_message_integrity_attr) {
return false;
}
// Getting length of the message to calculate Message Integrity.
size_t mi_pos = current_pos;
rtc::scoped_ptr<char[]> temp_data(new char[current_pos]);
memcpy(temp_data.get(), data, current_pos);
if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) {
// Stun message has other attributes after message integrity.
// Adjust the length parameter in stun message to calculate HMAC.
size_t extra_offset = size -
(mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize);
size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;
// Writing new length of the STUN message @ Message Length in temp buffer.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0 0| STUN Message Type | Message Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
rtc::SetBE16(temp_data.get() + 2,
static_cast<uint16>(new_adjusted_len));
}
char hmac[kStunMessageIntegritySize];
size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
password.c_str(), password.size(),
temp_data.get(), mi_pos,
hmac, sizeof(hmac));
ASSERT(ret == sizeof(hmac));
if (ret != sizeof(hmac))
return false;
// Comparing the calculated HMAC with the one present in the message.
return memcmp(data + current_pos + kStunAttributeHeaderSize,
hmac,
sizeof(hmac)) == 0;
}
bool StunMessage::AddMessageIntegrity(const std::string& password) {
return AddMessageIntegrity(password.c_str(), password.size());
}
bool StunMessage::AddMessageIntegrity(const char* key,
size_t keylen) {
// Add the attribute with a dummy value. Since this is a known attribute, it
// can't fail.
StunByteStringAttribute* msg_integrity_attr =
new StunByteStringAttribute(STUN_ATTR_MESSAGE_INTEGRITY,
std::string(kStunMessageIntegritySize, '0'));
VERIFY(AddAttribute(msg_integrity_attr));
// Calculate the HMAC for the message.
rtc::ByteBuffer buf;
if (!Write(&buf))
return false;
int msg_len_for_hmac = static_cast<int>(
buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length());
char hmac[kStunMessageIntegritySize];
size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
key, keylen,
buf.Data(), msg_len_for_hmac,
hmac, sizeof(hmac));
ASSERT(ret == sizeof(hmac));
if (ret != sizeof(hmac)) {
LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity "
<< "has dummy value.";
return false;
}
// Insert correct HMAC into the attribute.
msg_integrity_attr->CopyBytes(hmac, sizeof(hmac));
return true;
}
// Verifies a message is in fact a STUN message, by performing the checks
// outlined in RFC 5389, section 7.3, including the FINGERPRINT check detailed
// in section 15.5.
bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
// Check the message length.
size_t fingerprint_attr_size =
kStunAttributeHeaderSize + StunUInt32Attribute::SIZE;
if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size)
return false;
// Skip the rest if the magic cookie isn't present.
const char* magic_cookie =
data + kStunTransactionIdOffset - kStunMagicCookieLength;
if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
return false;
// Check the fingerprint type and length.
const char* fingerprint_attr_data = data + size - fingerprint_attr_size;
if (rtc::GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT ||
rtc::GetBE16(fingerprint_attr_data + sizeof(uint16)) !=
StunUInt32Attribute::SIZE)
return false;
// Check the fingerprint value.
uint32 fingerprint =
rtc::GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize);
return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) ==
rtc::ComputeCrc32(data, size - fingerprint_attr_size));
}
bool StunMessage::AddFingerprint() {
// Add the attribute with a dummy value. Since this is a known attribute,
// it can't fail.
StunUInt32Attribute* fingerprint_attr =
new StunUInt32Attribute(STUN_ATTR_FINGERPRINT, 0);
VERIFY(AddAttribute(fingerprint_attr));
// Calculate the CRC-32 for the message and insert it.
rtc::ByteBuffer buf;
if (!Write(&buf))
return false;
int msg_len_for_crc32 = static_cast<int>(
buf.Length() - kStunAttributeHeaderSize - fingerprint_attr->length());
uint32 c = rtc::ComputeCrc32(buf.Data(), msg_len_for_crc32);
// Insert the correct CRC-32, XORed with a constant, into the attribute.
fingerprint_attr->SetValue(c ^ STUN_FINGERPRINT_XOR_VALUE);
return true;
}
bool StunMessage::Read(ByteBuffer* buf) {
if (!buf->ReadUInt16(&type_))
return false;
if (type_ & 0x8000) {
// RTP and RTCP set the MSB of first byte, since first two bits are version,
// and version is always 2 (10). If set, this is not a STUN packet.
return false;
}
if (!buf->ReadUInt16(&length_))
return false;
std::string magic_cookie;
if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
return false;
std::string transaction_id;
if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
return false;
uint32 magic_cookie_int =
*reinterpret_cast<const uint32*>(magic_cookie.data());
if (rtc::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
// If magic cookie is invalid it means that the peer implements
// RFC3489 instead of RFC5389.
transaction_id.insert(0, magic_cookie);
}
ASSERT(IsValidTransactionId(transaction_id));
transaction_id_ = transaction_id;
if (length_ != buf->Length())
return false;
attrs_->resize(0);
size_t rest = buf->Length() - length_;
while (buf->Length() > rest) {
uint16 attr_type, attr_length;
if (!buf->ReadUInt16(&attr_type))
return false;
if (!buf->ReadUInt16(&attr_length))
return false;
StunAttribute* attr = CreateAttribute(attr_type, attr_length);
if (!attr) {
// Skip any unknown or malformed attributes.
if ((attr_length % 4) != 0) {
attr_length += (4 - (attr_length % 4));
}
if (!buf->Consume(attr_length))
return false;
} else {
if (!attr->Read(buf))
return false;
attrs_->push_back(attr);
}
}
ASSERT(buf->Length() == rest);
return true;
}
bool StunMessage::Write(ByteBuffer* buf) const {
buf->WriteUInt16(type_);
buf->WriteUInt16(length_);
if (!IsLegacy())
buf->WriteUInt32(kStunMagicCookie);
buf->WriteString(transaction_id_);
for (size_t i = 0; i < attrs_->size(); ++i) {
buf->WriteUInt16((*attrs_)[i]->type());
buf->WriteUInt16(static_cast<uint16>((*attrs_)[i]->length()));
if (!(*attrs_)[i]->Write(buf))
return false;
}
return true;
}
StunAttributeValueType StunMessage::GetAttributeValueType(int type) const {
switch (type) {
case STUN_ATTR_MAPPED_ADDRESS: return STUN_VALUE_ADDRESS;
case STUN_ATTR_USERNAME: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_MESSAGE_INTEGRITY: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_ERROR_CODE: return STUN_VALUE_ERROR_CODE;
case STUN_ATTR_UNKNOWN_ATTRIBUTES: return STUN_VALUE_UINT16_LIST;
case STUN_ATTR_REALM: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_NONCE: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_XOR_MAPPED_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
case STUN_ATTR_SOFTWARE: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_ALTERNATE_SERVER: return STUN_VALUE_ADDRESS;
case STUN_ATTR_FINGERPRINT: return STUN_VALUE_UINT32;
case STUN_ATTR_RETRANSMIT_COUNT: return STUN_VALUE_UINT32;
default: return STUN_VALUE_UNKNOWN;
}
}
StunAttribute* StunMessage::CreateAttribute(int type, size_t length) /*const*/ {
StunAttributeValueType value_type = GetAttributeValueType(type);
return StunAttribute::Create(value_type, type,
static_cast<uint16>(length), this);
}
const StunAttribute* StunMessage::GetAttribute(int type) const {
for (size_t i = 0; i < attrs_->size(); ++i) {
if ((*attrs_)[i]->type() == type)
return (*attrs_)[i];
}
return NULL;
}
bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
return transaction_id.size() == kStunTransactionIdLength ||
transaction_id.size() == kStunLegacyTransactionIdLength;
}
// StunAttribute
StunAttribute::StunAttribute(uint16 type, uint16 length)
: type_(type), length_(length) {
}
void StunAttribute::ConsumePadding(rtc::ByteBuffer* buf) const {
int remainder = length_ % 4;
if (remainder > 0) {
buf->Consume(4 - remainder);
}
}
void StunAttribute::WritePadding(rtc::ByteBuffer* buf) const {
int remainder = length_ % 4;
if (remainder > 0) {
char zeroes[4] = {0};
buf->WriteBytes(zeroes, 4 - remainder);
}
}
StunAttribute* StunAttribute::Create(StunAttributeValueType value_type,
uint16 type, uint16 length,
StunMessage* owner) {
switch (value_type) {
case STUN_VALUE_ADDRESS:
return new StunAddressAttribute(type, length);
case STUN_VALUE_XOR_ADDRESS:
return new StunXorAddressAttribute(type, length, owner);
case STUN_VALUE_UINT32:
return new StunUInt32Attribute(type);
case STUN_VALUE_UINT64:
return new StunUInt64Attribute(type);
case STUN_VALUE_BYTE_STRING:
return new StunByteStringAttribute(type, length);
case STUN_VALUE_ERROR_CODE:
return new StunErrorCodeAttribute(type, length);
case STUN_VALUE_UINT16_LIST:
return new StunUInt16ListAttribute(type, length);
default:
return NULL;
}
}
StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) {
return new StunAddressAttribute(type, 0);
}
StunXorAddressAttribute* StunAttribute::CreateXorAddress(uint16 type) {
return new StunXorAddressAttribute(type, 0, NULL);
}
StunUInt64Attribute* StunAttribute::CreateUInt64(uint16 type) {
return new StunUInt64Attribute(type);
}
StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) {
return new StunUInt32Attribute(type);
}
StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) {
return new StunByteStringAttribute(type, 0);
}
StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
return new StunErrorCodeAttribute(
STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
}
StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
}
StunAddressAttribute::StunAddressAttribute(uint16 type,
const rtc::SocketAddress& addr)
: StunAttribute(type, 0) {
SetAddress(addr);
}
StunAddressAttribute::StunAddressAttribute(uint16 type, uint16 length)
: StunAttribute(type, length) {
}
bool StunAddressAttribute::Read(ByteBuffer* buf) {
uint8 dummy;
if (!buf->ReadUInt8(&dummy))
return false;
uint8 stun_family;
if (!buf->ReadUInt8(&stun_family)) {
return false;
}
uint16 port;
if (!buf->ReadUInt16(&port))
return false;
if (stun_family == STUN_ADDRESS_IPV4) {
in_addr v4addr;
if (length() != SIZE_IP4) {
return false;
}
if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
return false;
}
rtc::IPAddress ipaddr(v4addr);
SetAddress(rtc::SocketAddress(ipaddr, port));
} else if (stun_family == STUN_ADDRESS_IPV6) {
in6_addr v6addr;
if (length() != SIZE_IP6) {
return false;
}
if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
return false;
}
rtc::IPAddress ipaddr(v6addr);
SetAddress(rtc::SocketAddress(ipaddr, port));
} else {
return false;
}
return true;
}
bool StunAddressAttribute::Write(ByteBuffer* buf) const {
StunAddressFamily address_family = family();
if (address_family == STUN_ADDRESS_UNDEF) {
LOG(LS_ERROR) << "Error writing address attribute: unknown family.";
return false;
}
buf->WriteUInt8(0);
buf->WriteUInt8(address_family);
buf->WriteUInt16(address_.port());
switch (address_.family()) {
case AF_INET: {
in_addr v4addr = address_.ipaddr().ipv4_address();
buf->WriteBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr));
break;
}
case AF_INET6: {
in6_addr v6addr = address_.ipaddr().ipv6_address();
buf->WriteBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr));
break;
}
}
return true;
}
StunXorAddressAttribute::StunXorAddressAttribute(uint16 type,
const rtc::SocketAddress& addr)
: StunAddressAttribute(type, addr), owner_(NULL) {
}
StunXorAddressAttribute::StunXorAddressAttribute(uint16 type,
uint16 length,
StunMessage* owner)
: StunAddressAttribute(type, length), owner_(owner) {}
rtc::IPAddress StunXorAddressAttribute::GetXoredIP() const {
if (owner_) {
rtc::IPAddress ip = ipaddr();
switch (ip.family()) {
case AF_INET: {
in_addr v4addr = ip.ipv4_address();
v4addr.s_addr =
(v4addr.s_addr ^ rtc::HostToNetwork32(kStunMagicCookie));
return rtc::IPAddress(v4addr);
}
case AF_INET6: {
in6_addr v6addr = ip.ipv6_address();
const std::string& transaction_id = owner_->transaction_id();
if (transaction_id.length() == kStunTransactionIdLength) {
uint32 transactionid_as_ints[3];
memcpy(&transactionid_as_ints[0], transaction_id.c_str(),
transaction_id.length());
uint32* ip_as_ints = reinterpret_cast<uint32*>(&v6addr.s6_addr);
// Transaction ID is in network byte order, but magic cookie
// is stored in host byte order.
ip_as_ints[0] =
(ip_as_ints[0] ^ rtc::HostToNetwork32(kStunMagicCookie));
ip_as_ints[1] = (ip_as_ints[1] ^ transactionid_as_ints[0]);
ip_as_ints[2] = (ip_as_ints[2] ^ transactionid_as_ints[1]);
ip_as_ints[3] = (ip_as_ints[3] ^ transactionid_as_ints[2]);
return rtc::IPAddress(v6addr);
}
break;
}
}
}
// Invalid ip family or transaction ID, or missing owner.
// Return an AF_UNSPEC address.
return rtc::IPAddress();
}
bool StunXorAddressAttribute::Read(ByteBuffer* buf) {
if (!StunAddressAttribute::Read(buf))
return false;
uint16 xoredport = port() ^ (kStunMagicCookie >> 16);
rtc::IPAddress xored_ip = GetXoredIP();
SetAddress(rtc::SocketAddress(xored_ip, xoredport));
return true;
}
bool StunXorAddressAttribute::Write(ByteBuffer* buf) const {
StunAddressFamily address_family = family();
if (address_family == STUN_ADDRESS_UNDEF) {
LOG(LS_ERROR) << "Error writing xor-address attribute: unknown family.";
return false;
}
rtc::IPAddress xored_ip = GetXoredIP();
if (xored_ip.family() == AF_UNSPEC) {
return false;
}
buf->WriteUInt8(0);
buf->WriteUInt8(family());
buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
switch (xored_ip.family()) {
case AF_INET: {
in_addr v4addr = xored_ip.ipv4_address();
buf->WriteBytes(reinterpret_cast<const char*>(&v4addr), sizeof(v4addr));
break;
}
case AF_INET6: {
in6_addr v6addr = xored_ip.ipv6_address();
buf->WriteBytes(reinterpret_cast<const char*>(&v6addr), sizeof(v6addr));
break;
}
}
return true;
}
StunUInt32Attribute::StunUInt32Attribute(uint16 type, uint32 value)
: StunAttribute(type, SIZE), bits_(value) {
}
StunUInt32Attribute::StunUInt32Attribute(uint16 type)
: StunAttribute(type, SIZE), bits_(0) {
}
bool StunUInt32Attribute::GetBit(size_t index) const {
ASSERT(index < 32);
return static_cast<bool>((bits_ >> index) & 0x1);
}
void StunUInt32Attribute::SetBit(size_t index, bool value) {
ASSERT(index < 32);
bits_ &= ~(1 << index);
bits_ |= value ? (1 << index) : 0;
}
bool StunUInt32Attribute::Read(ByteBuffer* buf) {
if (length() != SIZE || !buf->ReadUInt32(&bits_))
return false;
return true;
}
bool StunUInt32Attribute::Write(ByteBuffer* buf) const {
buf->WriteUInt32(bits_);
return true;
}
StunUInt64Attribute::StunUInt64Attribute(uint16 type, uint64 value)
: StunAttribute(type, SIZE), bits_(value) {
}
StunUInt64Attribute::StunUInt64Attribute(uint16 type)
: StunAttribute(type, SIZE), bits_(0) {
}
bool StunUInt64Attribute::Read(ByteBuffer* buf) {
if (length() != SIZE || !buf->ReadUInt64(&bits_))
return false;
return true;
}
bool StunUInt64Attribute::Write(ByteBuffer* buf) const {
buf->WriteUInt64(bits_);
return true;
}
StunByteStringAttribute::StunByteStringAttribute(uint16 type)
: StunAttribute(type, 0), bytes_(NULL) {
}
StunByteStringAttribute::StunByteStringAttribute(uint16 type,
const std::string& str)
: StunAttribute(type, 0), bytes_(NULL) {
CopyBytes(str.c_str(), str.size());
}
StunByteStringAttribute::StunByteStringAttribute(uint16 type,
const void* bytes,
size_t length)
: StunAttribute(type, 0), bytes_(NULL) {
CopyBytes(bytes, length);
}
StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length)
: StunAttribute(type, length), bytes_(NULL) {
}
StunByteStringAttribute::~StunByteStringAttribute() {
delete [] bytes_;
}
void StunByteStringAttribute::CopyBytes(const char* bytes) {
CopyBytes(bytes, strlen(bytes));
}
void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
char* new_bytes = new char[length];
memcpy(new_bytes, bytes, length);
SetBytes(new_bytes, length);
}
uint8 StunByteStringAttribute::GetByte(size_t index) const {
ASSERT(bytes_ != NULL);
ASSERT(index < length());
return static_cast<uint8>(bytes_[index]);
}
void StunByteStringAttribute::SetByte(size_t index, uint8 value) {
ASSERT(bytes_ != NULL);
ASSERT(index < length());
bytes_[index] = value;
}
bool StunByteStringAttribute::Read(ByteBuffer* buf) {
bytes_ = new char[length()];
if (!buf->ReadBytes(bytes_, length())) {
return false;
}
ConsumePadding(buf);
return true;
}
bool StunByteStringAttribute::Write(ByteBuffer* buf) const {
buf->WriteBytes(bytes_, length());
WritePadding(buf);
return true;
}
void StunByteStringAttribute::SetBytes(char* bytes, size_t length) {
delete [] bytes_;
bytes_ = bytes;
SetLength(static_cast<uint16>(length));
}
StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, int code,
const std::string& reason)
: StunAttribute(type, 0) {
SetCode(code);
SetReason(reason);
}
StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length)
: StunAttribute(type, length), class_(0), number_(0) {
}
StunErrorCodeAttribute::~StunErrorCodeAttribute() {
}
int StunErrorCodeAttribute::code() const {
return class_ * 100 + number_;
}
void StunErrorCodeAttribute::SetCode(int code) {
class_ = static_cast<uint8>(code / 100);
number_ = static_cast<uint8>(code % 100);
}
void StunErrorCodeAttribute::SetReason(const std::string& reason) {
SetLength(MIN_SIZE + static_cast<uint16>(reason.size()));
reason_ = reason;
}
bool StunErrorCodeAttribute::Read(ByteBuffer* buf) {
uint32 val;
if (length() < MIN_SIZE || !buf->ReadUInt32(&val))
return false;
if ((val >> 11) != 0)
LOG(LS_ERROR) << "error-code bits not zero";
class_ = ((val >> 8) & 0x7);
number_ = (val & 0xff);
if (!buf->ReadString(&reason_, length() - 4))
return false;
ConsumePadding(buf);
return true;
}
bool StunErrorCodeAttribute::Write(ByteBuffer* buf) const {
buf->WriteUInt32(class_ << 8 | number_);
buf->WriteString(reason_);
WritePadding(buf);
return true;
}
StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length)
: StunAttribute(type, length) {
attr_types_ = new std::vector<uint16>();
}
StunUInt16ListAttribute::~StunUInt16ListAttribute() {
delete attr_types_;
}
size_t StunUInt16ListAttribute::Size() const {
return attr_types_->size();
}
uint16 StunUInt16ListAttribute::GetType(int index) const {
return (*attr_types_)[index];
}
void StunUInt16ListAttribute::SetType(int index, uint16 value) {
(*attr_types_)[index] = value;
}
void StunUInt16ListAttribute::AddType(uint16 value) {
attr_types_->push_back(value);
SetLength(static_cast<uint16>(attr_types_->size() * 2));
}
bool StunUInt16ListAttribute::Read(ByteBuffer* buf) {
if (length() % 2)
return false;
for (size_t i = 0; i < length() / 2; i++) {
uint16 attr;
if (!buf->ReadUInt16(&attr))
return false;
attr_types_->push_back(attr);
}
// Padding of these attributes is done in RFC 5389 style. This is
// slightly different from RFC3489, but it shouldn't be important.
// RFC3489 pads out to a 32 bit boundary by duplicating one of the
// entries in the list (not necessarily the last one - it's unspecified).
// RFC5389 pads on the end, and the bytes are always ignored.
ConsumePadding(buf);
return true;
}
bool StunUInt16ListAttribute::Write(ByteBuffer* buf) const {
for (size_t i = 0; i < attr_types_->size(); ++i) {
buf->WriteUInt16((*attr_types_)[i]);
}
WritePadding(buf);
return true;
}
int GetStunSuccessResponseType(int req_type) {
return IsStunRequestType(req_type) ? (req_type | 0x100) : -1;
}
int GetStunErrorResponseType(int req_type) {
return IsStunRequestType(req_type) ? (req_type | 0x110) : -1;
}
bool IsStunRequestType(int msg_type) {
return ((msg_type & kStunTypeMask) == 0x000);
}
bool IsStunIndicationType(int msg_type) {
return ((msg_type & kStunTypeMask) == 0x010);
}
bool IsStunSuccessResponseType(int msg_type) {
return ((msg_type & kStunTypeMask) == 0x100);
}
bool IsStunErrorResponseType(int msg_type) {
return ((msg_type & kStunTypeMask) == 0x110);
}
bool ComputeStunCredentialHash(const std::string& username,
const std::string& realm,
const std::string& password,
std::string* hash) {
// http://tools.ietf.org/html/rfc5389#section-15.4
// long-term credentials will be calculated using the key and key is
// key = MD5(username ":" realm ":" SASLprep(password))
std::string input = username;
input += ':';
input += realm;
input += ':';
input += password;
char digest[rtc::MessageDigest::kMaxSize];
size_t size = rtc::ComputeDigest(
rtc::DIGEST_MD5, input.c_str(), input.size(),
digest, sizeof(digest));
if (size == 0) {
return false;
}
*hash = std::string(digest, size);
return true;
}
} // namespace cricket

View File

@ -1,649 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_STUN_H_
#define WEBRTC_P2P_BASE_STUN_H_
// This file contains classes for dealing with the STUN protocol, as specified
// in RFC 5389, and its descendants.
#include <string>
#include <vector>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/bytebuffer.h"
#include "webrtc/base/socketaddress.h"
namespace cricket {
// These are the types of STUN messages defined in RFC 5389.
enum StunMessageType {
STUN_BINDING_REQUEST = 0x0001,
STUN_BINDING_INDICATION = 0x0011,
STUN_BINDING_RESPONSE = 0x0101,
STUN_BINDING_ERROR_RESPONSE = 0x0111,
};
// These are all known STUN attributes, defined in RFC 5389 and elsewhere.
// Next to each is the name of the class (T is StunTAttribute) that implements
// that type.
// RETRANSMIT_COUNT is the number of outstanding pings without a response at
// the time the packet is generated.
enum StunAttributeType {
STUN_ATTR_MAPPED_ADDRESS = 0x0001, // Address
STUN_ATTR_USERNAME = 0x0006, // ByteString
STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, // ByteString, 20 bytes
STUN_ATTR_ERROR_CODE = 0x0009, // ErrorCode
STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, // UInt16List
STUN_ATTR_REALM = 0x0014, // ByteString
STUN_ATTR_NONCE = 0x0015, // ByteString
STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020, // XorAddress
STUN_ATTR_SOFTWARE = 0x8022, // ByteString
STUN_ATTR_ALTERNATE_SERVER = 0x8023, // Address
STUN_ATTR_FINGERPRINT = 0x8028, // UInt32
STUN_ATTR_RETRANSMIT_COUNT = 0xFF00 // UInt32
};
// These are the types of the values associated with the attributes above.
// This allows us to perform some basic validation when reading or adding
// attributes. Note that these values are for our own use, and not defined in
// RFC 5389.
enum StunAttributeValueType {
STUN_VALUE_UNKNOWN = 0,
STUN_VALUE_ADDRESS = 1,
STUN_VALUE_XOR_ADDRESS = 2,
STUN_VALUE_UINT32 = 3,
STUN_VALUE_UINT64 = 4,
STUN_VALUE_BYTE_STRING = 5,
STUN_VALUE_ERROR_CODE = 6,
STUN_VALUE_UINT16_LIST = 7
};
// These are the types of STUN addresses defined in RFC 5389.
enum StunAddressFamily {
// NB: UNDEF is not part of the STUN spec.
STUN_ADDRESS_UNDEF = 0,
STUN_ADDRESS_IPV4 = 1,
STUN_ADDRESS_IPV6 = 2
};
// These are the types of STUN error codes defined in RFC 5389.
enum StunErrorCode {
STUN_ERROR_TRY_ALTERNATE = 300,
STUN_ERROR_BAD_REQUEST = 400,
STUN_ERROR_UNAUTHORIZED = 401,
STUN_ERROR_UNKNOWN_ATTRIBUTE = 420,
STUN_ERROR_STALE_CREDENTIALS = 430, // GICE only
STUN_ERROR_STALE_NONCE = 438,
STUN_ERROR_SERVER_ERROR = 500,
STUN_ERROR_GLOBAL_FAILURE = 600
};
// Strings for the error codes above.
extern const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[];
extern const char STUN_ERROR_REASON_BAD_REQUEST[];
extern const char STUN_ERROR_REASON_UNAUTHORIZED[];
extern const char STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE[];
extern const char STUN_ERROR_REASON_STALE_CREDENTIALS[];
extern const char STUN_ERROR_REASON_STALE_NONCE[];
extern const char STUN_ERROR_REASON_SERVER_ERROR[];
// The mask used to determine whether a STUN message is a request/response etc.
const uint32 kStunTypeMask = 0x0110;
// STUN Attribute header length.
const size_t kStunAttributeHeaderSize = 4;
// Following values correspond to RFC5389.
const size_t kStunHeaderSize = 20;
const size_t kStunTransactionIdOffset = 8;
const size_t kStunTransactionIdLength = 12;
const uint32 kStunMagicCookie = 0x2112A442;
const size_t kStunMagicCookieLength = sizeof(kStunMagicCookie);
// Following value corresponds to an earlier version of STUN from
// RFC3489.
const size_t kStunLegacyTransactionIdLength = 16;
// STUN Message Integrity HMAC length.
const size_t kStunMessageIntegritySize = 20;
class StunAttribute;
class StunAddressAttribute;
class StunXorAddressAttribute;
class StunUInt32Attribute;
class StunUInt64Attribute;
class StunByteStringAttribute;
class StunErrorCodeAttribute;
class StunUInt16ListAttribute;
// Records a complete STUN/TURN message. Each message consists of a type and
// any number of attributes. Each attribute is parsed into an instance of an
// appropriate class (see above). The Get* methods will return instances of
// that attribute class.
class StunMessage {
public:
StunMessage();
virtual ~StunMessage();
int type() const { return type_; }
size_t length() const { return length_; }
const std::string& transaction_id() const { return transaction_id_; }
// Returns true if the message confirms to RFC3489 rather than
// RFC5389. The main difference between two version of the STUN
// protocol is the presence of the magic cookie and different length
// of transaction ID. For outgoing packets version of the protocol
// is determined by the lengths of the transaction ID.
bool IsLegacy() const;
void SetType(int type) { type_ = static_cast<uint16>(type); }
bool SetTransactionID(const std::string& str);
// Gets the desired attribute value, or NULL if no such attribute type exists.
const StunAddressAttribute* GetAddress(int type) const;
const StunUInt32Attribute* GetUInt32(int type) const;
const StunUInt64Attribute* GetUInt64(int type) const;
const StunByteStringAttribute* GetByteString(int type) const;
// Gets these specific attribute values.
const StunErrorCodeAttribute* GetErrorCode() const;
const StunUInt16ListAttribute* GetUnknownAttributes() const;
// Takes ownership of the specified attribute, verifies it is of the correct
// type, and adds it to the message. The return value indicates whether this
// was successful.
bool AddAttribute(StunAttribute* attr);
// Validates that a raw STUN message has a correct MESSAGE-INTEGRITY value.
// This can't currently be done on a StunMessage, since it is affected by
// padding data (which we discard when reading a StunMessage).
static bool ValidateMessageIntegrity(const char* data, size_t size,
const std::string& password);
// Adds a MESSAGE-INTEGRITY attribute that is valid for the current message.
bool AddMessageIntegrity(const std::string& password);
bool AddMessageIntegrity(const char* key, size_t keylen);
// Verifies that a given buffer is STUN by checking for a correct FINGERPRINT.
static bool ValidateFingerprint(const char* data, size_t size);
// Adds a FINGERPRINT attribute that is valid for the current message.
bool AddFingerprint();
// Parses the STUN packet in the given buffer and records it here. The
// return value indicates whether this was successful.
bool Read(rtc::ByteBuffer* buf);
// Writes this object into a STUN packet. The return value indicates whether
// this was successful.
bool Write(rtc::ByteBuffer* buf) const;
// Creates an empty message. Overridable by derived classes.
virtual StunMessage* CreateNew() const { return new StunMessage(); }
protected:
// Verifies that the given attribute is allowed for this message.
virtual StunAttributeValueType GetAttributeValueType(int type) const;
private:
StunAttribute* CreateAttribute(int type, size_t length) /* const*/;
const StunAttribute* GetAttribute(int type) const;
static bool IsValidTransactionId(const std::string& transaction_id);
uint16 type_;
uint16 length_;
std::string transaction_id_;
std::vector<StunAttribute*>* attrs_;
};
// Base class for all STUN/TURN attributes.
class StunAttribute {
public:
virtual ~StunAttribute() {
}
int type() const { return type_; }
size_t length() const { return length_; }
// Return the type of this attribute.
virtual StunAttributeValueType value_type() const = 0;
// Only XorAddressAttribute needs this so far.
virtual void SetOwner(StunMessage* owner) {}
// Reads the body (not the type or length) for this type of attribute from
// the given buffer. Return value is true if successful.
virtual bool Read(rtc::ByteBuffer* buf) = 0;
// Writes the body (not the type or length) to the given buffer. Return
// value is true if successful.
virtual bool Write(rtc::ByteBuffer* buf) const = 0;
// Creates an attribute object with the given type and smallest length.
static StunAttribute* Create(StunAttributeValueType value_type, uint16 type,
uint16 length, StunMessage* owner);
// TODO: Allow these create functions to take parameters, to reduce
// the amount of work callers need to do to initialize attributes.
static StunAddressAttribute* CreateAddress(uint16 type);
static StunXorAddressAttribute* CreateXorAddress(uint16 type);
static StunUInt32Attribute* CreateUInt32(uint16 type);
static StunUInt64Attribute* CreateUInt64(uint16 type);
static StunByteStringAttribute* CreateByteString(uint16 type);
static StunErrorCodeAttribute* CreateErrorCode();
static StunUInt16ListAttribute* CreateUnknownAttributes();
protected:
StunAttribute(uint16 type, uint16 length);
void SetLength(uint16 length) { length_ = length; }
void WritePadding(rtc::ByteBuffer* buf) const;
void ConsumePadding(rtc::ByteBuffer* buf) const;
private:
uint16 type_;
uint16 length_;
};
// Implements STUN attributes that record an Internet address.
class StunAddressAttribute : public StunAttribute {
public:
static const uint16 SIZE_UNDEF = 0;
static const uint16 SIZE_IP4 = 8;
static const uint16 SIZE_IP6 = 20;
StunAddressAttribute(uint16 type, const rtc::SocketAddress& addr);
StunAddressAttribute(uint16 type, uint16 length);
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_ADDRESS;
}
StunAddressFamily family() const {
switch (address_.ipaddr().family()) {
case AF_INET:
return STUN_ADDRESS_IPV4;
case AF_INET6:
return STUN_ADDRESS_IPV6;
}
return STUN_ADDRESS_UNDEF;
}
const rtc::SocketAddress& GetAddress() const { return address_; }
const rtc::IPAddress& ipaddr() const { return address_.ipaddr(); }
uint16 port() const { return address_.port(); }
void SetAddress(const rtc::SocketAddress& addr) {
address_ = addr;
EnsureAddressLength();
}
void SetIP(const rtc::IPAddress& ip) {
address_.SetIP(ip);
EnsureAddressLength();
}
void SetPort(uint16 port) { address_.SetPort(port); }
virtual bool Read(rtc::ByteBuffer* buf);
virtual bool Write(rtc::ByteBuffer* buf) const;
private:
void EnsureAddressLength() {
switch (family()) {
case STUN_ADDRESS_IPV4: {
SetLength(SIZE_IP4);
break;
}
case STUN_ADDRESS_IPV6: {
SetLength(SIZE_IP6);
break;
}
default: {
SetLength(SIZE_UNDEF);
break;
}
}
}
rtc::SocketAddress address_;
};
// Implements STUN attributes that record an Internet address. When encoded
// in a STUN message, the address contained in this attribute is XORed with the
// transaction ID of the message.
class StunXorAddressAttribute : public StunAddressAttribute {
public:
StunXorAddressAttribute(uint16 type, const rtc::SocketAddress& addr);
StunXorAddressAttribute(uint16 type, uint16 length,
StunMessage* owner);
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_XOR_ADDRESS;
}
virtual void SetOwner(StunMessage* owner) {
owner_ = owner;
}
virtual bool Read(rtc::ByteBuffer* buf);
virtual bool Write(rtc::ByteBuffer* buf) const;
private:
rtc::IPAddress GetXoredIP() const;
StunMessage* owner_;
};
// Implements STUN attributes that record a 32-bit integer.
class StunUInt32Attribute : public StunAttribute {
public:
static const uint16 SIZE = 4;
StunUInt32Attribute(uint16 type, uint32 value);
explicit StunUInt32Attribute(uint16 type);
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_UINT32;
}
uint32 value() const { return bits_; }
void SetValue(uint32 bits) { bits_ = bits; }
bool GetBit(size_t index) const;
void SetBit(size_t index, bool value);
virtual bool Read(rtc::ByteBuffer* buf);
virtual bool Write(rtc::ByteBuffer* buf) const;
private:
uint32 bits_;
};
class StunUInt64Attribute : public StunAttribute {
public:
static const uint16 SIZE = 8;
StunUInt64Attribute(uint16 type, uint64 value);
explicit StunUInt64Attribute(uint16 type);
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_UINT64;
}
uint64 value() const { return bits_; }
void SetValue(uint64 bits) { bits_ = bits; }
virtual bool Read(rtc::ByteBuffer* buf);
virtual bool Write(rtc::ByteBuffer* buf) const;
private:
uint64 bits_;
};
// Implements STUN attributes that record an arbitrary byte string.
class StunByteStringAttribute : public StunAttribute {
public:
explicit StunByteStringAttribute(uint16 type);
StunByteStringAttribute(uint16 type, const std::string& str);
StunByteStringAttribute(uint16 type, const void* bytes, size_t length);
StunByteStringAttribute(uint16 type, uint16 length);
~StunByteStringAttribute();
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_BYTE_STRING;
}
const char* bytes() const { return bytes_; }
std::string GetString() const { return std::string(bytes_, length()); }
void CopyBytes(const char* bytes); // uses strlen
void CopyBytes(const void* bytes, size_t length);
uint8 GetByte(size_t index) const;
void SetByte(size_t index, uint8 value);
virtual bool Read(rtc::ByteBuffer* buf);
virtual bool Write(rtc::ByteBuffer* buf) const;
private:
void SetBytes(char* bytes, size_t length);
char* bytes_;
};
// Implements STUN attributes that record an error code.
class StunErrorCodeAttribute : public StunAttribute {
public:
static const uint16 MIN_SIZE = 4;
StunErrorCodeAttribute(uint16 type, int code, const std::string& reason);
StunErrorCodeAttribute(uint16 type, uint16 length);
~StunErrorCodeAttribute();
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_ERROR_CODE;
}
// The combined error and class, e.g. 0x400.
int code() const;
void SetCode(int code);
// The individual error components.
int eclass() const { return class_; }
int number() const { return number_; }
const std::string& reason() const { return reason_; }
void SetClass(uint8 eclass) { class_ = eclass; }
void SetNumber(uint8 number) { number_ = number; }
void SetReason(const std::string& reason);
bool Read(rtc::ByteBuffer* buf);
bool Write(rtc::ByteBuffer* buf) const;
private:
uint8 class_;
uint8 number_;
std::string reason_;
};
// Implements STUN attributes that record a list of attribute names.
class StunUInt16ListAttribute : public StunAttribute {
public:
StunUInt16ListAttribute(uint16 type, uint16 length);
~StunUInt16ListAttribute();
virtual StunAttributeValueType value_type() const {
return STUN_VALUE_UINT16_LIST;
}
size_t Size() const;
uint16 GetType(int index) const;
void SetType(int index, uint16 value);
void AddType(uint16 value);
bool Read(rtc::ByteBuffer* buf);
bool Write(rtc::ByteBuffer* buf) const;
private:
std::vector<uint16>* attr_types_;
};
// Returns the (successful) response type for the given request type.
// Returns -1 if |request_type| is not a valid request type.
int GetStunSuccessResponseType(int request_type);
// Returns the error response type for the given request type.
// Returns -1 if |request_type| is not a valid request type.
int GetStunErrorResponseType(int request_type);
// Returns whether a given message is a request type.
bool IsStunRequestType(int msg_type);
// Returns whether a given message is an indication type.
bool IsStunIndicationType(int msg_type);
// Returns whether a given response is a success type.
bool IsStunSuccessResponseType(int msg_type);
// Returns whether a given response is an error type.
bool IsStunErrorResponseType(int msg_type);
// Computes the STUN long-term credential hash.
bool ComputeStunCredentialHash(const std::string& username,
const std::string& realm, const std::string& password, std::string* hash);
// TODO: Move the TURN/ICE stuff below out to separate files.
extern const char TURN_MAGIC_COOKIE_VALUE[4];
// "GTURN" STUN methods.
// TODO: Rename these methods to GTURN_ to make it clear they aren't
// part of standard STUN/TURN.
enum RelayMessageType {
// For now, using the same defs from TurnMessageType below.
// STUN_ALLOCATE_REQUEST = 0x0003,
// STUN_ALLOCATE_RESPONSE = 0x0103,
// STUN_ALLOCATE_ERROR_RESPONSE = 0x0113,
STUN_SEND_REQUEST = 0x0004,
STUN_SEND_RESPONSE = 0x0104,
STUN_SEND_ERROR_RESPONSE = 0x0114,
STUN_DATA_INDICATION = 0x0115,
};
// "GTURN"-specific STUN attributes.
// TODO: Rename these attributes to GTURN_ to avoid conflicts.
enum RelayAttributeType {
STUN_ATTR_LIFETIME = 0x000d, // UInt32
STUN_ATTR_MAGIC_COOKIE = 0x000f, // ByteString, 4 bytes
STUN_ATTR_BANDWIDTH = 0x0010, // UInt32
STUN_ATTR_DESTINATION_ADDRESS = 0x0011, // Address
STUN_ATTR_SOURCE_ADDRESS2 = 0x0012, // Address
STUN_ATTR_DATA = 0x0013, // ByteString
STUN_ATTR_OPTIONS = 0x8001, // UInt32
};
// A "GTURN" STUN message.
class RelayMessage : public StunMessage {
protected:
virtual StunAttributeValueType GetAttributeValueType(int type) const {
switch (type) {
case STUN_ATTR_LIFETIME: return STUN_VALUE_UINT32;
case STUN_ATTR_MAGIC_COOKIE: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_BANDWIDTH: return STUN_VALUE_UINT32;
case STUN_ATTR_DESTINATION_ADDRESS: return STUN_VALUE_ADDRESS;
case STUN_ATTR_SOURCE_ADDRESS2: return STUN_VALUE_ADDRESS;
case STUN_ATTR_DATA: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_OPTIONS: return STUN_VALUE_UINT32;
default: return StunMessage::GetAttributeValueType(type);
}
}
virtual StunMessage* CreateNew() const { return new RelayMessage(); }
};
// Defined in TURN RFC 5766.
enum TurnMessageType {
STUN_ALLOCATE_REQUEST = 0x0003,
STUN_ALLOCATE_RESPONSE = 0x0103,
STUN_ALLOCATE_ERROR_RESPONSE = 0x0113,
TURN_REFRESH_REQUEST = 0x0004,
TURN_REFRESH_RESPONSE = 0x0104,
TURN_REFRESH_ERROR_RESPONSE = 0x0114,
TURN_SEND_INDICATION = 0x0016,
TURN_DATA_INDICATION = 0x0017,
TURN_CREATE_PERMISSION_REQUEST = 0x0008,
TURN_CREATE_PERMISSION_RESPONSE = 0x0108,
TURN_CREATE_PERMISSION_ERROR_RESPONSE = 0x0118,
TURN_CHANNEL_BIND_REQUEST = 0x0009,
TURN_CHANNEL_BIND_RESPONSE = 0x0109,
TURN_CHANNEL_BIND_ERROR_RESPONSE = 0x0119,
};
enum TurnAttributeType {
STUN_ATTR_CHANNEL_NUMBER = 0x000C, // UInt32
STUN_ATTR_TURN_LIFETIME = 0x000d, // UInt32
STUN_ATTR_XOR_PEER_ADDRESS = 0x0012, // XorAddress
// TODO(mallinath) - Uncomment after RelayAttributes are renamed.
// STUN_ATTR_DATA = 0x0013, // ByteString
STUN_ATTR_XOR_RELAYED_ADDRESS = 0x0016, // XorAddress
STUN_ATTR_EVEN_PORT = 0x0018, // ByteString, 1 byte.
STUN_ATTR_REQUESTED_TRANSPORT = 0x0019, // UInt32
STUN_ATTR_DONT_FRAGMENT = 0x001A, // No content, Length = 0
STUN_ATTR_RESERVATION_TOKEN = 0x0022, // ByteString, 8 bytes.
// TODO(mallinath) - Rename STUN_ATTR_TURN_LIFETIME to STUN_ATTR_LIFETIME and
// STUN_ATTR_TURN_DATA to STUN_ATTR_DATA. Also rename RelayMessage attributes
// by appending G to attribute name.
};
// RFC 5766-defined errors.
enum TurnErrorType {
STUN_ERROR_FORBIDDEN = 403,
STUN_ERROR_ALLOCATION_MISMATCH = 437,
STUN_ERROR_WRONG_CREDENTIALS = 441,
STUN_ERROR_UNSUPPORTED_PROTOCOL = 442
};
extern const char STUN_ERROR_REASON_FORBIDDEN[];
extern const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[];
extern const char STUN_ERROR_REASON_WRONG_CREDENTIALS[];
extern const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[];
class TurnMessage : public StunMessage {
protected:
virtual StunAttributeValueType GetAttributeValueType(int type) const {
switch (type) {
case STUN_ATTR_CHANNEL_NUMBER: return STUN_VALUE_UINT32;
case STUN_ATTR_TURN_LIFETIME: return STUN_VALUE_UINT32;
case STUN_ATTR_XOR_PEER_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
case STUN_ATTR_DATA: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_XOR_RELAYED_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
case STUN_ATTR_EVEN_PORT: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_REQUESTED_TRANSPORT: return STUN_VALUE_UINT32;
case STUN_ATTR_DONT_FRAGMENT: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_RESERVATION_TOKEN: return STUN_VALUE_BYTE_STRING;
default: return StunMessage::GetAttributeValueType(type);
}
}
virtual StunMessage* CreateNew() const { return new TurnMessage(); }
};
// RFC 5245 ICE STUN attributes.
enum IceAttributeType {
STUN_ATTR_PRIORITY = 0x0024, // UInt32
STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0
STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64
STUN_ATTR_ICE_CONTROLLING = 0x802A // UInt64
};
// RFC 5245-defined errors.
enum IceErrorCode {
STUN_ERROR_ROLE_CONFLICT = 487,
};
extern const char STUN_ERROR_REASON_ROLE_CONFLICT[];
// A RFC 5245 ICE STUN message.
class IceMessage : public StunMessage {
protected:
virtual StunAttributeValueType GetAttributeValueType(int type) const {
switch (type) {
case STUN_ATTR_PRIORITY: return STUN_VALUE_UINT32;
case STUN_ATTR_USE_CANDIDATE: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_ICE_CONTROLLED: return STUN_VALUE_UINT64;
case STUN_ATTR_ICE_CONTROLLING: return STUN_VALUE_UINT64;
default: return StunMessage::GetAttributeValueType(type);
}
}
virtual StunMessage* CreateNew() const { return new IceMessage(); }
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_STUN_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,468 +0,0 @@
/*
* 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 "webrtc/p2p/base/stunport.h"
#include "webrtc/p2p/base/common.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/stun.h"
#include "webrtc/base/common.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/nethelpers.h"
namespace cricket {
// TODO: Move these to a common place (used in relayport too)
const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts
const int RETRY_DELAY = 50; // 50ms, from ICE spec
const int RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs
// Handles a binding request sent to the STUN server.
class StunBindingRequest : public StunRequest {
public:
StunBindingRequest(UDPPort* port, bool keep_alive,
const rtc::SocketAddress& addr)
: port_(port), keep_alive_(keep_alive), server_addr_(addr) {
start_time_ = rtc::Time();
}
virtual ~StunBindingRequest() {
}
const rtc::SocketAddress& server_addr() const { return server_addr_; }
virtual void Prepare(StunMessage* request) {
request->SetType(STUN_BINDING_REQUEST);
}
virtual void OnResponse(StunMessage* response) {
const StunAddressAttribute* addr_attr =
response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
if (!addr_attr) {
LOG(LS_ERROR) << "Binding response missing mapped address.";
} else if (addr_attr->family() != STUN_ADDRESS_IPV4 &&
addr_attr->family() != STUN_ADDRESS_IPV6) {
LOG(LS_ERROR) << "Binding address has bad family";
} else {
rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
port_->OnStunBindingRequestSucceeded(server_addr_, addr);
}
// We will do a keep-alive regardless of whether this request succeeds.
// This should have almost no impact on network usage.
if (keep_alive_) {
port_->requests_.SendDelayed(
new StunBindingRequest(port_, true, server_addr_),
port_->stun_keepalive_delay());
}
}
virtual void OnErrorResponse(StunMessage* response) {
const StunErrorCodeAttribute* attr = response->GetErrorCode();
if (!attr) {
LOG(LS_ERROR) << "Bad allocate response error code";
} else {
LOG(LS_ERROR) << "Binding error response:"
<< " class=" << attr->eclass()
<< " number=" << attr->number()
<< " reason='" << attr->reason() << "'";
}
port_->OnStunBindingOrResolveRequestFailed(server_addr_);
if (keep_alive_
&& (rtc::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
port_->requests_.SendDelayed(
new StunBindingRequest(port_, true, server_addr_),
port_->stun_keepalive_delay());
}
}
virtual void OnTimeout() {
LOG(LS_ERROR) << "Binding request timed out from "
<< port_->GetLocalAddress().ToSensitiveString()
<< " (" << port_->Network()->name() << ")";
port_->OnStunBindingOrResolveRequestFailed(server_addr_);
if (keep_alive_
&& (rtc::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
port_->requests_.SendDelayed(
new StunBindingRequest(port_, true, server_addr_),
RETRY_DELAY);
}
}
private:
UDPPort* port_;
bool keep_alive_;
const rtc::SocketAddress server_addr_;
uint32 start_time_;
};
UDPPort::AddressResolver::AddressResolver(
rtc::PacketSocketFactory* factory)
: socket_factory_(factory) {}
UDPPort::AddressResolver::~AddressResolver() {
for (ResolverMap::iterator it = resolvers_.begin();
it != resolvers_.end(); ++it) {
it->second->Destroy(true);
}
}
void UDPPort::AddressResolver::Resolve(
const rtc::SocketAddress& address) {
if (resolvers_.find(address) != resolvers_.end())
return;
rtc::AsyncResolverInterface* resolver =
socket_factory_->CreateAsyncResolver();
resolvers_.insert(
std::pair<rtc::SocketAddress, rtc::AsyncResolverInterface*>(
address, resolver));
resolver->SignalDone.connect(this,
&UDPPort::AddressResolver::OnResolveResult);
resolver->Start(address);
}
bool UDPPort::AddressResolver::GetResolvedAddress(
const rtc::SocketAddress& input,
int family,
rtc::SocketAddress* output) const {
ResolverMap::const_iterator it = resolvers_.find(input);
if (it == resolvers_.end())
return false;
return it->second->GetResolvedAddress(family, output);
}
void UDPPort::AddressResolver::OnResolveResult(
rtc::AsyncResolverInterface* resolver) {
for (ResolverMap::iterator it = resolvers_.begin();
it != resolvers_.end(); ++it) {
if (it->second == resolver) {
SignalDone(it->first, resolver->GetError());
return;
}
}
}
UDPPort::UDPPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
rtc::AsyncPacketSocket* socket,
const std::string& username, const std::string& password)
: Port(thread, factory, network, socket->GetLocalAddress().ipaddr(),
username, password),
requests_(thread),
socket_(socket),
error_(0),
ready_(false),
stun_keepalive_delay_(KEEPALIVE_DELAY) {
}
UDPPort::UDPPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip, int min_port, int max_port,
const std::string& username, const std::string& password)
: Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port,
username, password),
requests_(thread),
socket_(NULL),
error_(0),
ready_(false),
stun_keepalive_delay_(KEEPALIVE_DELAY) {
}
bool UDPPort::Init() {
if (!SharedSocket()) {
ASSERT(socket_ == NULL);
socket_ = socket_factory()->CreateUdpSocket(
rtc::SocketAddress(ip(), 0), min_port(), max_port());
if (!socket_) {
LOG_J(LS_WARNING, this) << "UDP socket creation failed";
return false;
}
socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);
}
socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);
socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady);
requests_.SignalSendPacket.connect(this, &UDPPort::OnSendPacket);
return true;
}
UDPPort::~UDPPort() {
if (!SharedSocket())
delete socket_;
}
void UDPPort::PrepareAddress() {
ASSERT(requests_.empty());
if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
OnLocalAddressReady(socket_, socket_->GetLocalAddress());
}
}
void UDPPort::MaybePrepareStunCandidate() {
// Sending binding request to the STUN server if address is available to
// prepare STUN candidate.
if (!server_addresses_.empty()) {
SendStunBindingRequests();
} else {
// Port is done allocating candidates.
MaybeSetPortCompleteOrError();
}
}
Connection* UDPPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
if (address.protocol() != "udp")
return NULL;
if (!IsCompatibleAddress(address.address())) {
return NULL;
}
if (SharedSocket() && Candidates()[0].type() != LOCAL_PORT_TYPE) {
ASSERT(false);
return NULL;
}
Connection* conn = new ProxyConnection(this, 0, address);
AddConnection(conn);
return conn;
}
int UDPPort::SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload) {
int sent = socket_->SendTo(data, size, addr, options);
if (sent < 0) {
error_ = socket_->GetError();
LOG_J(LS_ERROR, this) << "UDP send of " << size
<< " bytes failed with error " << error_;
}
return sent;
}
int UDPPort::SetOption(rtc::Socket::Option opt, int value) {
return socket_->SetOption(opt, value);
}
int UDPPort::GetOption(rtc::Socket::Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int UDPPort::GetError() {
return error_;
}
void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address) {
AddAddress(address, address, rtc::SocketAddress(),
UDP_PROTOCOL_NAME, "", LOCAL_PORT_TYPE,
ICE_TYPE_PREFERENCE_HOST, 0, false);
MaybePrepareStunCandidate();
}
void UDPPort::OnReadPacket(
rtc::AsyncPacketSocket* socket, const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
ASSERT(socket == socket_);
ASSERT(!remote_addr.IsUnresolved());
// Look for a response from the STUN server.
// Even if the response doesn't match one of our outstanding requests, we
// will eat it because it might be a response to a retransmitted packet, and
// we already cleared the request when we got the first response.
if (server_addresses_.find(remote_addr) != server_addresses_.end()) {
requests_.CheckResponse(data, size);
return;
}
if (Connection* conn = GetConnection(remote_addr)) {
conn->OnReadPacket(data, size, packet_time);
} else {
Port::OnReadPacket(data, size, remote_addr, PROTO_UDP);
}
}
void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
Port::OnReadyToSend();
}
void UDPPort::SendStunBindingRequests() {
// We will keep pinging the stun server to make sure our NAT pin-hole stays
// open during the call.
ASSERT(requests_.empty());
for (ServerAddresses::const_iterator it = server_addresses_.begin();
it != server_addresses_.end(); ++it) {
SendStunBindingRequest(*it);
}
}
void UDPPort::ResolveStunAddress(const rtc::SocketAddress& stun_addr) {
if (!resolver_) {
resolver_.reset(new AddressResolver(socket_factory()));
resolver_->SignalDone.connect(this, &UDPPort::OnResolveResult);
}
resolver_->Resolve(stun_addr);
}
void UDPPort::OnResolveResult(const rtc::SocketAddress& input,
int error) {
ASSERT(resolver_.get() != NULL);
rtc::SocketAddress resolved;
if (error != 0 ||
!resolver_->GetResolvedAddress(input, ip().family(), &resolved)) {
LOG_J(LS_WARNING, this) << "StunPort: stun host lookup received error "
<< error;
OnStunBindingOrResolveRequestFailed(input);
return;
}
server_addresses_.erase(input);
if (server_addresses_.find(resolved) == server_addresses_.end()) {
server_addresses_.insert(resolved);
SendStunBindingRequest(resolved);
}
}
void UDPPort::SendStunBindingRequest(
const rtc::SocketAddress& stun_addr) {
if (stun_addr.IsUnresolved()) {
ResolveStunAddress(stun_addr);
} else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
// Check if |server_addr_| is compatible with the port's ip.
if (IsCompatibleAddress(stun_addr)) {
requests_.Send(new StunBindingRequest(this, true, stun_addr));
} else {
// Since we can't send stun messages to the server, we should mark this
// port ready.
LOG(LS_WARNING) << "STUN server address is incompatible.";
OnStunBindingOrResolveRequestFailed(stun_addr);
}
}
}
void UDPPort::OnStunBindingRequestSucceeded(
const rtc::SocketAddress& stun_server_addr,
const rtc::SocketAddress& stun_reflected_addr) {
if (bind_request_succeeded_servers_.find(stun_server_addr) !=
bind_request_succeeded_servers_.end()) {
return;
}
bind_request_succeeded_servers_.insert(stun_server_addr);
// If socket is shared and |stun_reflected_addr| is equal to local socket
// address, or if the same address has been added by another STUN server,
// then discarding the stun address.
// For STUN, related address is the local socket address.
if ((!SharedSocket() || stun_reflected_addr != socket_->GetLocalAddress()) &&
!HasCandidateWithAddress(stun_reflected_addr)) {
rtc::SocketAddress related_address = socket_->GetLocalAddress();
if (!(candidate_filter() & CF_HOST)) {
// If candidate filter doesn't have CF_HOST specified, empty raddr to
// avoid local address leakage.
related_address = rtc::EmptySocketAddressWithFamily(
related_address.family());
}
AddAddress(stun_reflected_addr, socket_->GetLocalAddress(),
related_address, UDP_PROTOCOL_NAME, "",
STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, false);
}
MaybeSetPortCompleteOrError();
}
void UDPPort::OnStunBindingOrResolveRequestFailed(
const rtc::SocketAddress& stun_server_addr) {
if (bind_request_failed_servers_.find(stun_server_addr) !=
bind_request_failed_servers_.end()) {
return;
}
bind_request_failed_servers_.insert(stun_server_addr);
MaybeSetPortCompleteOrError();
}
void UDPPort::MaybeSetPortCompleteOrError() {
if (ready_)
return;
// Do not set port ready if we are still waiting for bind responses.
const size_t servers_done_bind_request = bind_request_failed_servers_.size() +
bind_request_succeeded_servers_.size();
if (server_addresses_.size() != servers_done_bind_request) {
return;
}
// Setting ready status.
ready_ = true;
// The port is "completed" if there is no stun server provided, or the bind
// request succeeded for any stun server, or the socket is shared.
if (server_addresses_.empty() ||
bind_request_succeeded_servers_.size() > 0 ||
SharedSocket()) {
SignalPortComplete(this);
} else {
SignalPortError(this);
}
}
// TODO: merge this with SendTo above.
void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req);
rtc::PacketOptions options(DefaultDscpValue());
if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0)
PLOG(LERROR, socket_->GetError()) << "sendto";
}
bool UDPPort::HasCandidateWithAddress(const rtc::SocketAddress& addr) const {
const std::vector<Candidate>& existing_candidates = Candidates();
std::vector<Candidate>::const_iterator it = existing_candidates.begin();
for (; it != existing_candidates.end(); ++it) {
if (it->address() == addr)
return true;
}
return false;
}
} // namespace cricket

View File

@ -1,255 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_STUNPORT_H_
#define WEBRTC_P2P_BASE_STUNPORT_H_
#include <string>
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/stunrequest.h"
#include "webrtc/base/asyncpacketsocket.h"
// TODO(mallinath) - Rename stunport.cc|h to udpport.cc|h.
namespace rtc {
class AsyncResolver;
class SignalThread;
}
namespace cricket {
// Communicates using the address on the outside of a NAT.
class UDPPort : public Port {
public:
static UDPPort* Create(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
rtc::AsyncPacketSocket* socket,
const std::string& username,
const std::string& password) {
UDPPort* port = new UDPPort(thread, factory, network, socket,
username, password);
if (!port->Init()) {
delete port;
port = NULL;
}
return port;
}
static UDPPort* Create(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username,
const std::string& password) {
UDPPort* port = new UDPPort(thread, factory, network,
ip, min_port, max_port,
username, password);
if (!port->Init()) {
delete port;
port = NULL;
}
return port;
}
virtual ~UDPPort();
rtc::SocketAddress GetLocalAddress() const {
return socket_->GetLocalAddress();
}
const ServerAddresses& server_addresses() const {
return server_addresses_;
}
void
set_server_addresses(const ServerAddresses& addresses) {
server_addresses_ = addresses;
}
virtual void PrepareAddress();
virtual Connection* CreateConnection(const Candidate& address,
CandidateOrigin origin);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetOption(rtc::Socket::Option opt, int* value);
virtual int GetError();
virtual bool HandleIncomingPacket(
rtc::AsyncPacketSocket* socket, const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
// All packets given to UDP port will be consumed.
OnReadPacket(socket, data, size, remote_addr, packet_time);
return true;
}
void set_stun_keepalive_delay(int delay) {
stun_keepalive_delay_ = delay;
}
int stun_keepalive_delay() const {
return stun_keepalive_delay_;
}
protected:
UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username, const std::string& password);
UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, rtc::AsyncPacketSocket* socket,
const std::string& username, const std::string& password);
bool Init();
virtual int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload);
void OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address);
void OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
// This method will send STUN binding request if STUN server address is set.
void MaybePrepareStunCandidate();
void SendStunBindingRequests();
private:
// A helper class which can be called repeatedly to resolve multiple
// addresses, as opposed to rtc::AsyncResolverInterface, which can only
// resolve one address per instance.
class AddressResolver : public sigslot::has_slots<> {
public:
explicit AddressResolver(rtc::PacketSocketFactory* factory);
~AddressResolver();
void Resolve(const rtc::SocketAddress& address);
bool GetResolvedAddress(const rtc::SocketAddress& input,
int family,
rtc::SocketAddress* output) const;
// The signal is sent when resolving the specified address is finished. The
// first argument is the input address, the second argument is the error
// or 0 if it succeeded.
sigslot::signal2<const rtc::SocketAddress&, int> SignalDone;
private:
typedef std::map<rtc::SocketAddress,
rtc::AsyncResolverInterface*> ResolverMap;
void OnResolveResult(rtc::AsyncResolverInterface* resolver);
rtc::PacketSocketFactory* socket_factory_;
ResolverMap resolvers_;
};
// DNS resolution of the STUN server.
void ResolveStunAddress(const rtc::SocketAddress& stun_addr);
void OnResolveResult(const rtc::SocketAddress& input, int error);
void SendStunBindingRequest(const rtc::SocketAddress& stun_addr);
// Below methods handles binding request responses.
void OnStunBindingRequestSucceeded(
const rtc::SocketAddress& stun_server_addr,
const rtc::SocketAddress& stun_reflected_addr);
void OnStunBindingOrResolveRequestFailed(
const rtc::SocketAddress& stun_server_addr);
// Sends STUN requests to the server.
void OnSendPacket(const void* data, size_t size, StunRequest* req);
// TODO(mallinaht) - Move this up to cricket::Port when SignalAddressReady is
// changed to SignalPortReady.
void MaybeSetPortCompleteOrError();
bool HasCandidateWithAddress(const rtc::SocketAddress& addr) const;
ServerAddresses server_addresses_;
ServerAddresses bind_request_succeeded_servers_;
ServerAddresses bind_request_failed_servers_;
StunRequestManager requests_;
rtc::AsyncPacketSocket* socket_;
int error_;
rtc::scoped_ptr<AddressResolver> resolver_;
bool ready_;
int stun_keepalive_delay_;
friend class StunBindingRequest;
};
class StunPort : public UDPPort {
public:
static StunPort* Create(
rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username,
const std::string& password,
const ServerAddresses& servers) {
StunPort* port = new StunPort(thread, factory, network,
ip, min_port, max_port,
username, password, servers);
if (!port->Init()) {
delete port;
port = NULL;
}
return port;
}
virtual ~StunPort() {}
virtual void PrepareAddress() {
SendStunBindingRequests();
}
protected:
StunPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username, const std::string& password,
const ServerAddresses& servers)
: UDPPort(thread, factory, network, ip, min_port, max_port, username,
password) {
// UDPPort will set these to local udp, updating these to STUN.
set_type(STUN_PORT_TYPE);
set_server_addresses(servers);
}
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_STUNPORT_H_

View File

@ -1,300 +0,0 @@
/*
* libjingle
* Copyright 2009 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 "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/stunport.h"
#include "webrtc/p2p/base/teststunserver.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/virtualsocketserver.h"
using cricket::ServerAddresses;
using rtc::SocketAddress;
static const SocketAddress kLocalAddr("127.0.0.1", 0);
static const SocketAddress kStunAddr1("127.0.0.1", 5000);
static const SocketAddress kStunAddr2("127.0.0.1", 4000);
static const SocketAddress kBadAddr("0.0.0.1", 5000);
static const SocketAddress kStunHostnameAddr("localhost", 5000);
static const SocketAddress kBadHostnameAddr("not-a-real-hostname", 5000);
static const int kTimeoutMs = 10000;
// stun prio = 100 << 24 | 30 (IPV4) << 8 | 256 - 0
static const uint32 kStunCandidatePriority = 1677729535;
// Tests connecting a StunPort to a fake STUN server (cricket::StunServer)
// TODO: Use a VirtualSocketServer here. We have to use a
// PhysicalSocketServer right now since DNS is not part of SocketServer yet.
class StunPortTest : public testing::Test,
public sigslot::has_slots<> {
public:
StunPortTest()
: pss_(new rtc::PhysicalSocketServer),
ss_(new rtc::VirtualSocketServer(pss_.get())),
ss_scope_(ss_.get()),
network_("unittest", "unittest", rtc::IPAddress(INADDR_ANY), 32),
socket_factory_(rtc::Thread::Current()),
stun_server_1_(cricket::TestStunServer::Create(
rtc::Thread::Current(), kStunAddr1)),
stun_server_2_(cricket::TestStunServer::Create(
rtc::Thread::Current(), kStunAddr2)),
done_(false), error_(false), stun_keepalive_delay_(0) {
}
const cricket::Port* port() const { return stun_port_.get(); }
bool done() const { return done_; }
bool error() const { return error_; }
void CreateStunPort(const rtc::SocketAddress& server_addr) {
ServerAddresses stun_servers;
stun_servers.insert(server_addr);
CreateStunPort(stun_servers);
}
void CreateStunPort(const ServerAddresses& stun_servers) {
stun_port_.reset(cricket::StunPort::Create(
rtc::Thread::Current(), &socket_factory_, &network_,
kLocalAddr.ipaddr(), 0, 0, rtc::CreateRandomString(16),
rtc::CreateRandomString(22), stun_servers));
stun_port_->set_stun_keepalive_delay(stun_keepalive_delay_);
stun_port_->SignalPortComplete.connect(this,
&StunPortTest::OnPortComplete);
stun_port_->SignalPortError.connect(this,
&StunPortTest::OnPortError);
}
void CreateSharedStunPort(const rtc::SocketAddress& server_addr) {
socket_.reset(socket_factory_.CreateUdpSocket(
rtc::SocketAddress(kLocalAddr.ipaddr(), 0), 0, 0));
ASSERT_TRUE(socket_ != NULL);
socket_->SignalReadPacket.connect(this, &StunPortTest::OnReadPacket);
stun_port_.reset(cricket::UDPPort::Create(
rtc::Thread::Current(), &socket_factory_,
&network_, socket_.get(),
rtc::CreateRandomString(16), rtc::CreateRandomString(22)));
ASSERT_TRUE(stun_port_ != NULL);
ServerAddresses stun_servers;
stun_servers.insert(server_addr);
stun_port_->set_server_addresses(stun_servers);
stun_port_->SignalPortComplete.connect(this,
&StunPortTest::OnPortComplete);
stun_port_->SignalPortError.connect(this,
&StunPortTest::OnPortError);
}
void PrepareAddress() {
stun_port_->PrepareAddress();
}
void OnReadPacket(rtc::AsyncPacketSocket* socket, const char* data,
size_t size, const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
stun_port_->HandleIncomingPacket(
socket, data, size, remote_addr, rtc::PacketTime());
}
void SendData(const char* data, size_t len) {
stun_port_->HandleIncomingPacket(
socket_.get(), data, len, rtc::SocketAddress("22.22.22.22", 0),
rtc::PacketTime());
}
protected:
static void SetUpTestCase() {
// Ensure the RNG is inited.
rtc::InitRandom(NULL, 0);
}
void OnPortComplete(cricket::Port* port) {
ASSERT_FALSE(done_);
done_ = true;
error_ = false;
}
void OnPortError(cricket::Port* port) {
done_ = true;
error_ = true;
}
void SetKeepaliveDelay(int delay) {
stun_keepalive_delay_ = delay;
}
cricket::TestStunServer* stun_server_1() {
return stun_server_1_.get();
}
cricket::TestStunServer* stun_server_2() {
return stun_server_2_.get();
}
private:
rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
rtc::scoped_ptr<rtc::VirtualSocketServer> ss_;
rtc::SocketServerScope ss_scope_;
rtc::Network network_;
rtc::BasicPacketSocketFactory socket_factory_;
rtc::scoped_ptr<cricket::UDPPort> stun_port_;
rtc::scoped_ptr<cricket::TestStunServer> stun_server_1_;
rtc::scoped_ptr<cricket::TestStunServer> stun_server_2_;
rtc::scoped_ptr<rtc::AsyncPacketSocket> socket_;
bool done_;
bool error_;
int stun_keepalive_delay_;
};
// Test that we can create a STUN port
TEST_F(StunPortTest, TestBasic) {
CreateStunPort(kStunAddr1);
EXPECT_EQ("stun", port()->Type());
EXPECT_EQ(0U, port()->Candidates().size());
}
// Test that we can get an address from a STUN server.
TEST_F(StunPortTest, TestPrepareAddress) {
CreateStunPort(kStunAddr1);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
ASSERT_EQ(1U, port()->Candidates().size());
EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
// TODO: Add IPv6 tests here, once either physicalsocketserver supports
// IPv6, or this test is changed to use VirtualSocketServer.
}
// Test that we fail properly if we can't get an address.
TEST_F(StunPortTest, TestPrepareAddressFail) {
CreateStunPort(kBadAddr);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
EXPECT_TRUE(error());
EXPECT_EQ(0U, port()->Candidates().size());
}
// Test that we can get an address from a STUN server specified by a hostname.
TEST_F(StunPortTest, TestPrepareAddressHostname) {
CreateStunPort(kStunHostnameAddr);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
ASSERT_EQ(1U, port()->Candidates().size());
EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
EXPECT_EQ(kStunCandidatePriority, port()->Candidates()[0].priority());
}
// Test that we handle hostname lookup failures properly.
TEST_F(StunPortTest, TestPrepareAddressHostnameFail) {
CreateStunPort(kBadHostnameAddr);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
EXPECT_TRUE(error());
EXPECT_EQ(0U, port()->Candidates().size());
}
// This test verifies keepalive response messages don't result in
// additional candidate generation.
TEST_F(StunPortTest, TestKeepAliveResponse) {
SetKeepaliveDelay(500); // 500ms of keepalive delay.
CreateStunPort(kStunHostnameAddr);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
ASSERT_EQ(1U, port()->Candidates().size());
EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
// Waiting for 1 seond, which will allow us to process
// response for keepalive binding request. 500 ms is the keepalive delay.
rtc::Thread::Current()->ProcessMessages(1000);
ASSERT_EQ(1U, port()->Candidates().size());
}
// Test that a local candidate can be generated using a shared socket.
TEST_F(StunPortTest, TestSharedSocketPrepareAddress) {
CreateSharedStunPort(kStunAddr1);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
ASSERT_EQ(1U, port()->Candidates().size());
EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
}
// Test that we still a get a local candidate with invalid stun server hostname.
// Also verifing that UDPPort can receive packets when stun address can't be
// resolved.
TEST_F(StunPortTest, TestSharedSocketPrepareAddressInvalidHostname) {
CreateSharedStunPort(kBadHostnameAddr);
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
ASSERT_EQ(1U, port()->Candidates().size());
EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address()));
// Send data to port after it's ready. This is to make sure, UDP port can
// handle data with unresolved stun server address.
std::string data = "some random data, sending to cricket::Port.";
SendData(data.c_str(), data.length());
// No crash is success.
}
// Test that the same address is added only once if two STUN servers are in use.
TEST_F(StunPortTest, TestNoDuplicatedAddressWithTwoStunServers) {
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr1);
stun_servers.insert(kStunAddr2);
CreateStunPort(stun_servers);
EXPECT_EQ("stun", port()->Type());
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
EXPECT_EQ(1U, port()->Candidates().size());
}
// Test that candidates can be allocated for multiple STUN servers, one of which
// is not reachable.
TEST_F(StunPortTest, TestMultipleStunServersWithBadServer) {
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr1);
stun_servers.insert(kBadAddr);
CreateStunPort(stun_servers);
EXPECT_EQ("stun", port()->Type());
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
EXPECT_EQ(1U, port()->Candidates().size());
}
// Test that two candidates are allocated if the two STUN servers return
// different mapped addresses.
TEST_F(StunPortTest, TestTwoCandidatesWithTwoStunServersAcrossNat) {
const SocketAddress kStunMappedAddr1("77.77.77.77", 0);
const SocketAddress kStunMappedAddr2("88.77.77.77", 0);
stun_server_1()->set_fake_stun_addr(kStunMappedAddr1);
stun_server_2()->set_fake_stun_addr(kStunMappedAddr2);
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr1);
stun_servers.insert(kStunAddr2);
CreateStunPort(stun_servers);
EXPECT_EQ("stun", port()->Type());
PrepareAddress();
EXPECT_TRUE_WAIT(done(), kTimeoutMs);
EXPECT_EQ(2U, port()->Candidates().size());
}

View File

@ -1,210 +0,0 @@
/*
* 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 "webrtc/p2p/base/stunrequest.h"
#include "webrtc/base/common.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
namespace cricket {
const uint32 MSG_STUN_SEND = 1;
const int MAX_SENDS = 9;
const int DELAY_UNIT = 100; // 100 milliseconds
const int DELAY_MAX_FACTOR = 16;
StunRequestManager::StunRequestManager(rtc::Thread* thread)
: thread_(thread) {
}
StunRequestManager::~StunRequestManager() {
while (requests_.begin() != requests_.end()) {
StunRequest *request = requests_.begin()->second;
requests_.erase(requests_.begin());
delete request;
}
}
void StunRequestManager::Send(StunRequest* request) {
SendDelayed(request, 0);
}
void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
request->set_manager(this);
ASSERT(requests_.find(request->id()) == requests_.end());
request->Construct();
requests_[request->id()] = request;
thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL);
}
void StunRequestManager::Remove(StunRequest* request) {
ASSERT(request->manager() == this);
RequestMap::iterator iter = requests_.find(request->id());
if (iter != requests_.end()) {
ASSERT(iter->second == request);
requests_.erase(iter);
thread_->Clear(request);
}
}
void StunRequestManager::Clear() {
std::vector<StunRequest*> requests;
for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i)
requests.push_back(i->second);
for (uint32 i = 0; i < requests.size(); ++i) {
// StunRequest destructor calls Remove() which deletes requests
// from |requests_|.
delete requests[i];
}
}
bool StunRequestManager::CheckResponse(StunMessage* msg) {
RequestMap::iterator iter = requests_.find(msg->transaction_id());
if (iter == requests_.end())
return false;
StunRequest* request = iter->second;
if (msg->type() == GetStunSuccessResponseType(request->type())) {
request->OnResponse(msg);
} else if (msg->type() == GetStunErrorResponseType(request->type())) {
request->OnErrorResponse(msg);
} else {
LOG(LERROR) << "Received response with wrong type: " << msg->type()
<< " (expecting "
<< GetStunSuccessResponseType(request->type()) << ")";
return false;
}
delete request;
return true;
}
bool StunRequestManager::CheckResponse(const char* data, size_t size) {
// Check the appropriate bytes of the stream to see if they match the
// transaction ID of a response we are expecting.
if (size < 20)
return false;
std::string id;
id.append(data + kStunTransactionIdOffset, kStunTransactionIdLength);
RequestMap::iterator iter = requests_.find(id);
if (iter == requests_.end())
return false;
// Parse the STUN message and continue processing as usual.
rtc::ByteBuffer buf(data, size);
rtc::scoped_ptr<StunMessage> response(iter->second->msg_->CreateNew());
if (!response->Read(&buf))
return false;
return CheckResponse(response.get());
}
StunRequest::StunRequest()
: count_(0), timeout_(false), manager_(0),
msg_(new StunMessage()), tstamp_(0) {
msg_->SetTransactionID(
rtc::CreateRandomString(kStunTransactionIdLength));
}
StunRequest::StunRequest(StunMessage* request)
: count_(0), timeout_(false), manager_(0),
msg_(request), tstamp_(0) {
msg_->SetTransactionID(
rtc::CreateRandomString(kStunTransactionIdLength));
}
StunRequest::~StunRequest() {
ASSERT(manager_ != NULL);
if (manager_) {
manager_->Remove(this);
manager_->thread_->Clear(this);
}
delete msg_;
}
void StunRequest::Construct() {
if (msg_->type() == 0) {
Prepare(msg_);
ASSERT(msg_->type() != 0);
}
}
int StunRequest::type() {
ASSERT(msg_ != NULL);
return msg_->type();
}
const StunMessage* StunRequest::msg() const {
return msg_;
}
uint32 StunRequest::Elapsed() const {
return rtc::TimeSince(tstamp_);
}
void StunRequest::set_manager(StunRequestManager* manager) {
ASSERT(!manager_);
manager_ = manager;
}
void StunRequest::OnMessage(rtc::Message* pmsg) {
ASSERT(manager_ != NULL);
ASSERT(pmsg->message_id == MSG_STUN_SEND);
if (timeout_) {
OnTimeout();
delete this;
return;
}
tstamp_ = rtc::Time();
rtc::ByteBuffer buf;
msg_->Write(&buf);
manager_->SignalSendPacket(buf.Data(), buf.Length(), this);
int delay = GetNextDelay();
manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL);
}
int StunRequest::GetNextDelay() {
int delay = DELAY_UNIT * rtc::_min(1 << count_, DELAY_MAX_FACTOR);
count_ += 1;
if (count_ == MAX_SENDS)
timeout_ = true;
return delay;
}
} // namespace cricket

View File

@ -1,133 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_STUNREQUEST_H_
#define WEBRTC_P2P_BASE_STUNREQUEST_H_
#include <map>
#include <string>
#include "webrtc/p2p/base/stun.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/thread.h"
namespace cricket {
class StunRequest;
// Manages a set of STUN requests, sending and resending until we receive a
// response or determine that the request has timed out.
class StunRequestManager {
public:
StunRequestManager(rtc::Thread* thread);
~StunRequestManager();
// Starts sending the given request (perhaps after a delay).
void Send(StunRequest* request);
void SendDelayed(StunRequest* request, int delay);
// Removes a stun request that was added previously. This will happen
// automatically when a request succeeds, fails, or times out.
void Remove(StunRequest* request);
// Removes all stun requests that were added previously.
void Clear();
// Determines whether the given message is a response to one of the
// outstanding requests, and if so, processes it appropriately.
bool CheckResponse(StunMessage* msg);
bool CheckResponse(const char* data, size_t size);
bool empty() { return requests_.empty(); }
// Raised when there are bytes to be sent.
sigslot::signal3<const void*, size_t, StunRequest*> SignalSendPacket;
private:
typedef std::map<std::string, StunRequest*> RequestMap;
rtc::Thread* thread_;
RequestMap requests_;
friend class StunRequest;
};
// Represents an individual request to be sent. The STUN message can either be
// constructed beforehand or built on demand.
class StunRequest : public rtc::MessageHandler {
public:
StunRequest();
StunRequest(StunMessage* request);
virtual ~StunRequest();
// Causes our wrapped StunMessage to be Prepared
void Construct();
// The manager handling this request (if it has been scheduled for sending).
StunRequestManager* manager() { return manager_; }
// Returns the transaction ID of this request.
const std::string& id() { return msg_->transaction_id(); }
// Returns the STUN type of the request message.
int type();
// Returns a const pointer to |msg_|.
const StunMessage* msg() const;
// Time elapsed since last send (in ms)
uint32 Elapsed() const;
protected:
int count_;
bool timeout_;
// Fills in a request object to be sent. Note that request's transaction ID
// will already be set and cannot be changed.
virtual void Prepare(StunMessage* request) {}
// Called when the message receives a response or times out.
virtual void OnResponse(StunMessage* response) {}
virtual void OnErrorResponse(StunMessage* response) {}
virtual void OnTimeout() {}
virtual int GetNextDelay();
private:
void set_manager(StunRequestManager* manager);
// Handles messages for sending and timeout.
void OnMessage(rtc::Message* pmsg);
StunRequestManager* manager_;
StunMessage* msg_;
uint32 tstamp_;
friend class StunRequestManager;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_STUNREQUEST_H_

View File

@ -1,220 +0,0 @@
/*
* libjingle
* Copyright 2004 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 "webrtc/p2p/base/stunrequest.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/timeutils.h"
using namespace cricket;
class StunRequestTest : public testing::Test,
public sigslot::has_slots<> {
public:
StunRequestTest()
: manager_(rtc::Thread::Current()),
request_count_(0), response_(NULL),
success_(false), failure_(false), timeout_(false) {
manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket);
}
void OnSendPacket(const void* data, size_t size, StunRequest* req) {
request_count_++;
}
void OnResponse(StunMessage* res) {
response_ = res;
success_ = true;
}
void OnErrorResponse(StunMessage* res) {
response_ = res;
failure_ = true;
}
void OnTimeout() {
timeout_ = true;
}
protected:
static StunMessage* CreateStunMessage(StunMessageType type,
StunMessage* req) {
StunMessage* msg = new StunMessage();
msg->SetType(type);
if (req) {
msg->SetTransactionID(req->transaction_id());
}
return msg;
}
static int TotalDelay(int sends) {
int total = 0;
for (int i = 0; i < sends; i++) {
if (i < 4)
total += 100 << i;
else
total += 1600;
}
return total;
}
StunRequestManager manager_;
int request_count_;
StunMessage* response_;
bool success_;
bool failure_;
bool timeout_;
};
// Forwards results to the test class.
class StunRequestThunker : public StunRequest {
public:
StunRequestThunker(StunMessage* msg, StunRequestTest* test)
: StunRequest(msg), test_(test) {}
explicit StunRequestThunker(StunRequestTest* test) : test_(test) {}
private:
virtual void OnResponse(StunMessage* res) {
test_->OnResponse(res);
}
virtual void OnErrorResponse(StunMessage* res) {
test_->OnErrorResponse(res);
}
virtual void OnTimeout() {
test_->OnTimeout();
}
virtual void Prepare(StunMessage* request) {
request->SetType(STUN_BINDING_REQUEST);
}
StunRequestTest* test_;
};
// Test handling of a normal binding response.
TEST_F(StunRequestTest, TestSuccess) {
StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
manager_.Send(new StunRequestThunker(req, this));
StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
EXPECT_TRUE(manager_.CheckResponse(res));
EXPECT_TRUE(response_ == res);
EXPECT_TRUE(success_);
EXPECT_FALSE(failure_);
EXPECT_FALSE(timeout_);
delete res;
}
// Test handling of an error binding response.
TEST_F(StunRequestTest, TestError) {
StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
manager_.Send(new StunRequestThunker(req, this));
StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req);
EXPECT_TRUE(manager_.CheckResponse(res));
EXPECT_TRUE(response_ == res);
EXPECT_FALSE(success_);
EXPECT_TRUE(failure_);
EXPECT_FALSE(timeout_);
delete res;
}
// Test handling of a binding response with the wrong transaction id.
TEST_F(StunRequestTest, TestUnexpected) {
StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
manager_.Send(new StunRequestThunker(req, this));
StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, NULL);
EXPECT_FALSE(manager_.CheckResponse(res));
EXPECT_TRUE(response_ == NULL);
EXPECT_FALSE(success_);
EXPECT_FALSE(failure_);
EXPECT_FALSE(timeout_);
delete res;
}
// Test that requests are sent at the right times, and that the 9th request
// (sent at 7900 ms) can be properly replied to.
TEST_F(StunRequestTest, TestBackoff) {
StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
uint32 start = rtc::Time();
manager_.Send(new StunRequestThunker(req, this));
StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
for (int i = 0; i < 9; ++i) {
while (request_count_ == i)
rtc::Thread::Current()->ProcessMessages(1);
int32 elapsed = rtc::TimeSince(start);
LOG(LS_INFO) << "STUN request #" << (i + 1)
<< " sent at " << elapsed << " ms";
EXPECT_GE(TotalDelay(i + 1), elapsed);
}
EXPECT_TRUE(manager_.CheckResponse(res));
EXPECT_TRUE(response_ == res);
EXPECT_TRUE(success_);
EXPECT_FALSE(failure_);
EXPECT_FALSE(timeout_);
delete res;
}
// Test that we timeout properly if no response is received in 9500 ms.
TEST_F(StunRequestTest, TestTimeout) {
StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
manager_.Send(new StunRequestThunker(req, this));
rtc::Thread::Current()->ProcessMessages(10000); // > STUN timeout
EXPECT_FALSE(manager_.CheckResponse(res));
EXPECT_TRUE(response_ == NULL);
EXPECT_FALSE(success_);
EXPECT_FALSE(failure_);
EXPECT_TRUE(timeout_);
delete res;
}
// Regression test for specific crash where we receive a response with the
// same id as a request that doesn't have an underlying StunMessage yet.
TEST_F(StunRequestTest, TestNoEmptyRequest) {
StunRequestThunker* request = new StunRequestThunker(this);
manager_.SendDelayed(request, 100);
StunMessage dummy_req;
dummy_req.SetTransactionID(request->id());
StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req);
EXPECT_TRUE(manager_.CheckResponse(res));
EXPECT_TRUE(response_ == res);
EXPECT_TRUE(success_);
EXPECT_FALSE(failure_);
EXPECT_FALSE(timeout_);
delete res;
}

View File

@ -1,116 +0,0 @@
/*
* 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 "webrtc/p2p/base/stunserver.h"
#include "webrtc/base/bytebuffer.h"
#include "webrtc/base/logging.h"
namespace cricket {
StunServer::StunServer(rtc::AsyncUDPSocket* socket) : socket_(socket) {
socket_->SignalReadPacket.connect(this, &StunServer::OnPacket);
}
StunServer::~StunServer() {
socket_->SignalReadPacket.disconnect(this);
}
void StunServer::OnPacket(
rtc::AsyncPacketSocket* socket, const char* buf, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
// Parse the STUN message; eat any messages that fail to parse.
rtc::ByteBuffer bbuf(buf, size);
StunMessage msg;
if (!msg.Read(&bbuf)) {
return;
}
// TODO: If unknown non-optional (<= 0x7fff) attributes are found, send a
// 420 "Unknown Attribute" response.
// Send the message to the appropriate handler function.
switch (msg.type()) {
case STUN_BINDING_REQUEST:
OnBindingRequest(&msg, remote_addr);
break;
default:
SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported");
}
}
void StunServer::OnBindingRequest(
StunMessage* msg, const rtc::SocketAddress& remote_addr) {
StunMessage response;
GetStunBindReqponse(msg, remote_addr, &response);
SendResponse(response, remote_addr);
}
void StunServer::SendErrorResponse(
const StunMessage& msg, const rtc::SocketAddress& addr,
int error_code, const char* error_desc) {
StunMessage err_msg;
err_msg.SetType(GetStunErrorResponseType(msg.type()));
err_msg.SetTransactionID(msg.transaction_id());
StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
err_code->SetCode(error_code);
err_code->SetReason(error_desc);
err_msg.AddAttribute(err_code);
SendResponse(err_msg, addr);
}
void StunServer::SendResponse(
const StunMessage& msg, const rtc::SocketAddress& addr) {
rtc::ByteBuffer buf;
msg.Write(&buf);
rtc::PacketOptions options;
if (socket_->SendTo(buf.Data(), buf.Length(), addr, options) < 0)
LOG_ERR(LS_ERROR) << "sendto";
}
void StunServer::GetStunBindReqponse(StunMessage* request,
const rtc::SocketAddress& remote_addr,
StunMessage* response) const {
response->SetType(STUN_BINDING_RESPONSE);
response->SetTransactionID(request->transaction_id());
// Tell the user the address that we received their request from.
StunAddressAttribute* mapped_addr;
if (!request->IsLegacy()) {
mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
} else {
mapped_addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
}
mapped_addr->SetAddress(remote_addr);
response->AddAttribute(mapped_addr);
}
} // namespace cricket

View File

@ -1,83 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_STUNSERVER_H_
#define WEBRTC_P2P_BASE_STUNSERVER_H_
#include "webrtc/p2p/base/stun.h"
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/scoped_ptr.h"
namespace cricket {
const int STUN_SERVER_PORT = 3478;
class StunServer : public sigslot::has_slots<> {
public:
// Creates a STUN server, which will listen on the given socket.
explicit StunServer(rtc::AsyncUDPSocket* socket);
// Removes the STUN server from the socket and deletes the socket.
~StunServer();
protected:
// Slot for AsyncSocket.PacketRead:
void OnPacket(
rtc::AsyncPacketSocket* socket, const char* buf, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
// Handlers for the different types of STUN/TURN requests:
virtual void OnBindingRequest(StunMessage* msg,
const rtc::SocketAddress& addr);
void OnAllocateRequest(StunMessage* msg,
const rtc::SocketAddress& addr);
void OnSharedSecretRequest(StunMessage* msg,
const rtc::SocketAddress& addr);
void OnSendRequest(StunMessage* msg,
const rtc::SocketAddress& addr);
// Sends an error response to the given message back to the user.
void SendErrorResponse(
const StunMessage& msg, const rtc::SocketAddress& addr,
int error_code, const char* error_desc);
// Sends the given message to the appropriate destination.
void SendResponse(const StunMessage& msg,
const rtc::SocketAddress& addr);
// A helper method to compose a STUN binding response.
void GetStunBindReqponse(StunMessage* request,
const rtc::SocketAddress& remote_addr,
StunMessage* response) const;
private:
rtc::scoped_ptr<rtc::AsyncUDPSocket> socket_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_STUNSERVER_H_

View File

@ -1,126 +0,0 @@
/*
* libjingle
* Copyright 2004 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 <string>
#include "webrtc/p2p/base/stunserver.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/testclient.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/virtualsocketserver.h"
using namespace cricket;
static const rtc::SocketAddress server_addr("99.99.99.1", 3478);
static const rtc::SocketAddress client_addr("1.2.3.4", 1234);
class StunServerTest : public testing::Test {
public:
StunServerTest()
: pss_(new rtc::PhysicalSocketServer),
ss_(new rtc::VirtualSocketServer(pss_.get())),
worker_(ss_.get()) {
}
virtual void SetUp() {
server_.reset(new StunServer(
rtc::AsyncUDPSocket::Create(ss_.get(), server_addr)));
client_.reset(new rtc::TestClient(
rtc::AsyncUDPSocket::Create(ss_.get(), client_addr)));
worker_.Start();
}
void Send(const StunMessage& msg) {
rtc::ByteBuffer buf;
msg.Write(&buf);
Send(buf.Data(), static_cast<int>(buf.Length()));
}
void Send(const char* buf, int len) {
client_->SendTo(buf, len, server_addr);
}
StunMessage* Receive() {
StunMessage* msg = NULL;
rtc::TestClient::Packet* packet = client_->NextPacket();
if (packet) {
rtc::ByteBuffer buf(packet->buf, packet->size);
msg = new StunMessage();
msg->Read(&buf);
delete packet;
}
return msg;
}
private:
rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
rtc::scoped_ptr<rtc::VirtualSocketServer> ss_;
rtc::Thread worker_;
rtc::scoped_ptr<StunServer> server_;
rtc::scoped_ptr<rtc::TestClient> client_;
};
// Disable for TSan v2, see
// https://code.google.com/p/webrtc/issues/detail?id=2517 for details.
#if !defined(THREAD_SANITIZER)
TEST_F(StunServerTest, TestGood) {
StunMessage req;
std::string transaction_id = "0123456789ab";
req.SetType(STUN_BINDING_REQUEST);
req.SetTransactionID(transaction_id);
Send(req);
StunMessage* msg = Receive();
ASSERT_TRUE(msg != NULL);
EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type());
EXPECT_EQ(req.transaction_id(), msg->transaction_id());
const StunAddressAttribute* mapped_addr =
msg->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
EXPECT_TRUE(mapped_addr != NULL);
EXPECT_EQ(1, mapped_addr->family());
EXPECT_EQ(client_addr.port(), mapped_addr->port());
if (mapped_addr->ipaddr() != client_addr.ipaddr()) {
LOG(LS_WARNING) << "Warning: mapped IP ("
<< mapped_addr->ipaddr()
<< ") != local IP (" << client_addr.ipaddr()
<< ")";
}
delete msg;
}
#endif // if !defined(THREAD_SANITIZER)
TEST_F(StunServerTest, TestBad) {
const char* bad = "this is a completely nonsensical message whose only "
"purpose is to make the parser go 'ack'. it doesn't "
"look anything like a normal stun message";
Send(bad, static_cast<int>(strlen(bad)));
StunMessage* msg = Receive();
ASSERT_TRUE(msg == NULL);
}

View File

@ -1,341 +0,0 @@
/*
* 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 "webrtc/p2p/base/tcpport.h"
#include "webrtc/p2p/base/common.h"
#include "webrtc/base/common.h"
#include "webrtc/base/logging.h"
namespace cricket {
TCPPort::TCPPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port, const std::string& username,
const std::string& password, bool allow_listen)
: Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port,
username, password),
incoming_only_(false),
allow_listen_(allow_listen),
socket_(NULL),
error_(0) {
// TODO(mallinath) - Set preference value as per RFC 6544.
// http://b/issue?id=7141794
}
bool TCPPort::Init() {
if (allow_listen_) {
// Treat failure to create or bind a TCP socket as fatal. This
// should never happen.
socket_ = socket_factory()->CreateServerTcpSocket(
rtc::SocketAddress(ip(), 0), min_port(), max_port(),
false /* ssl */);
if (!socket_) {
LOG_J(LS_ERROR, this) << "TCP socket creation failed.";
return false;
}
socket_->SignalNewConnection.connect(this, &TCPPort::OnNewConnection);
socket_->SignalAddressReady.connect(this, &TCPPort::OnAddressReady);
}
return true;
}
TCPPort::~TCPPort() {
delete socket_;
std::list<Incoming>::iterator it;
for (it = incoming_.begin(); it != incoming_.end(); ++it)
delete it->socket;
incoming_.clear();
}
Connection* TCPPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
// We only support TCP protocols
if ((address.protocol() != TCP_PROTOCOL_NAME) &&
(address.protocol() != SSLTCP_PROTOCOL_NAME)) {
return NULL;
}
if (address.tcptype() == TCPTYPE_ACTIVE_STR ||
(address.tcptype().empty() && address.address().port() == 0)) {
// It's active only candidate, we should not try to create connections
// for these candidates.
return NULL;
}
// We can't accept TCP connections incoming on other ports
if (origin == ORIGIN_OTHER_PORT)
return NULL;
// Check if we are allowed to make outgoing TCP connections
if (incoming_only_ && (origin == ORIGIN_MESSAGE))
return NULL;
// We don't know how to act as an ssl server yet
if ((address.protocol() == SSLTCP_PROTOCOL_NAME) &&
(origin == ORIGIN_THIS_PORT)) {
return NULL;
}
if (!IsCompatibleAddress(address.address())) {
return NULL;
}
TCPConnection* conn = NULL;
if (rtc::AsyncPacketSocket* socket =
GetIncoming(address.address(), true)) {
socket->SignalReadPacket.disconnect(this);
conn = new TCPConnection(this, address, socket);
} else {
conn = new TCPConnection(this, address);
}
AddConnection(conn);
return conn;
}
void TCPPort::PrepareAddress() {
if (socket_) {
// If socket isn't bound yet the address will be added in
// OnAddressReady(). Socket may be in the CLOSED state if Listen()
// failed, we still want to add the socket address.
LOG(LS_VERBOSE) << "Preparing TCP address, current state: "
<< socket_->GetState();
if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND ||
socket_->GetState() == rtc::AsyncPacketSocket::STATE_CLOSED)
AddAddress(socket_->GetLocalAddress(), socket_->GetLocalAddress(),
rtc::SocketAddress(),
TCP_PROTOCOL_NAME, TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE,
ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
} else {
LOG_J(LS_INFO, this) << "Not listening due to firewall restrictions.";
// Note: We still add the address, since otherwise the remote side won't
// recognize our incoming TCP connections.
AddAddress(rtc::SocketAddress(ip(), 0),
rtc::SocketAddress(ip(), 0), rtc::SocketAddress(),
TCP_PROTOCOL_NAME, TCPTYPE_ACTIVE_STR, LOCAL_PORT_TYPE,
ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
}
}
int TCPPort::SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload) {
rtc::AsyncPacketSocket * socket = NULL;
if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) {
socket = conn->socket();
} else {
socket = GetIncoming(addr);
}
if (!socket) {
LOG_J(LS_ERROR, this) << "Attempted to send to an unknown destination, "
<< addr.ToSensitiveString();
return -1; // TODO: Set error_
}
int sent = socket->Send(data, size, options);
if (sent < 0) {
error_ = socket->GetError();
LOG_J(LS_ERROR, this) << "TCP send of " << size
<< " bytes failed with error " << error_;
}
return sent;
}
int TCPPort::GetOption(rtc::Socket::Option opt, int* value) {
if (socket_) {
return socket_->GetOption(opt, value);
} else {
return SOCKET_ERROR;
}
}
int TCPPort::SetOption(rtc::Socket::Option opt, int value) {
if (socket_) {
return socket_->SetOption(opt, value);
} else {
return SOCKET_ERROR;
}
}
int TCPPort::GetError() {
return error_;
}
void TCPPort::OnNewConnection(rtc::AsyncPacketSocket* socket,
rtc::AsyncPacketSocket* new_socket) {
ASSERT(socket == socket_);
Incoming incoming;
incoming.addr = new_socket->GetRemoteAddress();
incoming.socket = new_socket;
incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket);
incoming.socket->SignalReadyToSend.connect(this, &TCPPort::OnReadyToSend);
LOG_J(LS_VERBOSE, this) << "Accepted connection from "
<< incoming.addr.ToSensitiveString();
incoming_.push_back(incoming);
}
rtc::AsyncPacketSocket* TCPPort::GetIncoming(
const rtc::SocketAddress& addr, bool remove) {
rtc::AsyncPacketSocket* socket = NULL;
for (std::list<Incoming>::iterator it = incoming_.begin();
it != incoming_.end(); ++it) {
if (it->addr == addr) {
socket = it->socket;
if (remove)
incoming_.erase(it);
break;
}
}
return socket;
}
void TCPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
Port::OnReadPacket(data, size, remote_addr, PROTO_TCP);
}
void TCPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
Port::OnReadyToSend();
}
void TCPPort::OnAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address) {
AddAddress(address, address, rtc::SocketAddress(),
TCP_PROTOCOL_NAME, TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE,
ICE_TYPE_PREFERENCE_HOST_TCP, 0, true);
}
TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate,
rtc::AsyncPacketSocket* socket)
: Connection(port, 0, candidate), socket_(socket), error_(0) {
bool outgoing = (socket_ == NULL);
if (outgoing) {
// TODO: Handle failures here (unlikely since TCP).
int opts = (candidate.protocol() == SSLTCP_PROTOCOL_NAME) ?
rtc::PacketSocketFactory::OPT_SSLTCP : 0;
socket_ = port->socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(port->ip(), 0),
candidate.address(), port->proxy(), port->user_agent(), opts);
if (socket_) {
LOG_J(LS_VERBOSE, this) << "Connecting from "
<< socket_->GetLocalAddress().ToSensitiveString()
<< " to "
<< candidate.address().ToSensitiveString();
set_connected(false);
socket_->SignalConnect.connect(this, &TCPConnection::OnConnect);
} else {
LOG_J(LS_WARNING, this) << "Failed to create connection to "
<< candidate.address().ToSensitiveString();
}
} else {
// Incoming connections should match the network address.
ASSERT(socket_->GetLocalAddress().ipaddr() == port->ip());
}
if (socket_) {
socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket);
socket_->SignalReadyToSend.connect(this, &TCPConnection::OnReadyToSend);
socket_->SignalClose.connect(this, &TCPConnection::OnClose);
}
}
TCPConnection::~TCPConnection() {
delete socket_;
}
int TCPConnection::Send(const void* data, size_t size,
const rtc::PacketOptions& options) {
if (!socket_) {
error_ = ENOTCONN;
return SOCKET_ERROR;
}
if (write_state() != STATE_WRITABLE) {
// TODO: Should STATE_WRITE_TIMEOUT return a non-blocking error?
error_ = EWOULDBLOCK;
return SOCKET_ERROR;
}
int sent = socket_->Send(data, size, options);
if (sent < 0) {
error_ = socket_->GetError();
} else {
send_rate_tracker_.Update(sent);
}
return sent;
}
int TCPConnection::GetError() {
return error_;
}
void TCPConnection::OnConnect(rtc::AsyncPacketSocket* socket) {
ASSERT(socket == socket_);
// Do not use this connection if the socket bound to a different address than
// the one we asked for. This is seen in Chrome, where TCP sockets cannot be
// given a binding address, and the platform is expected to pick the
// correct local address.
const rtc::IPAddress& socket_ip = socket->GetLocalAddress().ipaddr();
if (socket_ip == port()->ip()) {
LOG_J(LS_VERBOSE, this) << "Connection established to "
<< socket->GetRemoteAddress().ToSensitiveString();
set_connected(true);
} else {
LOG_J(LS_WARNING, this) << "Dropping connection as TCP socket bound to IP "
<< socket_ip.ToSensitiveString()
<< ", different from the local candidate IP "
<< port()->ip().ToSensitiveString();
socket_->Close();
}
}
void TCPConnection::OnClose(rtc::AsyncPacketSocket* socket, int error) {
ASSERT(socket == socket_);
LOG_J(LS_INFO, this) << "Connection closed with error " << error;
set_connected(false);
set_write_state(STATE_WRITE_TIMEOUT);
}
void TCPConnection::OnReadPacket(
rtc::AsyncPacketSocket* socket, const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
ASSERT(socket == socket_);
Connection::OnReadPacket(data, size, packet_time);
}
void TCPConnection::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
ASSERT(socket == socket_);
Connection::OnReadyToSend();
}
} // namespace cricket

View File

@ -1,153 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_TCPPORT_H_
#define WEBRTC_P2P_BASE_TCPPORT_H_
#include <list>
#include <string>
#include "webrtc/p2p/base/port.h"
#include "webrtc/base/asyncpacketsocket.h"
namespace cricket {
class TCPConnection;
// Communicates using a local TCP port.
//
// This class is designed to allow subclasses to take advantage of the
// connection management provided by this class. A subclass should take of all
// packet sending and preparation, but when a packet is received, it should
// call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection.
class TCPPort : public Port {
public:
static TCPPort* Create(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username,
const std::string& password,
bool allow_listen) {
TCPPort* port = new TCPPort(thread, factory, network,
ip, min_port, max_port,
username, password, allow_listen);
if (!port->Init()) {
delete port;
port = NULL;
}
return port;
}
virtual ~TCPPort();
virtual Connection* CreateConnection(const Candidate& address,
CandidateOrigin origin);
virtual void PrepareAddress();
virtual int GetOption(rtc::Socket::Option opt, int* value);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetError();
protected:
TCPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port, const std::string& username,
const std::string& password, bool allow_listen);
bool Init();
// Handles sending using the local TCP socket.
virtual int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload);
// Accepts incoming TCP connection.
void OnNewConnection(rtc::AsyncPacketSocket* socket,
rtc::AsyncPacketSocket* new_socket);
private:
struct Incoming {
rtc::SocketAddress addr;
rtc::AsyncPacketSocket* socket;
};
rtc::AsyncPacketSocket* GetIncoming(
const rtc::SocketAddress& addr, bool remove = false);
// Receives packet signal from the local TCP Socket.
void OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
void OnAddressReady(rtc::AsyncPacketSocket* socket,
const rtc::SocketAddress& address);
// TODO: Is this still needed?
bool incoming_only_;
bool allow_listen_;
rtc::AsyncPacketSocket* socket_;
int error_;
std::list<Incoming> incoming_;
friend class TCPConnection;
};
class TCPConnection : public Connection {
public:
// Connection is outgoing unless socket is specified
TCPConnection(TCPPort* port, const Candidate& candidate,
rtc::AsyncPacketSocket* socket = 0);
virtual ~TCPConnection();
virtual int Send(const void* data, size_t size,
const rtc::PacketOptions& options);
virtual int GetError();
rtc::AsyncPacketSocket* socket() { return socket_; }
private:
void OnConnect(rtc::AsyncPacketSocket* socket);
void OnClose(rtc::AsyncPacketSocket* socket, int error);
void OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
rtc::AsyncPacketSocket* socket_;
int error_;
friend class TCPPort;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TCPPORT_H_

View File

@ -1,118 +0,0 @@
/*
* libjingle
* Copyright 2008 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.
*/
#ifndef WEBRTC_P2P_BASE_TESTRELAYSERVER_H_
#define WEBRTC_P2P_BASE_TESTRELAYSERVER_H_
#include "webrtc/p2p/base/relayserver.h"
#include "webrtc/base/asynctcpsocket.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socketadapters.h"
#include "webrtc/base/thread.h"
namespace cricket {
// A test relay server. Useful for unit tests.
class TestRelayServer : public sigslot::has_slots<> {
public:
TestRelayServer(rtc::Thread* thread,
const rtc::SocketAddress& udp_int_addr,
const rtc::SocketAddress& udp_ext_addr,
const rtc::SocketAddress& tcp_int_addr,
const rtc::SocketAddress& tcp_ext_addr,
const rtc::SocketAddress& ssl_int_addr,
const rtc::SocketAddress& ssl_ext_addr)
: server_(thread) {
server_.AddInternalSocket(rtc::AsyncUDPSocket::Create(
thread->socketserver(), udp_int_addr));
server_.AddExternalSocket(rtc::AsyncUDPSocket::Create(
thread->socketserver(), udp_ext_addr));
tcp_int_socket_.reset(CreateListenSocket(thread, tcp_int_addr));
tcp_ext_socket_.reset(CreateListenSocket(thread, tcp_ext_addr));
ssl_int_socket_.reset(CreateListenSocket(thread, ssl_int_addr));
ssl_ext_socket_.reset(CreateListenSocket(thread, ssl_ext_addr));
}
int GetConnectionCount() const {
return server_.GetConnectionCount();
}
rtc::SocketAddressPair GetConnection(int connection) const {
return server_.GetConnection(connection);
}
bool HasConnection(const rtc::SocketAddress& address) const {
return server_.HasConnection(address);
}
private:
rtc::AsyncSocket* CreateListenSocket(rtc::Thread* thread,
const rtc::SocketAddress& addr) {
rtc::AsyncSocket* socket =
thread->socketserver()->CreateAsyncSocket(addr.family(), SOCK_STREAM);
socket->Bind(addr);
socket->Listen(5);
socket->SignalReadEvent.connect(this, &TestRelayServer::OnAccept);
return socket;
}
void OnAccept(rtc::AsyncSocket* socket) {
bool external = (socket == tcp_ext_socket_.get() ||
socket == ssl_ext_socket_.get());
bool ssl = (socket == ssl_int_socket_.get() ||
socket == ssl_ext_socket_.get());
rtc::AsyncSocket* raw_socket = socket->Accept(NULL);
if (raw_socket) {
rtc::AsyncTCPSocket* packet_socket = new rtc::AsyncTCPSocket(
(!ssl) ? raw_socket :
new rtc::AsyncSSLServerSocket(raw_socket), false);
if (!external) {
packet_socket->SignalClose.connect(this,
&TestRelayServer::OnInternalClose);
server_.AddInternalSocket(packet_socket);
} else {
packet_socket->SignalClose.connect(this,
&TestRelayServer::OnExternalClose);
server_.AddExternalSocket(packet_socket);
}
}
}
void OnInternalClose(rtc::AsyncPacketSocket* socket, int error) {
server_.RemoveInternalSocket(socket);
}
void OnExternalClose(rtc::AsyncPacketSocket* socket, int error) {
server_.RemoveExternalSocket(socket);
}
private:
cricket::RelayServer server_;
rtc::scoped_ptr<rtc::AsyncSocket> tcp_int_socket_;
rtc::scoped_ptr<rtc::AsyncSocket> tcp_ext_socket_;
rtc::scoped_ptr<rtc::AsyncSocket> ssl_int_socket_;
rtc::scoped_ptr<rtc::AsyncSocket> ssl_ext_socket_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TESTRELAYSERVER_H_

View File

@ -1,75 +0,0 @@
/*
* libjingle
* Copyright 2008 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.
*/
#ifndef WEBRTC_P2P_BASE_TESTSTUNSERVER_H_
#define WEBRTC_P2P_BASE_TESTSTUNSERVER_H_
#include "webrtc/p2p/base/stunserver.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/thread.h"
namespace cricket {
// A test STUN server. Useful for unit tests.
class TestStunServer : StunServer {
public:
static TestStunServer* Create(rtc::Thread* thread,
const rtc::SocketAddress& addr) {
rtc::AsyncSocket* socket =
thread->socketserver()->CreateAsyncSocket(addr.family(), SOCK_DGRAM);
rtc::AsyncUDPSocket* udp_socket =
rtc::AsyncUDPSocket::Create(socket, addr);
return new TestStunServer(udp_socket);
}
// Set a fake STUN address to return to the client.
void set_fake_stun_addr(const rtc::SocketAddress& addr) {
fake_stun_addr_ = addr;
}
private:
explicit TestStunServer(rtc::AsyncUDPSocket* socket) : StunServer(socket) {}
void OnBindingRequest(StunMessage* msg,
const rtc::SocketAddress& remote_addr) OVERRIDE {
if (fake_stun_addr_.IsNil()) {
StunServer::OnBindingRequest(msg, remote_addr);
} else {
StunMessage response;
GetStunBindReqponse(msg, fake_stun_addr_, &response);
SendResponse(response, remote_addr);
}
}
private:
rtc::SocketAddress fake_stun_addr_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TESTSTUNSERVER_H_

View File

@ -1,120 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#ifndef WEBRTC_P2P_BASE_TESTTURNSERVER_H_
#define WEBRTC_P2P_BASE_TESTTURNSERVER_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/stun.h"
#include "webrtc/p2p/base/turnserver.h"
#include "webrtc/base/asyncudpsocket.h"
#include "webrtc/base/thread.h"
namespace cricket {
static const char kTestRealm[] = "example.org";
static const char kTestSoftware[] = "TestTurnServer";
class TestTurnRedirector : public TurnRedirectInterface {
public:
explicit TestTurnRedirector(const std::vector<rtc::SocketAddress>& addresses)
: alternate_server_addresses_(addresses),
iter_(alternate_server_addresses_.begin()) {
}
virtual bool ShouldRedirect(const rtc::SocketAddress&,
rtc::SocketAddress* out) {
if (!out || iter_ == alternate_server_addresses_.end()) {
return false;
}
*out = *iter_++;
return true;
}
private:
const std::vector<rtc::SocketAddress>& alternate_server_addresses_;
std::vector<rtc::SocketAddress>::const_iterator iter_;
};
class TestTurnServer : public TurnAuthInterface {
public:
TestTurnServer(rtc::Thread* thread,
const rtc::SocketAddress& udp_int_addr,
const rtc::SocketAddress& udp_ext_addr)
: server_(thread) {
AddInternalSocket(udp_int_addr, cricket::PROTO_UDP);
server_.SetExternalSocketFactory(new rtc::BasicPacketSocketFactory(),
udp_ext_addr);
server_.set_realm(kTestRealm);
server_.set_software(kTestSoftware);
server_.set_auth_hook(this);
}
void set_enable_otu_nonce(bool enable) {
server_.set_enable_otu_nonce(enable);
}
TurnServer* server() { return &server_; }
void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
server_.set_redirect_hook(redirect_hook);
}
void AddInternalSocket(const rtc::SocketAddress& int_addr,
ProtocolType proto) {
rtc::Thread* thread = rtc::Thread::Current();
if (proto == cricket::PROTO_UDP) {
server_.AddInternalSocket(rtc::AsyncUDPSocket::Create(
thread->socketserver(), int_addr), proto);
} else if (proto == cricket::PROTO_TCP) {
// For TCP we need to create a server socket which can listen for incoming
// new connections.
rtc::AsyncSocket* socket =
thread->socketserver()->CreateAsyncSocket(SOCK_STREAM);
socket->Bind(int_addr);
socket->Listen(5);
server_.AddInternalServerSocket(socket, proto);
}
}
private:
// For this test server, succeed if the password is the same as the username.
// Obviously, do not use this in a production environment.
virtual bool GetKey(const std::string& username, const std::string& realm,
std::string* key) {
return ComputeStunCredentialHash(username, realm, username, key);
}
TurnServer server_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TESTTURNSERVER_H_

View File

@ -1,977 +0,0 @@
/*
* libjingle
* Copyright 2004, 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 "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/parsing.h"
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/sessionmanager.h"
#include "webrtc/p2p/base/transportchannelimpl.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/common.h"
#include "webrtc/base/logging.h"
namespace cricket {
using rtc::Bind;
enum {
MSG_ONSIGNALINGREADY = 1,
MSG_ONREMOTECANDIDATE,
MSG_READSTATE,
MSG_WRITESTATE,
MSG_REQUESTSIGNALING,
MSG_CANDIDATEREADY,
MSG_ROUTECHANGE,
MSG_CONNECTING,
MSG_CANDIDATEALLOCATIONCOMPLETE,
MSG_ROLECONFLICT,
MSG_COMPLETED,
MSG_FAILED,
};
struct ChannelParams : public rtc::MessageData {
ChannelParams() : channel(NULL), candidate(NULL) {}
explicit ChannelParams(int component)
: component(component), channel(NULL), candidate(NULL) {}
explicit ChannelParams(Candidate* candidate)
: channel(NULL), candidate(candidate) {
}
~ChannelParams() {
delete candidate;
}
std::string name;
int component;
TransportChannelImpl* channel;
Candidate* candidate;
};
static std::string IceProtoToString(TransportProtocol proto) {
std::string proto_str;
switch (proto) {
case ICEPROTO_GOOGLE:
proto_str = "gice";
break;
case ICEPROTO_HYBRID:
proto_str = "hybrid";
break;
case ICEPROTO_RFC5245:
proto_str = "ice";
break;
default:
ASSERT(false);
break;
}
return proto_str;
}
static bool VerifyIceParams(const TransportDescription& desc) {
// For legacy protocols.
if (desc.ice_ufrag.empty() && desc.ice_pwd.empty())
return true;
if (desc.ice_ufrag.length() < ICE_UFRAG_MIN_LENGTH ||
desc.ice_ufrag.length() > ICE_UFRAG_MAX_LENGTH) {
return false;
}
if (desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH ||
desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) {
return false;
}
return true;
}
bool BadTransportDescription(const std::string& desc, std::string* err_desc) {
if (err_desc) {
*err_desc = desc;
}
LOG(LS_ERROR) << desc;
return false;
}
bool IceCredentialsChanged(const std::string& old_ufrag,
const std::string& old_pwd,
const std::string& new_ufrag,
const std::string& new_pwd) {
// TODO(jiayl): The standard (RFC 5245 Section 9.1.1.1) says that ICE should
// restart when both the ufrag and password are changed, but we do restart
// when either ufrag or passwrod is changed to keep compatible with GICE. We
// should clean this up when GICE is no longer used.
return (old_ufrag != new_ufrag) || (old_pwd != new_pwd);
}
static bool IceCredentialsChanged(const TransportDescription& old_desc,
const TransportDescription& new_desc) {
return IceCredentialsChanged(old_desc.ice_ufrag, old_desc.ice_pwd,
new_desc.ice_ufrag, new_desc.ice_pwd);
}
Transport::Transport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
const std::string& type,
PortAllocator* allocator)
: signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
content_name_(content_name),
type_(type),
allocator_(allocator),
destroyed_(false),
readable_(TRANSPORT_STATE_NONE),
writable_(TRANSPORT_STATE_NONE),
was_writable_(false),
connect_requested_(false),
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
protocol_(ICEPROTO_HYBRID),
remote_ice_mode_(ICEMODE_FULL) {
}
Transport::~Transport() {
ASSERT(signaling_thread_->IsCurrent());
ASSERT(destroyed_);
}
void Transport::SetIceRole(IceRole role) {
worker_thread_->Invoke<void>(Bind(&Transport::SetIceRole_w, this, role));
}
void Transport::SetIdentity(rtc::SSLIdentity* identity) {
worker_thread_->Invoke<void>(Bind(&Transport::SetIdentity_w, this, identity));
}
bool Transport::GetIdentity(rtc::SSLIdentity** identity) {
// The identity is set on the worker thread, so for safety it must also be
// acquired on the worker thread.
return worker_thread_->Invoke<bool>(
Bind(&Transport::GetIdentity_w, this, identity));
}
bool Transport::GetRemoteCertificate(rtc::SSLCertificate** cert) {
// Channels can be deleted on the worker thread, so for safety the remote
// certificate is acquired on the worker thread.
return worker_thread_->Invoke<bool>(
Bind(&Transport::GetRemoteCertificate_w, this, cert));
}
bool Transport::GetRemoteCertificate_w(rtc::SSLCertificate** cert) {
ASSERT(worker_thread()->IsCurrent());
if (channels_.empty())
return false;
ChannelMap::iterator iter = channels_.begin();
return iter->second->GetRemoteCertificate(cert);
}
bool Transport::SetLocalTransportDescription(
const TransportDescription& description,
ContentAction action,
std::string* error_desc) {
return worker_thread_->Invoke<bool>(Bind(
&Transport::SetLocalTransportDescription_w, this,
description, action, error_desc));
}
bool Transport::SetRemoteTransportDescription(
const TransportDescription& description,
ContentAction action,
std::string* error_desc) {
return worker_thread_->Invoke<bool>(Bind(
&Transport::SetRemoteTransportDescription_w, this,
description, action, error_desc));
}
TransportChannelImpl* Transport::CreateChannel(int component) {
return worker_thread_->Invoke<TransportChannelImpl*>(Bind(
&Transport::CreateChannel_w, this, component));
}
TransportChannelImpl* Transport::CreateChannel_w(int component) {
ASSERT(worker_thread()->IsCurrent());
TransportChannelImpl *impl;
rtc::CritScope cs(&crit_);
// Create the entry if it does not exist.
bool impl_exists = false;
if (channels_.find(component) == channels_.end()) {
impl = CreateTransportChannel(component);
channels_[component] = ChannelMapEntry(impl);
} else {
impl = channels_[component].get();
impl_exists = true;
}
// Increase the ref count.
channels_[component].AddRef();
destroyed_ = false;
if (impl_exists) {
// If this is an existing channel, we should just return it without
// connecting to all the signal again.
return impl;
}
// Push down our transport state to the new channel.
impl->SetIceRole(ice_role_);
impl->SetIceTiebreaker(tiebreaker_);
// TODO(ronghuawu): Change CreateChannel_w to be able to return error since
// below Apply**Description_w calls can fail.
if (local_description_)
ApplyLocalTransportDescription_w(impl, NULL);
if (remote_description_)
ApplyRemoteTransportDescription_w(impl, NULL);
if (local_description_ && remote_description_)
ApplyNegotiatedTransportDescription_w(impl, NULL);
impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState);
impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
impl->SignalRequestSignaling.connect(
this, &Transport::OnChannelRequestSignaling);
impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady);
impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange);
impl->SignalCandidatesAllocationDone.connect(
this, &Transport::OnChannelCandidatesAllocationDone);
impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict);
impl->SignalConnectionRemoved.connect(
this, &Transport::OnChannelConnectionRemoved);
if (connect_requested_) {
impl->Connect();
if (channels_.size() == 1) {
// If this is the first channel, then indicate that we have started
// connecting.
signaling_thread()->Post(this, MSG_CONNECTING, NULL);
}
}
return impl;
}
TransportChannelImpl* Transport::GetChannel(int component) {
rtc::CritScope cs(&crit_);
ChannelMap::iterator iter = channels_.find(component);
return (iter != channels_.end()) ? iter->second.get() : NULL;
}
bool Transport::HasChannels() {
rtc::CritScope cs(&crit_);
return !channels_.empty();
}
void Transport::DestroyChannel(int component) {
worker_thread_->Invoke<void>(Bind(
&Transport::DestroyChannel_w, this, component));
}
void Transport::DestroyChannel_w(int component) {
ASSERT(worker_thread()->IsCurrent());
TransportChannelImpl* impl = NULL;
{
rtc::CritScope cs(&crit_);
ChannelMap::iterator iter = channels_.find(component);
if (iter == channels_.end())
return;
iter->second.DecRef();
if (!iter->second.ref()) {
impl = iter->second.get();
channels_.erase(iter);
}
}
if (connect_requested_ && channels_.empty()) {
// We're no longer attempting to connect.
signaling_thread()->Post(this, MSG_CONNECTING, NULL);
}
if (impl) {
// Check in case the deleted channel was the only non-writable channel.
OnChannelWritableState(impl);
DestroyTransportChannel(impl);
}
}
void Transport::ConnectChannels() {
ASSERT(signaling_thread()->IsCurrent());
worker_thread_->Invoke<void>(Bind(&Transport::ConnectChannels_w, this));
}
void Transport::ConnectChannels_w() {
ASSERT(worker_thread()->IsCurrent());
if (connect_requested_ || channels_.empty())
return;
connect_requested_ = true;
signaling_thread()->Post(
this, MSG_CANDIDATEREADY, NULL);
if (!local_description_) {
// TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here.
// As Transport must know TD is offer or answer and cricket::Transport
// doesn't have the capability to decide it. This should be set by the
// Session.
// Session must generate local TD before remote candidates pushed when
// initiate request initiated by the remote.
LOG(LS_INFO) << "Transport::ConnectChannels_w: No local description has "
<< "been set. Will generate one.";
TransportDescription desc(NS_GINGLE_P2P, std::vector<std::string>(),
rtc::CreateRandomString(ICE_UFRAG_LENGTH),
rtc::CreateRandomString(ICE_PWD_LENGTH),
ICEMODE_FULL, CONNECTIONROLE_NONE, NULL,
Candidates());
SetLocalTransportDescription_w(desc, CA_OFFER, NULL);
}
CallChannels_w(&TransportChannelImpl::Connect);
if (!channels_.empty()) {
signaling_thread()->Post(this, MSG_CONNECTING, NULL);
}
}
void Transport::OnConnecting_s() {
ASSERT(signaling_thread()->IsCurrent());
SignalConnecting(this);
}
void Transport::DestroyAllChannels() {
ASSERT(signaling_thread()->IsCurrent());
worker_thread_->Invoke<void>(
Bind(&Transport::DestroyAllChannels_w, this));
worker_thread()->Clear(this);
signaling_thread()->Clear(this);
destroyed_ = true;
}
void Transport::DestroyAllChannels_w() {
ASSERT(worker_thread()->IsCurrent());
std::vector<TransportChannelImpl*> impls;
{
rtc::CritScope cs(&crit_);
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
iter->second.DecRef();
if (!iter->second.ref())
impls.push_back(iter->second.get());
}
}
channels_.clear();
for (size_t i = 0; i < impls.size(); ++i)
DestroyTransportChannel(impls[i]);
}
void Transport::ResetChannels() {
ASSERT(signaling_thread()->IsCurrent());
worker_thread_->Invoke<void>(Bind(&Transport::ResetChannels_w, this));
}
void Transport::ResetChannels_w() {
ASSERT(worker_thread()->IsCurrent());
// We are no longer attempting to connect
connect_requested_ = false;
// Clear out the old messages, they aren't relevant
rtc::CritScope cs(&crit_);
ready_candidates_.clear();
// Reset all of the channels
CallChannels_w(&TransportChannelImpl::Reset);
}
void Transport::OnSignalingReady() {
ASSERT(signaling_thread()->IsCurrent());
if (destroyed_) return;
worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL);
// Notify the subclass.
OnTransportSignalingReady();
}
void Transport::CallChannels_w(TransportChannelFunc func) {
ASSERT(worker_thread()->IsCurrent());
rtc::CritScope cs(&crit_);
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
((iter->second.get())->*func)();
}
}
bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) {
// No address zero.
if (cand.address().IsNil() || cand.address().IsAny()) {
*error = "candidate has address of zero";
return false;
}
// Disallow all ports below 1024, except for 80 and 443 on public addresses.
int port = cand.address().port();
if (cand.protocol() == TCP_PROTOCOL_NAME &&
(cand.tcptype() == TCPTYPE_ACTIVE_STR || port == 0)) {
// Expected for active-only candidates per
// http://tools.ietf.org/html/rfc6544#section-4.5 so no error.
// Libjingle clients emit port 0, in "active" mode.
return true;
}
if (port < 1024) {
if ((port != 80) && (port != 443)) {
*error = "candidate has port below 1024, but not 80 or 443";
return false;
}
if (cand.address().IsPrivateIP()) {
*error = "candidate has port of 80 or 443 with private IP address";
return false;
}
}
return true;
}
bool Transport::GetStats(TransportStats* stats) {
ASSERT(signaling_thread()->IsCurrent());
return worker_thread_->Invoke<bool>(Bind(
&Transport::GetStats_w, this, stats));
}
bool Transport::GetStats_w(TransportStats* stats) {
ASSERT(worker_thread()->IsCurrent());
stats->content_name = content_name();
stats->channel_stats.clear();
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
TransportChannelStats substats;
substats.component = iter->second->component();
if (!iter->second->GetStats(&substats.connection_infos)) {
return false;
}
stats->channel_stats.push_back(substats);
}
return true;
}
bool Transport::GetSslRole(rtc::SSLRole* ssl_role) const {
return worker_thread_->Invoke<bool>(Bind(
&Transport::GetSslRole_w, this, ssl_role));
}
void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) {
for (std::vector<Candidate>::const_iterator iter = candidates.begin();
iter != candidates.end();
++iter) {
OnRemoteCandidate(*iter);
}
}
void Transport::OnRemoteCandidate(const Candidate& candidate) {
ASSERT(signaling_thread()->IsCurrent());
if (destroyed_) return;
if (!HasChannel(candidate.component())) {
LOG(LS_WARNING) << "Ignoring candidate for unknown component "
<< candidate.component();
return;
}
ChannelParams* params = new ChannelParams(new Candidate(candidate));
worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, params);
}
void Transport::OnRemoteCandidate_w(const Candidate& candidate) {
ASSERT(worker_thread()->IsCurrent());
ChannelMap::iterator iter = channels_.find(candidate.component());
// It's ok for a channel to go away while this message is in transit.
if (iter != channels_.end()) {
iter->second->OnCandidate(candidate);
}
}
void Transport::OnChannelReadableState(TransportChannel* channel) {
ASSERT(worker_thread()->IsCurrent());
signaling_thread()->Post(this, MSG_READSTATE, NULL);
}
void Transport::OnChannelReadableState_s() {
ASSERT(signaling_thread()->IsCurrent());
TransportState readable = GetTransportState_s(true);
if (readable_ != readable) {
readable_ = readable;
SignalReadableState(this);
}
}
void Transport::OnChannelWritableState(TransportChannel* channel) {
ASSERT(worker_thread()->IsCurrent());
signaling_thread()->Post(this, MSG_WRITESTATE, NULL);
MaybeCompleted_w();
}
void Transport::OnChannelWritableState_s() {
ASSERT(signaling_thread()->IsCurrent());
TransportState writable = GetTransportState_s(false);
if (writable_ != writable) {
was_writable_ = (writable_ == TRANSPORT_STATE_ALL);
writable_ = writable;
SignalWritableState(this);
}
}
TransportState Transport::GetTransportState_s(bool read) {
ASSERT(signaling_thread()->IsCurrent());
rtc::CritScope cs(&crit_);
bool any = false;
bool all = !channels_.empty();
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
bool b = (read ? iter->second->readable() :
iter->second->writable());
any = any || b;
all = all && b;
}
if (all) {
return TRANSPORT_STATE_ALL;
} else if (any) {
return TRANSPORT_STATE_SOME;
} else {
return TRANSPORT_STATE_NONE;
}
}
void Transport::OnChannelRequestSignaling(TransportChannelImpl* channel) {
ASSERT(worker_thread()->IsCurrent());
ChannelParams* params = new ChannelParams(channel->component());
signaling_thread()->Post(this, MSG_REQUESTSIGNALING, params);
}
void Transport::OnChannelRequestSignaling_s(int component) {
ASSERT(signaling_thread()->IsCurrent());
LOG(LS_INFO) << "Transport: " << content_name_ << ", allocating candidates";
// Resetting ICE state for the channel.
{
rtc::CritScope cs(&crit_);
ChannelMap::iterator iter = channels_.find(component);
if (iter != channels_.end())
iter->second.set_candidates_allocated(false);
}
SignalRequestSignaling(this);
}
void Transport::OnChannelCandidateReady(TransportChannelImpl* channel,
const Candidate& candidate) {
ASSERT(worker_thread()->IsCurrent());
rtc::CritScope cs(&crit_);
ready_candidates_.push_back(candidate);
// We hold any messages until the client lets us connect.
if (connect_requested_) {
signaling_thread()->Post(
this, MSG_CANDIDATEREADY, NULL);
}
}
void Transport::OnChannelCandidateReady_s() {
ASSERT(signaling_thread()->IsCurrent());
ASSERT(connect_requested_);
std::vector<Candidate> candidates;
{
rtc::CritScope cs(&crit_);
candidates.swap(ready_candidates_);
}
// we do the deleting of Candidate* here to keep the new above and
// delete below close to each other
if (!candidates.empty()) {
SignalCandidatesReady(this, candidates);
}
}
void Transport::OnChannelRouteChange(TransportChannel* channel,
const Candidate& remote_candidate) {
ASSERT(worker_thread()->IsCurrent());
ChannelParams* params = new ChannelParams(new Candidate(remote_candidate));
params->channel = static_cast<cricket::TransportChannelImpl*>(channel);
signaling_thread()->Post(this, MSG_ROUTECHANGE, params);
}
void Transport::OnChannelRouteChange_s(const TransportChannel* channel,
const Candidate& remote_candidate) {
ASSERT(signaling_thread()->IsCurrent());
SignalRouteChange(this, remote_candidate.component(), remote_candidate);
}
void Transport::OnChannelCandidatesAllocationDone(
TransportChannelImpl* channel) {
ASSERT(worker_thread()->IsCurrent());
rtc::CritScope cs(&crit_);
ChannelMap::iterator iter = channels_.find(channel->component());
ASSERT(iter != channels_.end());
LOG(LS_INFO) << "Transport: " << content_name_ << ", component "
<< channel->component() << " allocation complete";
iter->second.set_candidates_allocated(true);
// If all channels belonging to this Transport got signal, then
// forward this signal to upper layer.
// Can this signal arrive before all transport channels are created?
for (iter = channels_.begin(); iter != channels_.end(); ++iter) {
if (!iter->second.candidates_allocated())
return;
}
signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE);
MaybeCompleted_w();
}
void Transport::OnChannelCandidatesAllocationDone_s() {
ASSERT(signaling_thread()->IsCurrent());
LOG(LS_INFO) << "Transport: " << content_name_ << " allocation complete";
SignalCandidatesAllocationDone(this);
}
void Transport::OnRoleConflict(TransportChannelImpl* channel) {
signaling_thread_->Post(this, MSG_ROLECONFLICT);
}
void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) {
ASSERT(worker_thread()->IsCurrent());
MaybeCompleted_w();
// Check if the state is now Failed.
// Failed is only available in the Controlling ICE role.
if (channel->GetIceRole() != ICEROLE_CONTROLLING) {
return;
}
ChannelMap::iterator iter = channels_.find(channel->component());
ASSERT(iter != channels_.end());
// Failed can only occur after candidate allocation has stopped.
if (!iter->second.candidates_allocated()) {
return;
}
size_t connections = channel->GetConnectionCount();
if (connections == 0) {
// A Transport has failed if any of its channels have no remaining
// connections.
signaling_thread_->Post(this, MSG_FAILED);
}
}
void Transport::MaybeCompleted_w() {
ASSERT(worker_thread()->IsCurrent());
// A Transport's ICE process is completed if all of its channels are writable,
// have finished allocating candidates, and have pruned all but one of their
// connections.
ChannelMap::const_iterator iter;
for (iter = channels_.begin(); iter != channels_.end(); ++iter) {
const TransportChannelImpl* channel = iter->second.get();
if (!(channel->writable() &&
channel->GetConnectionCount() == 1 &&
channel->GetIceRole() == ICEROLE_CONTROLLING &&
iter->second.candidates_allocated())) {
return;
}
}
signaling_thread_->Post(this, MSG_COMPLETED);
}
void Transport::SetIceRole_w(IceRole role) {
rtc::CritScope cs(&crit_);
ice_role_ = role;
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end(); ++iter) {
iter->second->SetIceRole(ice_role_);
}
}
void Transport::SetRemoteIceMode_w(IceMode mode) {
rtc::CritScope cs(&crit_);
remote_ice_mode_ = mode;
// Shouldn't channels be created after this method executed?
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end(); ++iter) {
iter->second->SetRemoteIceMode(remote_ice_mode_);
}
}
bool Transport::SetLocalTransportDescription_w(
const TransportDescription& desc,
ContentAction action,
std::string* error_desc) {
bool ret = true;
rtc::CritScope cs(&crit_);
if (!VerifyIceParams(desc)) {
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
error_desc);
}
if (local_description_ && IceCredentialsChanged(*local_description_, desc)) {
IceRole new_ice_role = (action == CA_OFFER) ? ICEROLE_CONTROLLING
: ICEROLE_CONTROLLED;
// It must be called before ApplyLocalTransportDescription_w, which may
// trigger an ICE restart and depends on the new ICE role.
SetIceRole_w(new_ice_role);
}
local_description_.reset(new TransportDescription(desc));
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end(); ++iter) {
ret &= ApplyLocalTransportDescription_w(iter->second.get(), error_desc);
}
if (!ret)
return false;
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
if (action == CA_PRANSWER || action == CA_ANSWER) {
ret &= NegotiateTransportDescription_w(action, error_desc);
}
return ret;
}
bool Transport::SetRemoteTransportDescription_w(
const TransportDescription& desc,
ContentAction action,
std::string* error_desc) {
bool ret = true;
rtc::CritScope cs(&crit_);
if (!VerifyIceParams(desc)) {
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
error_desc);
}
remote_description_.reset(new TransportDescription(desc));
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end(); ++iter) {
ret &= ApplyRemoteTransportDescription_w(iter->second.get(), error_desc);
}
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
if (action == CA_PRANSWER || action == CA_ANSWER) {
ret = NegotiateTransportDescription_w(CA_OFFER, error_desc);
}
return ret;
}
bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch,
std::string* error_desc) {
// If existing protocol_type is HYBRID, we may have not chosen the final
// protocol type, so update the channel protocol type from the
// local description. Otherwise, skip updating the protocol type.
// We check for HYBRID to avoid accidental changes; in the case of a
// session renegotiation, the new offer will have the google-ice ICE option,
// so we need to make sure we don't switch back from ICE mode to HYBRID
// when this happens.
// There are some other ways we could have solved this, but this is the
// simplest. The ultimate solution will be to get rid of GICE altogether.
IceProtocolType protocol_type;
if (ch->GetIceProtocolType(&protocol_type) &&
protocol_type == ICEPROTO_HYBRID) {
ch->SetIceProtocolType(
TransportProtocolFromDescription(local_description()));
}
ch->SetIceCredentials(local_description_->ice_ufrag,
local_description_->ice_pwd);
return true;
}
bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
std::string* error_desc) {
ch->SetRemoteIceCredentials(remote_description_->ice_ufrag,
remote_description_->ice_pwd);
return true;
}
bool Transport::ApplyNegotiatedTransportDescription_w(
TransportChannelImpl* channel, std::string* error_desc) {
channel->SetIceProtocolType(protocol_);
channel->SetRemoteIceMode(remote_ice_mode_);
return true;
}
bool Transport::NegotiateTransportDescription_w(ContentAction local_role,
std::string* error_desc) {
// TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into
// P2PTransport.
const TransportDescription* offer;
const TransportDescription* answer;
if (local_role == CA_OFFER) {
offer = local_description_.get();
answer = remote_description_.get();
} else {
offer = remote_description_.get();
answer = local_description_.get();
}
TransportProtocol offer_proto = TransportProtocolFromDescription(offer);
TransportProtocol answer_proto = TransportProtocolFromDescription(answer);
// If offered protocol is gice/ice, then we expect to receive matching
// protocol in answer, anything else is treated as an error.
// HYBRID is not an option when offered specific protocol.
// If offered protocol is HYBRID and answered protocol is HYBRID then
// gice is preferred protocol.
// TODO(mallinath) - Answer from local or remote should't have both ice
// and gice support. It should always pick which protocol it wants to use.
// Once WebRTC stops supporting gice (for backward compatibility), HYBRID in
// answer must be treated as error.
if ((offer_proto == ICEPROTO_GOOGLE || offer_proto == ICEPROTO_RFC5245) &&
(offer_proto != answer_proto)) {
std::ostringstream desc;
desc << "Offer and answer protocol mismatch: "
<< IceProtoToString(offer_proto)
<< " vs "
<< IceProtoToString(answer_proto);
return BadTransportDescription(desc.str(), error_desc);
}
protocol_ = answer_proto == ICEPROTO_HYBRID ? ICEPROTO_GOOGLE : answer_proto;
// If transport is in ICEROLE_CONTROLLED and remote end point supports only
// ice_lite, this local end point should take CONTROLLING role.
if (ice_role_ == ICEROLE_CONTROLLED &&
remote_description_->ice_mode == ICEMODE_LITE) {
SetIceRole_w(ICEROLE_CONTROLLING);
}
// Update remote ice_mode to all existing channels.
remote_ice_mode_ = remote_description_->ice_mode;
// Now that we have negotiated everything, push it downward.
// Note that we cache the result so that if we have race conditions
// between future SetRemote/SetLocal invocations and new channel
// creation, we have the negotiation state saved until a new
// negotiation happens.
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
if (!ApplyNegotiatedTransportDescription_w(iter->second.get(), error_desc))
return false;
}
return true;
}
void Transport::OnMessage(rtc::Message* msg) {
switch (msg->message_id) {
case MSG_ONSIGNALINGREADY:
CallChannels_w(&TransportChannelImpl::OnSignalingReady);
break;
case MSG_ONREMOTECANDIDATE: {
ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
OnRemoteCandidate_w(*params->candidate);
delete params;
}
break;
case MSG_CONNECTING:
OnConnecting_s();
break;
case MSG_READSTATE:
OnChannelReadableState_s();
break;
case MSG_WRITESTATE:
OnChannelWritableState_s();
break;
case MSG_REQUESTSIGNALING: {
ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
OnChannelRequestSignaling_s(params->component);
delete params;
}
break;
case MSG_CANDIDATEREADY:
OnChannelCandidateReady_s();
break;
case MSG_ROUTECHANGE: {
ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
OnChannelRouteChange_s(params->channel, *params->candidate);
delete params;
}
break;
case MSG_CANDIDATEALLOCATIONCOMPLETE:
OnChannelCandidatesAllocationDone_s();
break;
case MSG_ROLECONFLICT:
SignalRoleConflict();
break;
case MSG_COMPLETED:
SignalCompleted(this);
break;
case MSG_FAILED:
SignalFailed(this);
break;
}
}
bool TransportParser::ParseAddress(const buzz::XmlElement* elem,
const buzz::QName& address_name,
const buzz::QName& port_name,
rtc::SocketAddress* address,
ParseError* error) {
if (!elem->HasAttr(address_name))
return BadParse("address does not have " + address_name.LocalPart(), error);
if (!elem->HasAttr(port_name))
return BadParse("address does not have " + port_name.LocalPart(), error);
address->SetIP(elem->Attr(address_name));
std::istringstream ist(elem->Attr(port_name));
int port = 0;
ist >> port;
address->SetPort(port);
return true;
}
// We're GICE if the namespace is NS_GOOGLE_P2P, or if NS_JINGLE_ICE_UDP is
// used and the GICE ice-option is set.
TransportProtocol TransportProtocolFromDescription(
const TransportDescription* desc) {
ASSERT(desc != NULL);
if (desc->transport_type == NS_JINGLE_ICE_UDP) {
return (desc->HasOption(ICE_OPTION_GICE)) ?
ICEPROTO_HYBRID : ICEPROTO_RFC5245;
}
return ICEPROTO_GOOGLE;
}
} // namespace cricket

View File

@ -1,530 +0,0 @@
/*
* 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.
*/
// A Transport manages a set of named channels of the same type.
//
// Subclasses choose the appropriate class to instantiate for each channel;
// however, this base class keeps track of the channels by name, watches their
// state changes (in order to update the manager's state), and forwards
// requests to begin connecting or to reset to each of the channels.
//
// On Threading: Transport performs work on both the signaling and worker
// threads. For subclasses, the rule is that all signaling related calls will
// be made on the signaling thread and all channel related calls (including
// signaling for a channel) will be made on the worker thread. When
// information needs to be sent between the two threads, this class should do
// the work (e.g., OnRemoteCandidate).
//
// Note: Subclasses must call DestroyChannels() in their own constructors.
// It is not possible to do so here because the subclass constructor will
// already have run.
#ifndef WEBRTC_P2P_BASE_TRANSPORT_H_
#define WEBRTC_P2P_BASE_TRANSPORT_H_
#include <map>
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/sessiondescription.h"
#include "webrtc/p2p/base/transportinfo.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslstreamadapter.h"
namespace rtc {
class Thread;
}
namespace buzz {
class QName;
class XmlElement;
}
namespace cricket {
struct ParseError;
struct WriteError;
class CandidateTranslator;
class PortAllocator;
class SessionManager;
class Session;
class TransportChannel;
class TransportChannelImpl;
typedef std::vector<buzz::XmlElement*> XmlElements;
typedef std::vector<Candidate> Candidates;
// Used to parse and serialize (write) transport candidates. For
// convenience of old code, Transports will implement TransportParser.
// Parse/Write seems better than Serialize/Deserialize or
// Create/Translate.
class TransportParser {
public:
// The incoming Translator value may be null, in which case
// ParseCandidates should return false if there are candidates to
// parse (indicating a failure to parse). If the Translator is null
// and there are no candidates to parse, then return true,
// indicating a successful parse of 0 candidates.
// Parse or write a transport description, including ICE credentials and
// any DTLS fingerprint. Since only Jingle has transport descriptions, these
// functions are only used when serializing to Jingle.
virtual bool ParseTransportDescription(const buzz::XmlElement* elem,
const CandidateTranslator* translator,
TransportDescription* tdesc,
ParseError* error) = 0;
virtual bool WriteTransportDescription(const TransportDescription& tdesc,
const CandidateTranslator* translator,
buzz::XmlElement** tdesc_elem,
WriteError* error) = 0;
// Parse a single candidate. This must be used when parsing Gingle
// candidates, since there is no enclosing transport description.
virtual bool ParseGingleCandidate(const buzz::XmlElement* elem,
const CandidateTranslator* translator,
Candidate* candidates,
ParseError* error) = 0;
virtual bool WriteGingleCandidate(const Candidate& candidate,
const CandidateTranslator* translator,
buzz::XmlElement** candidate_elem,
WriteError* error) = 0;
// Helper function to parse an element describing an address. This
// retrieves the IP and port from the given element and verifies
// that they look like plausible values.
bool ParseAddress(const buzz::XmlElement* elem,
const buzz::QName& address_name,
const buzz::QName& port_name,
rtc::SocketAddress* address,
ParseError* error);
virtual ~TransportParser() {}
};
// For "writable" and "readable", we need to differentiate between
// none, all, and some.
enum TransportState {
TRANSPORT_STATE_NONE = 0,
TRANSPORT_STATE_SOME,
TRANSPORT_STATE_ALL
};
// Stats that we can return about the connections for a transport channel.
// TODO(hta): Rename to ConnectionStats
struct ConnectionInfo {
ConnectionInfo()
: best_connection(false),
writable(false),
readable(false),
timeout(false),
new_connection(false),
rtt(0),
sent_total_bytes(0),
sent_bytes_second(0),
recv_total_bytes(0),
recv_bytes_second(0),
key(NULL) {}
bool best_connection; // Is this the best connection we have?
bool writable; // Has this connection received a STUN response?
bool readable; // Has this connection received a STUN request?
bool timeout; // Has this connection timed out?
bool new_connection; // Is this a newly created connection?
size_t rtt; // The STUN RTT for this connection.
size_t sent_total_bytes; // Total bytes sent on this connection.
size_t sent_bytes_second; // Bps over the last measurement interval.
size_t recv_total_bytes; // Total bytes received on this connection.
size_t recv_bytes_second; // Bps over the last measurement interval.
Candidate local_candidate; // The local candidate for this connection.
Candidate remote_candidate; // The remote candidate for this connection.
void* key; // A static value that identifies this conn.
};
// Information about all the connections of a channel.
typedef std::vector<ConnectionInfo> ConnectionInfos;
// Information about a specific channel
struct TransportChannelStats {
int component;
ConnectionInfos connection_infos;
};
// Information about all the channels of a transport.
// TODO(hta): Consider if a simple vector is as good as a map.
typedef std::vector<TransportChannelStats> TransportChannelStatsList;
// Information about the stats of a transport.
struct TransportStats {
std::string content_name;
TransportChannelStatsList channel_stats;
};
bool BadTransportDescription(const std::string& desc, std::string* err_desc);
bool IceCredentialsChanged(const std::string& old_ufrag,
const std::string& old_pwd,
const std::string& new_ufrag,
const std::string& new_pwd);
class Transport : public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
Transport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
const std::string& content_name,
const std::string& type,
PortAllocator* allocator);
virtual ~Transport();
// Returns the signaling thread. The app talks to Transport on this thread.
rtc::Thread* signaling_thread() { return signaling_thread_; }
// Returns the worker thread. The actual networking is done on this thread.
rtc::Thread* worker_thread() { return worker_thread_; }
// Returns the content_name of this transport.
const std::string& content_name() const { return content_name_; }
// Returns the type of this transport.
const std::string& type() const { return type_; }
// Returns the port allocator object for this transport.
PortAllocator* port_allocator() { return allocator_; }
// Returns the readable and states of this manager. These bits are the ORs
// of the corresponding bits on the managed channels. Each time one of these
// states changes, a signal is raised.
// TODO: Replace uses of readable() and writable() with
// any_channels_readable() and any_channels_writable().
bool readable() const { return any_channels_readable(); }
bool writable() const { return any_channels_writable(); }
bool was_writable() const { return was_writable_; }
bool any_channels_readable() const {
return (readable_ == TRANSPORT_STATE_SOME ||
readable_ == TRANSPORT_STATE_ALL);
}
bool any_channels_writable() const {
return (writable_ == TRANSPORT_STATE_SOME ||
writable_ == TRANSPORT_STATE_ALL);
}
bool all_channels_readable() const {
return (readable_ == TRANSPORT_STATE_ALL);
}
bool all_channels_writable() const {
return (writable_ == TRANSPORT_STATE_ALL);
}
sigslot::signal1<Transport*> SignalReadableState;
sigslot::signal1<Transport*> SignalWritableState;
sigslot::signal1<Transport*> SignalCompleted;
sigslot::signal1<Transport*> SignalFailed;
// Returns whether the client has requested the channels to connect.
bool connect_requested() const { return connect_requested_; }
void SetIceRole(IceRole role);
IceRole ice_role() const { return ice_role_; }
void SetIceTiebreaker(uint64 IceTiebreaker) { tiebreaker_ = IceTiebreaker; }
uint64 IceTiebreaker() { return tiebreaker_; }
// Must be called before applying local session description.
void SetIdentity(rtc::SSLIdentity* identity);
// Get a copy of the local identity provided by SetIdentity.
bool GetIdentity(rtc::SSLIdentity** identity);
// Get a copy of the remote certificate in use by the specified channel.
bool GetRemoteCertificate(rtc::SSLCertificate** cert);
TransportProtocol protocol() const { return protocol_; }
// Create, destroy, and lookup the channels of this type by their components.
TransportChannelImpl* CreateChannel(int component);
// Note: GetChannel may lead to race conditions, since the mutex is not held
// after the pointer is returned.
TransportChannelImpl* GetChannel(int component);
// Note: HasChannel does not lead to race conditions, unlike GetChannel.
bool HasChannel(int component) {
return (NULL != GetChannel(component));
}
bool HasChannels();
void DestroyChannel(int component);
// Set the local TransportDescription to be used by TransportChannels.
// This should be called before ConnectChannels().
bool SetLocalTransportDescription(const TransportDescription& description,
ContentAction action,
std::string* error_desc);
// Set the remote TransportDescription to be used by TransportChannels.
bool SetRemoteTransportDescription(const TransportDescription& description,
ContentAction action,
std::string* error_desc);
// Tells all current and future channels to start connecting. When the first
// channel begins connecting, the following signal is raised.
void ConnectChannels();
sigslot::signal1<Transport*> SignalConnecting;
// Resets all of the channels back to their initial state. They are no
// longer connecting.
void ResetChannels();
// Destroys every channel created so far.
void DestroyAllChannels();
bool GetStats(TransportStats* stats);
// Before any stanza is sent, the manager will request signaling. Once
// signaling is available, the client should call OnSignalingReady. Once
// this occurs, the transport (or its channels) can send any waiting stanzas.
// OnSignalingReady invokes OnTransportSignalingReady and then forwards this
// signal to each channel.
sigslot::signal1<Transport*> SignalRequestSignaling;
void OnSignalingReady();
// Handles sending of ready candidates and receiving of remote candidates.
sigslot::signal2<Transport*,
const std::vector<Candidate>&> SignalCandidatesReady;
sigslot::signal1<Transport*> SignalCandidatesAllocationDone;
void OnRemoteCandidates(const std::vector<Candidate>& candidates);
// If candidate is not acceptable, returns false and sets error.
// Call this before calling OnRemoteCandidates.
virtual bool VerifyCandidate(const Candidate& candidate,
std::string* error);
// Signals when the best connection for a channel changes.
sigslot::signal3<Transport*,
int, // component
const Candidate&> SignalRouteChange;
// A transport message has generated an transport-specific error. The
// stanza that caused the error is available in session_msg. If false is
// returned, the error is considered unrecoverable, and the session is
// terminated.
// TODO(juberti): Remove these obsolete functions once Session no longer
// references them.
virtual void OnTransportError(const buzz::XmlElement* error) {}
sigslot::signal6<Transport*, const buzz::XmlElement*, const buzz::QName&,
const std::string&, const std::string&,
const buzz::XmlElement*>
SignalTransportError;
// Forwards the signal from TransportChannel to BaseSession.
sigslot::signal0<> SignalRoleConflict;
virtual bool GetSslRole(rtc::SSLRole* ssl_role) const;
protected:
// These are called by Create/DestroyChannel above in order to create or
// destroy the appropriate type of channel.
virtual TransportChannelImpl* CreateTransportChannel(int component) = 0;
virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0;
// Informs the subclass that we received the signaling ready message.
virtual void OnTransportSignalingReady() {}
// The current local transport description, for use by derived classes
// when performing transport description negotiation.
const TransportDescription* local_description() const {
return local_description_.get();
}
// The current remote transport description, for use by derived classes
// when performing transport description negotiation.
const TransportDescription* remote_description() const {
return remote_description_.get();
}
virtual void SetIdentity_w(rtc::SSLIdentity* identity) {}
virtual bool GetIdentity_w(rtc::SSLIdentity** identity) {
return false;
}
// Pushes down the transport parameters from the local description, such
// as the ICE ufrag and pwd.
// Derived classes can override, but must call the base as well.
virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
std::string* error_desc);
// Pushes down remote ice credentials from the remote description to the
// transport channel.
virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
std::string* error_desc);
// Negotiates the transport parameters based on the current local and remote
// transport description, such at the version of ICE to use, and whether DTLS
// should be activated.
// Derived classes can negotiate their specific parameters here, but must call
// the base as well.
virtual bool NegotiateTransportDescription_w(ContentAction local_role,
std::string* error_desc);
// Pushes down the transport parameters obtained via negotiation.
// Derived classes can set their specific parameters here, but must call the
// base as well.
virtual bool ApplyNegotiatedTransportDescription_w(
TransportChannelImpl* channel, std::string* error_desc);
virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const {
return false;
}
private:
struct ChannelMapEntry {
ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {}
explicit ChannelMapEntry(TransportChannelImpl *impl)
: impl_(impl),
candidates_allocated_(false),
ref_(0) {
}
void AddRef() { ++ref_; }
void DecRef() {
ASSERT(ref_ > 0);
--ref_;
}
int ref() const { return ref_; }
TransportChannelImpl* get() const { return impl_; }
TransportChannelImpl* operator->() const { return impl_; }
void set_candidates_allocated(bool status) {
candidates_allocated_ = status;
}
bool candidates_allocated() const { return candidates_allocated_; }
private:
TransportChannelImpl *impl_;
bool candidates_allocated_;
int ref_;
};
// Candidate component => ChannelMapEntry
typedef std::map<int, ChannelMapEntry> ChannelMap;
// Called when the state of a channel changes.
void OnChannelReadableState(TransportChannel* channel);
void OnChannelWritableState(TransportChannel* channel);
// Called when a channel requests signaling.
void OnChannelRequestSignaling(TransportChannelImpl* channel);
// Called when a candidate is ready from remote peer.
void OnRemoteCandidate(const Candidate& candidate);
// Called when a candidate is ready from channel.
void OnChannelCandidateReady(TransportChannelImpl* channel,
const Candidate& candidate);
void OnChannelRouteChange(TransportChannel* channel,
const Candidate& remote_candidate);
void OnChannelCandidatesAllocationDone(TransportChannelImpl* channel);
// Called when there is ICE role change.
void OnRoleConflict(TransportChannelImpl* channel);
// Called when the channel removes a connection.
void OnChannelConnectionRemoved(TransportChannelImpl* channel);
// Dispatches messages to the appropriate handler (below).
void OnMessage(rtc::Message* msg);
// These are versions of the above methods that are called only on a
// particular thread (s = signaling, w = worker). The above methods post or
// send a message to invoke this version.
TransportChannelImpl* CreateChannel_w(int component);
void DestroyChannel_w(int component);
void ConnectChannels_w();
void ResetChannels_w();
void DestroyAllChannels_w();
void OnRemoteCandidate_w(const Candidate& candidate);
void OnChannelReadableState_s();
void OnChannelWritableState_s();
void OnChannelRequestSignaling_s(int component);
void OnConnecting_s();
void OnChannelRouteChange_s(const TransportChannel* channel,
const Candidate& remote_candidate);
void OnChannelCandidatesAllocationDone_s();
// Helper function that invokes the given function on every channel.
typedef void (TransportChannelImpl::* TransportChannelFunc)();
void CallChannels_w(TransportChannelFunc func);
// Computes the OR of the channel's read or write state (argument picks).
TransportState GetTransportState_s(bool read);
void OnChannelCandidateReady_s();
void SetIceRole_w(IceRole role);
void SetRemoteIceMode_w(IceMode mode);
bool SetLocalTransportDescription_w(const TransportDescription& desc,
ContentAction action,
std::string* error_desc);
bool SetRemoteTransportDescription_w(const TransportDescription& desc,
ContentAction action,
std::string* error_desc);
bool GetStats_w(TransportStats* infos);
bool GetRemoteCertificate_w(rtc::SSLCertificate** cert);
// Sends SignalCompleted if we are now in that state.
void MaybeCompleted_w();
rtc::Thread* signaling_thread_;
rtc::Thread* worker_thread_;
std::string content_name_;
std::string type_;
PortAllocator* allocator_;
bool destroyed_;
TransportState readable_;
TransportState writable_;
bool was_writable_;
bool connect_requested_;
IceRole ice_role_;
uint64 tiebreaker_;
TransportProtocol protocol_;
IceMode remote_ice_mode_;
rtc::scoped_ptr<TransportDescription> local_description_;
rtc::scoped_ptr<TransportDescription> remote_description_;
ChannelMap channels_;
// Buffers the ready_candidates so that SignalCanidatesReady can
// provide them in multiples.
std::vector<Candidate> ready_candidates_;
// Protects changes to channels and messages
rtc::CriticalSection crit_;
DISALLOW_EVIL_CONSTRUCTORS(Transport);
};
// Extract a TransportProtocol from a TransportDescription.
TransportProtocol TransportProtocolFromDescription(
const TransportDescription* desc);
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORT_H_

View File

@ -1,455 +0,0 @@
/*
* libjingle
* Copyright 2011 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 "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/fakesession.h"
#include "webrtc/p2p/base/p2ptransport.h"
#include "webrtc/p2p/base/parsing.h"
#include "webrtc/p2p/base/rawtransport.h"
#include "webrtc/p2p/base/sessionmessages.h"
#include "webrtc/libjingle/xmllite/xmlelement.h"
#include "webrtc/libjingle/xmpp/constants.h"
#include "webrtc/base/fakesslidentity.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/thread.h"
using cricket::Candidate;
using cricket::Candidates;
using cricket::Transport;
using cricket::FakeTransport;
using cricket::TransportChannel;
using cricket::FakeTransportChannel;
using cricket::IceRole;
using cricket::TransportDescription;
using cricket::WriteError;
using cricket::ParseError;
using rtc::SocketAddress;
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const char kIceUfrag2[] = "TESTICEUFRAG0002";
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
class TransportTest : public testing::Test,
public sigslot::has_slots<> {
public:
TransportTest()
: thread_(rtc::Thread::Current()),
transport_(new FakeTransport(
thread_, thread_, "test content name", NULL)),
channel_(NULL),
connecting_signalled_(false),
completed_(false),
failed_(false) {
transport_->SignalConnecting.connect(this, &TransportTest::OnConnecting);
transport_->SignalCompleted.connect(this, &TransportTest::OnCompleted);
transport_->SignalFailed.connect(this, &TransportTest::OnFailed);
}
~TransportTest() {
transport_->DestroyAllChannels();
}
bool SetupChannel() {
channel_ = CreateChannel(1);
return (channel_ != NULL);
}
FakeTransportChannel* CreateChannel(int component) {
return static_cast<FakeTransportChannel*>(
transport_->CreateChannel(component));
}
void DestroyChannel() {
transport_->DestroyChannel(1);
channel_ = NULL;
}
protected:
void OnConnecting(Transport* transport) {
connecting_signalled_ = true;
}
void OnCompleted(Transport* transport) {
completed_ = true;
}
void OnFailed(Transport* transport) {
failed_ = true;
}
rtc::Thread* thread_;
rtc::scoped_ptr<FakeTransport> transport_;
FakeTransportChannel* channel_;
bool connecting_signalled_;
bool completed_;
bool failed_;
};
class FakeCandidateTranslator : public cricket::CandidateTranslator {
public:
void AddMapping(int component, const std::string& channel_name) {
name_to_component[channel_name] = component;
component_to_name[component] = channel_name;
}
bool GetChannelNameFromComponent(
int component, std::string* channel_name) const {
if (component_to_name.find(component) == component_to_name.end()) {
return false;
}
*channel_name = component_to_name.find(component)->second;
return true;
}
bool GetComponentFromChannelName(
const std::string& channel_name, int* component) const {
if (name_to_component.find(channel_name) == name_to_component.end()) {
return false;
}
*component = name_to_component.find(channel_name)->second;
return true;
}
std::map<std::string, int> name_to_component;
std::map<int, std::string> component_to_name;
};
// Test that calling ConnectChannels triggers an OnConnecting signal.
TEST_F(TransportTest, TestConnectChannelsDoesSignal) {
EXPECT_TRUE(SetupChannel());
transport_->ConnectChannels();
EXPECT_FALSE(connecting_signalled_);
EXPECT_TRUE_WAIT(connecting_signalled_, 100);
}
// Test that DestroyAllChannels kills any pending OnConnecting signals.
TEST_F(TransportTest, TestDestroyAllClearsPosts) {
EXPECT_TRUE(transport_->CreateChannel(1) != NULL);
transport_->ConnectChannels();
transport_->DestroyAllChannels();
thread_->ProcessMessages(0);
EXPECT_FALSE(connecting_signalled_);
}
// This test verifies channels are created with proper ICE
// role, tiebreaker and remote ice mode and credentials after offer and
// answer negotiations.
TEST_F(TransportTest, TestChannelIceParameters) {
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
transport_->SetIceTiebreaker(99U);
cricket::TransportDescription local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_OFFER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
EXPECT_TRUE(SetupChannel());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode());
EXPECT_EQ(kIceUfrag1, channel_->ice_ufrag());
EXPECT_EQ(kIcePwd1, channel_->ice_pwd());
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
EXPECT_EQ(99U, channel_->IceTiebreaker());
EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode());
// Changing the transport role from CONTROLLING to CONTROLLED.
transport_->SetIceRole(cricket::ICEROLE_CONTROLLED);
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel_->GetIceRole());
EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode());
EXPECT_EQ(kIceUfrag1, channel_->remote_ice_ufrag());
EXPECT_EQ(kIcePwd1, channel_->remote_ice_pwd());
}
// Verifies that IceCredentialsChanged returns true when either ufrag or pwd
// changed, and false in other cases.
TEST_F(TransportTest, TestIceCredentialsChanged) {
EXPECT_TRUE(cricket::IceCredentialsChanged("u1", "p1", "u2", "p2"));
EXPECT_TRUE(cricket::IceCredentialsChanged("u1", "p1", "u2", "p1"));
EXPECT_TRUE(cricket::IceCredentialsChanged("u1", "p1", "u1", "p2"));
EXPECT_FALSE(cricket::IceCredentialsChanged("u1", "p1", "u1", "p1"));
}
// This test verifies that the callee's ICE role changes from controlled to
// controlling when the callee triggers an ICE restart.
TEST_F(TransportTest, TestIceControlledToControllingOnIceRestart) {
EXPECT_TRUE(SetupChannel());
transport_->SetIceRole(cricket::ICEROLE_CONTROLLED);
cricket::TransportDescription desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(desc,
cricket::CA_OFFER,
NULL));
ASSERT_TRUE(transport_->SetLocalTransportDescription(desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, transport_->ice_role());
cricket::TransportDescription new_local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag2, kIcePwd2);
ASSERT_TRUE(transport_->SetLocalTransportDescription(new_local_desc,
cricket::CA_OFFER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
}
// This test verifies that the caller's ICE role changes from controlling to
// controlled when the callee triggers an ICE restart.
TEST_F(TransportTest, TestIceControllingToControlledOnIceRestart) {
EXPECT_TRUE(SetupChannel());
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
cricket::TransportDescription desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(desc,
cricket::CA_OFFER,
NULL));
ASSERT_TRUE(transport_->SetRemoteTransportDescription(desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
cricket::TransportDescription new_local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag2, kIcePwd2);
ASSERT_TRUE(transport_->SetLocalTransportDescription(new_local_desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, transport_->ice_role());
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel_->GetIceRole());
}
// This test verifies that the caller's ICE role is still controlling after the
// callee triggers ICE restart if the callee's ICE mode is LITE.
TEST_F(TransportTest, TestIceControllingOnIceRestartIfRemoteIsIceLite) {
EXPECT_TRUE(SetupChannel());
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
cricket::TransportDescription desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(desc,
cricket::CA_OFFER,
NULL));
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE,
cricket::CONNECTIONROLE_NONE, NULL, cricket::Candidates());
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
cricket::TransportDescription new_local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag2, kIcePwd2);
ASSERT_TRUE(transport_->SetLocalTransportDescription(new_local_desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
}
// This test verifies that the Completed and Failed states can be reached.
TEST_F(TransportTest, TestChannelCompletedAndFailed) {
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
cricket::TransportDescription local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_OFFER,
NULL));
EXPECT_TRUE(SetupChannel());
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_ANSWER,
NULL));
channel_->SetConnectionCount(2);
channel_->SignalCandidatesAllocationDone(channel_);
channel_->SetWritable(true);
EXPECT_TRUE_WAIT(transport_->all_channels_writable(), 100);
// ICE is not yet completed because there is still more than one connection.
EXPECT_FALSE(completed_);
EXPECT_FALSE(failed_);
// When the connection count drops to 1, SignalCompleted should be emitted,
// and completed() should be true.
channel_->SetConnectionCount(1);
EXPECT_TRUE_WAIT(completed_, 100);
completed_ = false;
// When the connection count drops to 0, SignalFailed should be emitted, and
// completed() should be false.
channel_->SetConnectionCount(0);
EXPECT_TRUE_WAIT(failed_, 100);
EXPECT_FALSE(completed_);
}
// Tests channel role is reversed after receiving ice-lite from remote.
TEST_F(TransportTest, TestSetRemoteIceLiteInOffer) {
transport_->SetIceRole(cricket::ICEROLE_CONTROLLED);
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE,
cricket::CONNECTIONROLE_ACTPASS, NULL, cricket::Candidates());
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_OFFER,
NULL));
cricket::TransportDescription local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
EXPECT_TRUE(SetupChannel());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
EXPECT_EQ(cricket::ICEMODE_LITE, channel_->remote_ice_mode());
}
// Tests ice-lite in remote answer.
TEST_F(TransportTest, TestSetRemoteIceLiteInAnswer) {
transport_->SetIceRole(cricket::ICEROLE_CONTROLLING);
cricket::TransportDescription local_desc(
cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
cricket::CA_OFFER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role());
EXPECT_TRUE(SetupChannel());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
// Channels will be created in ICEFULL_MODE.
EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode());
cricket::TransportDescription remote_desc(
cricket::NS_JINGLE_ICE_UDP, std::vector<std::string>(),
kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE,
cricket::CONNECTIONROLE_NONE, NULL, cricket::Candidates());
ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc,
cricket::CA_ANSWER,
NULL));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole());
// After receiving remote description with ICEMODE_LITE, channel should
// have mode set to ICEMODE_LITE.
EXPECT_EQ(cricket::ICEMODE_LITE, channel_->remote_ice_mode());
}
// Tests that we can properly serialize/deserialize candidates.
TEST_F(TransportTest, TestP2PTransportWriteAndParseCandidate) {
Candidate test_candidate(
"", 1, "udp",
rtc::SocketAddress("2001:db8:fefe::1", 9999),
738197504, "abcdef", "ghijkl", "foo", "testnet", 50, "");
Candidate test_candidate2(
"", 2, "tcp",
rtc::SocketAddress("192.168.7.1", 9999),
1107296256, "mnopqr", "stuvwx", "bar", "testnet2", 100, "");
rtc::SocketAddress host_address("www.google.com", 24601);
host_address.SetResolvedIP(rtc::IPAddress(0x0A000001));
Candidate test_candidate3(
"", 3, "spdy", host_address, 1476395008, "yzabcd",
"efghij", "baz", "testnet3", 150, "");
WriteError write_error;
ParseError parse_error;
rtc::scoped_ptr<buzz::XmlElement> elem;
cricket::Candidate parsed_candidate;
cricket::P2PTransportParser parser;
FakeCandidateTranslator translator;
translator.AddMapping(1, "test");
translator.AddMapping(2, "test2");
translator.AddMapping(3, "test3");
EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate, &translator,
elem.accept(), &write_error));
EXPECT_EQ("", write_error.text);
EXPECT_EQ("test", elem->Attr(buzz::QN_NAME));
EXPECT_EQ("udp", elem->Attr(cricket::QN_PROTOCOL));
EXPECT_EQ("2001:db8:fefe::1", elem->Attr(cricket::QN_ADDRESS));
EXPECT_EQ("9999", elem->Attr(cricket::QN_PORT));
EXPECT_EQ("0.34", elem->Attr(cricket::QN_PREFERENCE));
EXPECT_EQ("abcdef", elem->Attr(cricket::QN_USERNAME));
EXPECT_EQ("ghijkl", elem->Attr(cricket::QN_PASSWORD));
EXPECT_EQ("foo", elem->Attr(cricket::QN_TYPE));
EXPECT_EQ("testnet", elem->Attr(cricket::QN_NETWORK));
EXPECT_EQ("50", elem->Attr(cricket::QN_GENERATION));
EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator,
&parsed_candidate, &parse_error));
EXPECT_TRUE(test_candidate.IsEquivalent(parsed_candidate));
EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate2, &translator,
elem.accept(), &write_error));
EXPECT_EQ("test2", elem->Attr(buzz::QN_NAME));
EXPECT_EQ("tcp", elem->Attr(cricket::QN_PROTOCOL));
EXPECT_EQ("192.168.7.1", elem->Attr(cricket::QN_ADDRESS));
EXPECT_EQ("9999", elem->Attr(cricket::QN_PORT));
EXPECT_EQ("0.51", elem->Attr(cricket::QN_PREFERENCE));
EXPECT_EQ("mnopqr", elem->Attr(cricket::QN_USERNAME));
EXPECT_EQ("stuvwx", elem->Attr(cricket::QN_PASSWORD));
EXPECT_EQ("bar", elem->Attr(cricket::QN_TYPE));
EXPECT_EQ("testnet2", elem->Attr(cricket::QN_NETWORK));
EXPECT_EQ("100", elem->Attr(cricket::QN_GENERATION));
EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator,
&parsed_candidate, &parse_error));
EXPECT_TRUE(test_candidate2.IsEquivalent(parsed_candidate));
// Check that an ip is preferred over hostname.
EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate3, &translator,
elem.accept(), &write_error));
EXPECT_EQ("test3", elem->Attr(cricket::QN_NAME));
EXPECT_EQ("spdy", elem->Attr(cricket::QN_PROTOCOL));
EXPECT_EQ("10.0.0.1", elem->Attr(cricket::QN_ADDRESS));
EXPECT_EQ("24601", elem->Attr(cricket::QN_PORT));
EXPECT_EQ("0.69", elem->Attr(cricket::QN_PREFERENCE));
EXPECT_EQ("yzabcd", elem->Attr(cricket::QN_USERNAME));
EXPECT_EQ("efghij", elem->Attr(cricket::QN_PASSWORD));
EXPECT_EQ("baz", elem->Attr(cricket::QN_TYPE));
EXPECT_EQ("testnet3", elem->Attr(cricket::QN_NETWORK));
EXPECT_EQ("150", elem->Attr(cricket::QN_GENERATION));
EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator,
&parsed_candidate, &parse_error));
EXPECT_TRUE(test_candidate3.IsEquivalent(parsed_candidate));
}
TEST_F(TransportTest, TestGetStats) {
EXPECT_TRUE(SetupChannel());
cricket::TransportStats stats;
EXPECT_TRUE(transport_->GetStats(&stats));
// Note that this tests the behavior of a FakeTransportChannel.
ASSERT_EQ(1U, stats.channel_stats.size());
EXPECT_EQ(1, stats.channel_stats[0].component);
transport_->ConnectChannels();
EXPECT_TRUE(transport_->GetStats(&stats));
ASSERT_EQ(1U, stats.channel_stats.size());
EXPECT_EQ(1, stats.channel_stats[0].component);
}

View File

@ -1,60 +0,0 @@
/*
* 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 <sstream>
#include "webrtc/p2p/base/transportchannel.h"
namespace cricket {
std::string TransportChannel::ToString() const {
const char READABLE_ABBREV[2] = { '_', 'R' };
const char WRITABLE_ABBREV[2] = { '_', 'W' };
std::stringstream ss;
ss << "Channel[" << content_name_
<< "|" << component_
<< "|" << READABLE_ABBREV[readable_] << WRITABLE_ABBREV[writable_] << "]";
return ss.str();
}
void TransportChannel::set_readable(bool readable) {
if (readable_ != readable) {
readable_ = readable;
SignalReadableState(this);
}
}
void TransportChannel::set_writable(bool writable) {
if (writable_ != writable) {
writable_ = writable;
if (writable_) {
SignalReadyToSend(this);
}
SignalWritableState(this);
}
}
} // namespace cricket

View File

@ -1,160 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_TRANSPORTCHANNEL_H_
#define WEBRTC_P2P_BASE_TRANSPORTCHANNEL_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportdescription.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/dscp.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socket.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/sslstreamadapter.h"
namespace cricket {
class Candidate;
// Flags for SendPacket/SignalReadPacket.
enum PacketFlags {
PF_NORMAL = 0x00, // A normal packet.
PF_SRTP_BYPASS = 0x01, // An encrypted SRTP packet; bypass any additional
// crypto provided by the transport (e.g. DTLS)
};
// A TransportChannel represents one logical stream of packets that are sent
// between the two sides of a session.
class TransportChannel : public sigslot::has_slots<> {
public:
explicit TransportChannel(const std::string& content_name, int component)
: content_name_(content_name),
component_(component),
readable_(false), writable_(false) {}
virtual ~TransportChannel() {}
// TODO(mallinath) - Remove this API, as it's no longer useful.
// Returns the session id of this channel.
virtual const std::string SessionId() const { return std::string(); }
const std::string& content_name() const { return content_name_; }
int component() const { return component_; }
// Returns the readable and states of this channel. Each time one of these
// states changes, a signal is raised. These states are aggregated by the
// TransportManager.
bool readable() const { return readable_; }
bool writable() const { return writable_; }
sigslot::signal1<TransportChannel*> SignalReadableState;
sigslot::signal1<TransportChannel*> SignalWritableState;
// Emitted when the TransportChannel's ability to send has changed.
sigslot::signal1<TransportChannel*> SignalReadyToSend;
// Attempts to send the given packet. The return value is < 0 on failure.
// TODO: Remove the default argument once channel code is updated.
virtual int SendPacket(const char* data, size_t len,
const rtc::PacketOptions& options,
int flags = 0) = 0;
// Sets a socket option on this channel. Note that not all options are
// supported by all transport types.
virtual int SetOption(rtc::Socket::Option opt, int value) = 0;
// Returns the most recent error that occurred on this channel.
virtual int GetError() = 0;
// Returns the current stats for this connection.
virtual bool GetStats(ConnectionInfos* infos) = 0;
// Is DTLS active?
virtual bool IsDtlsActive() const = 0;
// Default implementation.
virtual bool GetSslRole(rtc::SSLRole* role) const = 0;
// Sets up the ciphers to use for DTLS-SRTP.
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) = 0;
// Finds out which DTLS-SRTP cipher was negotiated
virtual bool GetSrtpCipher(std::string* cipher) = 0;
// Gets a copy of the local SSL identity, owned by the caller.
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const = 0;
// Gets a copy of the remote side's SSL certificate, owned by the caller.
virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const = 0;
// Allows key material to be extracted for external encryption.
virtual bool ExportKeyingMaterial(const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len) = 0;
// Signalled each time a packet is received on this channel.
sigslot::signal5<TransportChannel*, const char*,
size_t, const rtc::PacketTime&, int> SignalReadPacket;
// This signal occurs when there is a change in the way that packets are
// being routed, i.e. to a different remote location. The candidate
// indicates where and how we are currently sending media.
sigslot::signal2<TransportChannel*, const Candidate&> SignalRouteChange;
// Invoked when the channel is being destroyed.
sigslot::signal1<TransportChannel*> SignalDestroyed;
// Debugging description of this transport channel.
std::string ToString() const;
protected:
// Sets the readable state, signaling if necessary.
void set_readable(bool readable);
// Sets the writable state, signaling if necessary.
void set_writable(bool writable);
private:
// Used mostly for debugging.
std::string content_name_;
int component_;
bool readable_;
bool writable_;
DISALLOW_EVIL_CONSTRUCTORS(TransportChannel);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORTCHANNEL_H_

View File

@ -1,128 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_TRANSPORTCHANNELIMPL_H_
#define WEBRTC_P2P_BASE_TRANSPORTCHANNELIMPL_H_
#include <string>
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportchannel.h"
namespace buzz { class XmlElement; }
namespace cricket {
class Candidate;
// Base class for real implementations of TransportChannel. This includes some
// methods called only by Transport, which do not need to be exposed to the
// client.
class TransportChannelImpl : public TransportChannel {
public:
explicit TransportChannelImpl(const std::string& content_name, int component)
: TransportChannel(content_name, component) {}
// Returns the transport that created this channel.
virtual Transport* GetTransport() = 0;
// For ICE channels.
virtual IceRole GetIceRole() const = 0;
virtual void SetIceRole(IceRole role) = 0;
virtual void SetIceTiebreaker(uint64 tiebreaker) = 0;
virtual size_t GetConnectionCount() const = 0;
// To toggle G-ICE/ICE.
virtual bool GetIceProtocolType(IceProtocolType* type) const = 0;
virtual void SetIceProtocolType(IceProtocolType type) = 0;
// SetIceCredentials only need to be implemented by the ICE
// transport channels. Non-ICE transport channels can just ignore.
// The ufrag and pwd should be set before the Connect() is called.
virtual void SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) = 0;
// SetRemoteIceCredentials only need to be implemented by the ICE
// transport channels. Non-ICE transport channels can just ignore.
virtual void SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) = 0;
// SetRemoteIceMode must be implemented only by the ICE transport channels.
virtual void SetRemoteIceMode(IceMode mode) = 0;
// Begins the process of attempting to make a connection to the other client.
virtual void Connect() = 0;
// Resets this channel back to the initial state (i.e., not connecting).
virtual void Reset() = 0;
// Allows an individual channel to request signaling and be notified when it
// is ready. This is useful if the individual named channels have need to
// send their own transport-info stanzas.
sigslot::signal1<TransportChannelImpl*> SignalRequestSignaling;
virtual void OnSignalingReady() = 0;
// Handles sending and receiving of candidates. The Transport
// receives the candidates and may forward them to the relevant
// channel.
//
// Note: Since candidates are delivered asynchronously to the
// channel, they cannot return an error if the message is invalid.
// It is assumed that the Transport will have checked validity
// before forwarding.
sigslot::signal2<TransportChannelImpl*,
const Candidate&> SignalCandidateReady;
virtual void OnCandidate(const Candidate& candidate) = 0;
// DTLS methods
// Set DTLS local identity. The identity object is not copied, but the caller
// retains ownership and must delete it after this TransportChannelImpl is
// destroyed.
// TODO(bemasc): Fix the ownership semantics of this method.
virtual bool SetLocalIdentity(rtc::SSLIdentity* identity) = 0;
// Set DTLS Remote fingerprint. Must be after local identity set.
virtual bool SetRemoteFingerprint(const std::string& digest_alg,
const uint8* digest,
size_t digest_len) = 0;
virtual bool SetSslRole(rtc::SSLRole role) = 0;
// TransportChannel is forwarding this signal from PortAllocatorSession.
sigslot::signal1<TransportChannelImpl*> SignalCandidatesAllocationDone;
// Invoked when there is conflict in the ICE role between local and remote
// agents.
sigslot::signal1<TransportChannelImpl*> SignalRoleConflict;
// Emitted whenever the number of connections available to the transport
// channel decreases.
sigslot::signal1<TransportChannelImpl*> SignalConnectionRemoved;
private:
DISALLOW_EVIL_CONSTRUCTORS(TransportChannelImpl);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORTCHANNELIMPL_H_

View File

@ -1,266 +0,0 @@
/*
* 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 "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportchannelimpl.h"
#include "webrtc/p2p/base/transportchannelproxy.h"
#include "webrtc/base/common.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/thread.h"
namespace cricket {
enum {
MSG_UPDATESTATE,
};
TransportChannelProxy::TransportChannelProxy(const std::string& content_name,
const std::string& name,
int component)
: TransportChannel(content_name, component),
name_(name),
impl_(NULL) {
worker_thread_ = rtc::Thread::Current();
}
TransportChannelProxy::~TransportChannelProxy() {
// Clearing any pending signal.
worker_thread_->Clear(this);
if (impl_)
impl_->GetTransport()->DestroyChannel(impl_->component());
}
void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (impl == impl_) {
// Ignore if the |impl| has already been set.
LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call "
<< "with a same impl as the existing one.";
return;
}
// Destroy any existing impl_.
if (impl_) {
impl_->GetTransport()->DestroyChannel(impl_->component());
}
// Adopt the supplied impl, and connect to its signals.
impl_ = impl;
if (impl_) {
impl_->SignalReadableState.connect(
this, &TransportChannelProxy::OnReadableState);
impl_->SignalWritableState.connect(
this, &TransportChannelProxy::OnWritableState);
impl_->SignalReadPacket.connect(
this, &TransportChannelProxy::OnReadPacket);
impl_->SignalReadyToSend.connect(
this, &TransportChannelProxy::OnReadyToSend);
impl_->SignalRouteChange.connect(
this, &TransportChannelProxy::OnRouteChange);
for (OptionList::iterator it = pending_options_.begin();
it != pending_options_.end();
++it) {
impl_->SetOption(it->first, it->second);
}
// Push down the SRTP ciphers, if any were set.
if (!pending_srtp_ciphers_.empty()) {
impl_->SetSrtpCiphers(pending_srtp_ciphers_);
}
pending_options_.clear();
}
// Post ourselves a message to see if we need to fire state callbacks.
worker_thread_->Post(this, MSG_UPDATESTATE);
}
int TransportChannelProxy::SendPacket(const char* data, size_t len,
const rtc::PacketOptions& options,
int flags) {
ASSERT(rtc::Thread::Current() == worker_thread_);
// Fail if we don't have an impl yet.
if (!impl_) {
return -1;
}
return impl_->SendPacket(data, len, options, flags);
}
int TransportChannelProxy::SetOption(rtc::Socket::Option opt, int value) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
pending_options_.push_back(OptionPair(opt, value));
return 0;
}
return impl_->SetOption(opt, value);
}
int TransportChannelProxy::GetError() {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return 0;
}
return impl_->GetError();
}
bool TransportChannelProxy::GetStats(ConnectionInfos* infos) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->GetStats(infos);
}
bool TransportChannelProxy::IsDtlsActive() const {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->IsDtlsActive();
}
bool TransportChannelProxy::GetSslRole(rtc::SSLRole* role) const {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->GetSslRole(role);
}
bool TransportChannelProxy::SetSslRole(rtc::SSLRole role) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->SetSslRole(role);
}
bool TransportChannelProxy::SetSrtpCiphers(const std::vector<std::string>&
ciphers) {
ASSERT(rtc::Thread::Current() == worker_thread_);
pending_srtp_ciphers_ = ciphers; // Cache so we can send later, but always
// set so it stays consistent.
if (impl_) {
return impl_->SetSrtpCiphers(ciphers);
}
return true;
}
bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->GetSrtpCipher(cipher);
}
bool TransportChannelProxy::GetLocalIdentity(
rtc::SSLIdentity** identity) const {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->GetLocalIdentity(identity);
}
bool TransportChannelProxy::GetRemoteCertificate(
rtc::SSLCertificate** cert) const {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->GetRemoteCertificate(cert);
}
bool TransportChannelProxy::ExportKeyingMaterial(const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return false;
}
return impl_->ExportKeyingMaterial(label, context, context_len, use_context,
result, result_len);
}
IceRole TransportChannelProxy::GetIceRole() const {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (!impl_) {
return ICEROLE_UNKNOWN;
}
return impl_->GetIceRole();
}
void TransportChannelProxy::OnReadableState(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == impl_);
set_readable(impl_->readable());
// Note: SignalReadableState fired by set_readable.
}
void TransportChannelProxy::OnWritableState(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == impl_);
set_writable(impl_->writable());
// Note: SignalWritableState fired by set_readable.
}
void TransportChannelProxy::OnReadPacket(
TransportChannel* channel, const char* data, size_t size,
const rtc::PacketTime& packet_time, int flags) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == impl_);
SignalReadPacket(this, data, size, packet_time, flags);
}
void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == impl_);
SignalReadyToSend(this);
}
void TransportChannelProxy::OnRouteChange(TransportChannel* channel,
const Candidate& candidate) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(channel == impl_);
SignalRouteChange(this, candidate);
}
void TransportChannelProxy::OnMessage(rtc::Message* msg) {
ASSERT(rtc::Thread::Current() == worker_thread_);
if (msg->message_id == MSG_UPDATESTATE) {
// If impl_ is already readable or writable, push up those signals.
set_readable(impl_ ? impl_->readable() : false);
set_writable(impl_ ? impl_->writable() : false);
}
}
} // namespace cricket

View File

@ -1,112 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
#define WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
#include <string>
#include <utility>
#include <vector>
#include "webrtc/p2p/base/transportchannel.h"
#include "webrtc/base/messagehandler.h"
namespace rtc {
class Thread;
}
namespace cricket {
class TransportChannelImpl;
// Proxies calls between the client and the transport channel implementation.
// This is needed because clients are allowed to create channels before the
// network negotiation is complete. Hence, we create a proxy up front, and
// when negotiation completes, connect the proxy to the implementaiton.
class TransportChannelProxy : public TransportChannel,
public rtc::MessageHandler {
public:
TransportChannelProxy(const std::string& content_name,
const std::string& name,
int component);
virtual ~TransportChannelProxy();
const std::string& name() const { return name_; }
TransportChannelImpl* impl() { return impl_; }
// Sets the implementation to which we will proxy.
void SetImplementation(TransportChannelImpl* impl);
// Implementation of the TransportChannel interface. These simply forward to
// the implementation.
virtual int SendPacket(const char* data, size_t len,
const rtc::PacketOptions& options,
int flags);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetError();
virtual IceRole GetIceRole() const;
virtual bool GetStats(ConnectionInfos* infos);
virtual bool IsDtlsActive() const;
virtual bool GetSslRole(rtc::SSLRole* role) const;
virtual bool SetSslRole(rtc::SSLRole role);
virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers);
virtual bool GetSrtpCipher(std::string* cipher);
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const;
virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const;
virtual bool ExportKeyingMaterial(const std::string& label,
const uint8* context,
size_t context_len,
bool use_context,
uint8* result,
size_t result_len);
private:
// Catch signals from the implementation channel. These just forward to the
// client (after updating our state to match).
void OnReadableState(TransportChannel* channel);
void OnWritableState(TransportChannel* channel);
void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
const rtc::PacketTime& packet_time, int flags);
void OnReadyToSend(TransportChannel* channel);
void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
void OnMessage(rtc::Message* message);
typedef std::pair<rtc::Socket::Option, int> OptionPair;
typedef std::vector<OptionPair> OptionList;
std::string name_;
rtc::Thread* worker_thread_;
TransportChannelImpl* impl_;
OptionList pending_options_;
std::vector<std::string> pending_srtp_ciphers_;
DISALLOW_EVIL_CONSTRUCTORS(TransportChannelProxy);
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_

View File

@ -1,72 +0,0 @@
/*
* libjingle
* Copyright 2013 Google Inc. All rights reserved.
*
* 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 "webrtc/p2p/base/transportdescription.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/base/stringutils.h"
namespace cricket {
bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role) {
const char* const roles[] = {
CONNECTIONROLE_ACTIVE_STR,
CONNECTIONROLE_PASSIVE_STR,
CONNECTIONROLE_ACTPASS_STR,
CONNECTIONROLE_HOLDCONN_STR
};
for (size_t i = 0; i < ARRAY_SIZE(roles); ++i) {
if (_stricmp(roles[i], role_str.c_str()) == 0) {
*role = static_cast<ConnectionRole>(CONNECTIONROLE_ACTIVE + i);
return true;
}
}
return false;
}
bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str) {
switch (role) {
case cricket::CONNECTIONROLE_ACTIVE:
*role_str = cricket::CONNECTIONROLE_ACTIVE_STR;
break;
case cricket::CONNECTIONROLE_ACTPASS:
*role_str = cricket::CONNECTIONROLE_ACTPASS_STR;
break;
case cricket::CONNECTIONROLE_PASSIVE:
*role_str = cricket::CONNECTIONROLE_PASSIVE_STR;
break;
case cricket::CONNECTIONROLE_HOLDCONN:
*role_str = cricket::CONNECTIONROLE_HOLDCONN_STR;
break;
default:
return false;
}
return true;
}
} // namespace cricket

View File

@ -1,188 +0,0 @@
/*
* libjingle
* Copyright 2012, The Libjingle Authors.
*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_TRANSPORTDESCRIPTION_H_
#define WEBRTC_P2P_BASE_TRANSPORTDESCRIPTION_H_
#include <algorithm>
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/sslfingerprint.h"
namespace cricket {
// SEC_ENABLED and SEC_REQUIRED should only be used if the session
// was negotiated over TLS, to protect the inline crypto material
// exchange.
// SEC_DISABLED: No crypto in outgoing offer, ignore any supplied crypto.
// SEC_ENABLED: Crypto in outgoing offer and answer (if supplied in offer).
// SEC_REQUIRED: Crypto in outgoing offer and answer. Fail any offer with absent
// or unsupported crypto.
enum SecurePolicy {
SEC_DISABLED,
SEC_ENABLED,
SEC_REQUIRED
};
// The transport protocol we've elected to use.
enum TransportProtocol {
ICEPROTO_GOOGLE, // Google version of ICE protocol.
ICEPROTO_HYBRID, // ICE, but can fall back to the Google version.
ICEPROTO_RFC5245 // Standard RFC 5245 version of ICE.
};
// The old name for TransportProtocol.
// TODO(juberti): remove this.
typedef TransportProtocol IceProtocolType;
// Whether our side of the call is driving the negotiation, or the other side.
enum IceRole {
ICEROLE_CONTROLLING = 0,
ICEROLE_CONTROLLED,
ICEROLE_UNKNOWN
};
// ICE RFC 5245 implementation type.
enum IceMode {
ICEMODE_FULL, // As defined in http://tools.ietf.org/html/rfc5245#section-4.1
ICEMODE_LITE // As defined in http://tools.ietf.org/html/rfc5245#section-4.2
};
// RFC 4145 - http://tools.ietf.org/html/rfc4145#section-4
// 'active': The endpoint will initiate an outgoing connection.
// 'passive': The endpoint will accept an incoming connection.
// 'actpass': The endpoint is willing to accept an incoming
// connection or to initiate an outgoing connection.
enum ConnectionRole {
CONNECTIONROLE_NONE = 0,
CONNECTIONROLE_ACTIVE,
CONNECTIONROLE_PASSIVE,
CONNECTIONROLE_ACTPASS,
CONNECTIONROLE_HOLDCONN,
};
extern const char CONNECTIONROLE_ACTIVE_STR[];
extern const char CONNECTIONROLE_PASSIVE_STR[];
extern const char CONNECTIONROLE_ACTPASS_STR[];
extern const char CONNECTIONROLE_HOLDCONN_STR[];
bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role);
bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str);
typedef std::vector<Candidate> Candidates;
struct TransportDescription {
TransportDescription()
: ice_mode(ICEMODE_FULL),
connection_role(CONNECTIONROLE_NONE) {}
TransportDescription(const std::string& transport_type,
const std::vector<std::string>& transport_options,
const std::string& ice_ufrag,
const std::string& ice_pwd,
IceMode ice_mode,
ConnectionRole role,
const rtc::SSLFingerprint* identity_fingerprint,
const Candidates& candidates)
: transport_type(transport_type),
transport_options(transport_options),
ice_ufrag(ice_ufrag),
ice_pwd(ice_pwd),
ice_mode(ice_mode),
connection_role(role),
identity_fingerprint(CopyFingerprint(identity_fingerprint)),
candidates(candidates) {}
TransportDescription(const std::string& transport_type,
const std::string& ice_ufrag,
const std::string& ice_pwd)
: transport_type(transport_type),
ice_ufrag(ice_ufrag),
ice_pwd(ice_pwd),
ice_mode(ICEMODE_FULL),
connection_role(CONNECTIONROLE_NONE) {}
TransportDescription(const TransportDescription& from)
: transport_type(from.transport_type),
transport_options(from.transport_options),
ice_ufrag(from.ice_ufrag),
ice_pwd(from.ice_pwd),
ice_mode(from.ice_mode),
connection_role(from.connection_role),
identity_fingerprint(CopyFingerprint(from.identity_fingerprint.get())),
candidates(from.candidates) {}
TransportDescription& operator=(const TransportDescription& from) {
// Self-assignment
if (this == &from)
return *this;
transport_type = from.transport_type;
transport_options = from.transport_options;
ice_ufrag = from.ice_ufrag;
ice_pwd = from.ice_pwd;
ice_mode = from.ice_mode;
connection_role = from.connection_role;
identity_fingerprint.reset(CopyFingerprint(
from.identity_fingerprint.get()));
candidates = from.candidates;
return *this;
}
bool HasOption(const std::string& option) const {
return (std::find(transport_options.begin(), transport_options.end(),
option) != transport_options.end());
}
void AddOption(const std::string& option) {
transport_options.push_back(option);
}
bool secure() const { return identity_fingerprint != NULL; }
static rtc::SSLFingerprint* CopyFingerprint(
const rtc::SSLFingerprint* from) {
if (!from)
return NULL;
return new rtc::SSLFingerprint(*from);
}
std::string transport_type; // xmlns of <transport>
std::vector<std::string> transport_options;
std::string ice_ufrag;
std::string ice_pwd;
IceMode ice_mode;
ConnectionRole connection_role;
rtc::scoped_ptr<rtc::SSLFingerprint> identity_fingerprint;
Candidates candidates;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORTDESCRIPTION_H_

View File

@ -1,177 +0,0 @@
/*
* libjingle
* Copyright 2012 Google Inc. All rights reserved.
*
* 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 "webrtc/p2p/base/transportdescriptionfactory.h"
#include "webrtc/p2p/base/transportdescription.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/messagedigest.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/sslfingerprint.h"
namespace cricket {
static TransportProtocol kDefaultProtocol = ICEPROTO_GOOGLE;
TransportDescriptionFactory::TransportDescriptionFactory()
: protocol_(kDefaultProtocol),
secure_(SEC_DISABLED),
identity_(NULL) {
}
TransportDescription* TransportDescriptionFactory::CreateOffer(
const TransportOptions& options,
const TransportDescription* current_description) const {
rtc::scoped_ptr<TransportDescription> desc(new TransportDescription());
// Set the transport type depending on the selected protocol.
if (protocol_ == ICEPROTO_RFC5245) {
desc->transport_type = NS_JINGLE_ICE_UDP;
} else if (protocol_ == ICEPROTO_HYBRID) {
desc->transport_type = NS_JINGLE_ICE_UDP;
desc->AddOption(ICE_OPTION_GICE);
} else if (protocol_ == ICEPROTO_GOOGLE) {
desc->transport_type = NS_GINGLE_P2P;
}
// Generate the ICE credentials if we don't already have them.
if (!current_description || options.ice_restart) {
desc->ice_ufrag = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
desc->ice_pwd = rtc::CreateRandomString(ICE_PWD_LENGTH);
} else {
desc->ice_ufrag = current_description->ice_ufrag;
desc->ice_pwd = current_description->ice_pwd;
}
// If we are trying to establish a secure transport, add a fingerprint.
if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
// Fail if we can't create the fingerprint.
// If we are the initiator set role to "actpass".
if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) {
return NULL;
}
}
return desc.release();
}
TransportDescription* TransportDescriptionFactory::CreateAnswer(
const TransportDescription* offer,
const TransportOptions& options,
const TransportDescription* current_description) const {
// A NULL offer is treated as a GICE transport description.
// TODO(juberti): Figure out why we get NULL offers, and fix this upstream.
rtc::scoped_ptr<TransportDescription> desc(new TransportDescription());
// Figure out which ICE variant to negotiate; prefer RFC 5245 ICE, but fall
// back to G-ICE if needed. Note that we never create a hybrid answer, since
// we know what the other side can support already.
if (offer && offer->transport_type == NS_JINGLE_ICE_UDP &&
(protocol_ == ICEPROTO_RFC5245 || protocol_ == ICEPROTO_HYBRID)) {
// Offer is ICE or hybrid, we support ICE or hybrid: use ICE.
desc->transport_type = NS_JINGLE_ICE_UDP;
} else if (offer && offer->transport_type == NS_JINGLE_ICE_UDP &&
offer->HasOption(ICE_OPTION_GICE) &&
protocol_ == ICEPROTO_GOOGLE) {
desc->transport_type = NS_GINGLE_P2P;
// Offer is hybrid, we support GICE: use GICE.
} else if ((!offer || offer->transport_type == NS_GINGLE_P2P) &&
(protocol_ == ICEPROTO_HYBRID || protocol_ == ICEPROTO_GOOGLE)) {
// Offer is GICE, we support hybrid or GICE: use GICE.
desc->transport_type = NS_GINGLE_P2P;
} else {
// Mismatch.
LOG(LS_WARNING) << "Failed to create TransportDescription answer "
"because of incompatible transport types";
return NULL;
}
// Generate the ICE credentials if we don't already have them or ice is
// being restarted.
if (!current_description || options.ice_restart) {
desc->ice_ufrag = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
desc->ice_pwd = rtc::CreateRandomString(ICE_PWD_LENGTH);
} else {
desc->ice_ufrag = current_description->ice_ufrag;
desc->ice_pwd = current_description->ice_pwd;
}
// Negotiate security params.
if (offer && offer->identity_fingerprint.get()) {
// The offer supports DTLS, so answer with DTLS, as long as we support it.
if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
// Fail if we can't create the fingerprint.
// Setting DTLS role to active.
ConnectionRole role = (options.prefer_passive_role) ?
CONNECTIONROLE_PASSIVE : CONNECTIONROLE_ACTIVE;
if (!SetSecurityInfo(desc.get(), role)) {
return NULL;
}
}
} else if (secure_ == SEC_REQUIRED) {
// We require DTLS, but the other side didn't offer it. Fail.
LOG(LS_WARNING) << "Failed to create TransportDescription answer "
"because of incompatible security settings";
return NULL;
}
return desc.release();
}
bool TransportDescriptionFactory::SetSecurityInfo(
TransportDescription* desc, ConnectionRole role) const {
if (!identity_) {
LOG(LS_ERROR) << "Cannot create identity digest with no identity";
return false;
}
// This digest algorithm is used to produce the a=fingerprint lines in SDP.
// RFC 4572 Section 5 requires that those lines use the same hash function as
// the certificate's signature.
std::string digest_alg;
if (!identity_->certificate().GetSignatureDigestAlgorithm(&digest_alg)) {
LOG(LS_ERROR) << "Failed to retrieve the certificate's digest algorithm";
return false;
}
desc->identity_fingerprint.reset(
rtc::SSLFingerprint::Create(digest_alg, identity_));
if (!desc->identity_fingerprint.get()) {
LOG(LS_ERROR) << "Failed to create identity fingerprint, alg="
<< digest_alg;
return false;
}
// Assign security role.
desc->connection_role = role;
return true;
}
} // namespace cricket

View File

@ -1,83 +0,0 @@
/*
* libjingle
* Copyright 2012 Google Inc. All rights reserved.
*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_TRANSPORTDESCRIPTIONFACTORY_H_
#define WEBRTC_P2P_BASE_TRANSPORTDESCRIPTIONFACTORY_H_
#include "webrtc/p2p/base/transportdescription.h"
namespace rtc {
class SSLIdentity;
}
namespace cricket {
struct TransportOptions {
TransportOptions() : ice_restart(false), prefer_passive_role(false) {}
bool ice_restart;
bool prefer_passive_role;
};
// Creates transport descriptions according to the supplied configuration.
// When creating answers, performs the appropriate negotiation
// of the various fields to determine the proper result.
class TransportDescriptionFactory {
public:
// Default ctor; use methods below to set configuration.
TransportDescriptionFactory();
SecurePolicy secure() const { return secure_; }
// The identity to use when setting up DTLS.
rtc::SSLIdentity* identity() const { return identity_; }
// Specifies the transport protocol to be use.
void set_protocol(TransportProtocol protocol) { protocol_ = protocol; }
// Specifies the transport security policy to use.
void set_secure(SecurePolicy s) { secure_ = s; }
// Specifies the identity to use (only used when secure is not SEC_DISABLED).
void set_identity(rtc::SSLIdentity* identity) { identity_ = identity; }
// Creates a transport description suitable for use in an offer.
TransportDescription* CreateOffer(const TransportOptions& options,
const TransportDescription* current_description) const;
// Create a transport description that is a response to an offer.
TransportDescription* CreateAnswer(
const TransportDescription* offer,
const TransportOptions& options,
const TransportDescription* current_description) const;
private:
bool SetSecurityInfo(TransportDescription* description,
ConnectionRole role) const;
TransportProtocol protocol_;
SecurePolicy secure_;
rtc::SSLIdentity* identity_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORTDESCRIPTIONFACTORY_H_

View File

@ -1,382 +0,0 @@
/*
* libjingle
* Copyright 2012, 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 <string>
#include <vector>
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/transportdescription.h"
#include "webrtc/p2p/base/transportdescriptionfactory.h"
#include "webrtc/base/fakesslidentity.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/ssladapter.h"
using rtc::scoped_ptr;
using cricket::TransportDescriptionFactory;
using cricket::TransportDescription;
using cricket::TransportOptions;
class TransportDescriptionFactoryTest : public testing::Test {
public:
TransportDescriptionFactoryTest()
: id1_(new rtc::FakeSSLIdentity("User1")),
id2_(new rtc::FakeSSLIdentity("User2")) {
}
void CheckDesc(const TransportDescription* desc, const std::string& type,
const std::string& opt, const std::string& ice_ufrag,
const std::string& ice_pwd, const std::string& dtls_alg) {
ASSERT_TRUE(desc != NULL);
EXPECT_EQ(type, desc->transport_type);
EXPECT_EQ(!opt.empty(), desc->HasOption(opt));
if (ice_ufrag.empty() && ice_pwd.empty()) {
EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
desc->ice_ufrag.size());
EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
desc->ice_pwd.size());
} else {
EXPECT_EQ(ice_ufrag, desc->ice_ufrag);
EXPECT_EQ(ice_pwd, desc->ice_pwd);
}
if (dtls_alg.empty()) {
EXPECT_TRUE(desc->identity_fingerprint.get() == NULL);
} else {
ASSERT_TRUE(desc->identity_fingerprint.get() != NULL);
EXPECT_EQ(desc->identity_fingerprint->algorithm, dtls_alg);
EXPECT_GT(desc->identity_fingerprint->digest.length(), 0U);
}
}
// This test ice restart by doing two offer answer exchanges. On the second
// exchange ice is restarted. The test verifies that the ufrag and password
// in the offer and answer is changed.
// If |dtls| is true, the test verifies that the finger print is not changed.
void TestIceRestart(bool dtls) {
if (dtls) {
f1_.set_secure(cricket::SEC_ENABLED);
f2_.set_secure(cricket::SEC_ENABLED);
f1_.set_identity(id1_.get());
f2_.set_identity(id2_.get());
} else {
f1_.set_secure(cricket::SEC_DISABLED);
f2_.set_secure(cricket::SEC_DISABLED);
}
cricket::TransportOptions options;
// The initial offer / answer exchange.
rtc::scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
options, NULL));
rtc::scoped_ptr<TransportDescription> answer(
f2_.CreateAnswer(offer.get(),
options, NULL));
// Create an updated offer where we restart ice.
options.ice_restart = true;
rtc::scoped_ptr<TransportDescription> restart_offer(f1_.CreateOffer(
options, offer.get()));
VerifyUfragAndPasswordChanged(dtls, offer.get(), restart_offer.get());
// Create a new answer. The transport ufrag and password is changed since
// |options.ice_restart == true|
rtc::scoped_ptr<TransportDescription> restart_answer(
f2_.CreateAnswer(restart_offer.get(), options, answer.get()));
ASSERT_TRUE(restart_answer.get() != NULL);
VerifyUfragAndPasswordChanged(dtls, answer.get(), restart_answer.get());
}
void VerifyUfragAndPasswordChanged(bool dtls,
const TransportDescription* org_desc,
const TransportDescription* restart_desc) {
EXPECT_NE(org_desc->ice_pwd, restart_desc->ice_pwd);
EXPECT_NE(org_desc->ice_ufrag, restart_desc->ice_ufrag);
EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
restart_desc->ice_ufrag.size());
EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
restart_desc->ice_pwd.size());
// If DTLS is enabled, make sure the finger print is unchanged.
if (dtls) {
EXPECT_FALSE(
org_desc->identity_fingerprint->GetRfc4572Fingerprint().empty());
EXPECT_EQ(org_desc->identity_fingerprint->GetRfc4572Fingerprint(),
restart_desc->identity_fingerprint->GetRfc4572Fingerprint());
}
}
protected:
TransportDescriptionFactory f1_;
TransportDescriptionFactory f2_;
scoped_ptr<rtc::SSLIdentity> id1_;
scoped_ptr<rtc::SSLIdentity> id2_;
};
// Test that in the default case, we generate the expected G-ICE offer.
TEST_F(TransportDescriptionFactoryTest, TestOfferGice) {
f1_.set_protocol(cricket::ICEPROTO_GOOGLE);
scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
}
// Test generating a hybrid offer.
TEST_F(TransportDescriptionFactoryTest, TestOfferHybrid) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "", "");
}
// Test generating an ICE-only offer.
TEST_F(TransportDescriptionFactoryTest, TestOfferIce) {
f1_.set_protocol(cricket::ICEPROTO_RFC5245);
scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
}
// Test generating a hybrid offer with DTLS.
TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtls) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f1_.set_secure(cricket::SEC_ENABLED);
f1_.set_identity(id1_.get());
std::string digest_alg;
ASSERT_TRUE(id1_->certificate().GetSignatureDigestAlgorithm(&digest_alg));
scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "",
digest_alg);
// Ensure it also works with SEC_REQUIRED.
f1_.set_secure(cricket::SEC_REQUIRED);
desc.reset(f1_.CreateOffer(TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "",
digest_alg);
}
// Test generating a hybrid offer with DTLS fails with no identity.
TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsWithNoIdentity) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f1_.set_secure(cricket::SEC_ENABLED);
scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
TransportOptions(), NULL));
ASSERT_TRUE(desc.get() == NULL);
}
// Test updating a hybrid offer with DTLS to pick ICE.
// The ICE credentials should stay the same in the new offer.
TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsReofferIceDtls) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f1_.set_secure(cricket::SEC_ENABLED);
f1_.set_identity(id1_.get());
std::string digest_alg;
ASSERT_TRUE(id1_->certificate().GetSignatureDigestAlgorithm(&digest_alg));
scoped_ptr<TransportDescription> old_desc(f1_.CreateOffer(
TransportOptions(), NULL));
ASSERT_TRUE(old_desc.get() != NULL);
f1_.set_protocol(cricket::ICEPROTO_RFC5245);
scoped_ptr<TransportDescription> desc(
f1_.CreateOffer(TransportOptions(), old_desc.get()));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "",
old_desc->ice_ufrag, old_desc->ice_pwd, digest_alg);
}
// Test that we can answer a GICE offer with GICE.
TEST_F(TransportDescriptionFactoryTest, TestAnswerGiceToGice) {
f1_.set_protocol(cricket::ICEPROTO_GOOGLE);
f2_.set_protocol(cricket::ICEPROTO_GOOGLE);
scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
// Should get the same result when answering as hybrid.
f2_.set_protocol(cricket::ICEPROTO_HYBRID);
desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
NULL));
CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
}
// Test that we can answer a hybrid offer with GICE.
TEST_F(TransportDescriptionFactoryTest, TestAnswerGiceToHybrid) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f2_.set_protocol(cricket::ICEPROTO_GOOGLE);
scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
}
// Test that we can answer a hybrid offer with ICE.
TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToHybrid) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f2_.set_protocol(cricket::ICEPROTO_RFC5245);
scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
// Should get the same result when answering as hybrid.
f2_.set_protocol(cricket::ICEPROTO_HYBRID);
desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
}
// Test that we can answer an ICE offer with ICE.
TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToIce) {
f1_.set_protocol(cricket::ICEPROTO_RFC5245);
f2_.set_protocol(cricket::ICEPROTO_RFC5245);
scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
// Should get the same result when answering as hybrid.
f2_.set_protocol(cricket::ICEPROTO_HYBRID);
desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
}
// Test that we can't answer a GICE offer with ICE.
TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToGice) {
f1_.set_protocol(cricket::ICEPROTO_GOOGLE);
f2_.set_protocol(cricket::ICEPROTO_RFC5245);
scoped_ptr<TransportDescription> offer(
f1_.CreateOffer(TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
ASSERT_TRUE(desc.get() == NULL);
}
// Test that we can't answer an ICE offer with GICE.
TEST_F(TransportDescriptionFactoryTest, TestAnswerGiceToIce) {
f1_.set_protocol(cricket::ICEPROTO_RFC5245);
f2_.set_protocol(cricket::ICEPROTO_GOOGLE);
scoped_ptr<TransportDescription> offer(
f1_.CreateOffer(TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
offer.get(), TransportOptions(), NULL));
ASSERT_TRUE(desc.get() == NULL);
}
// Test that we can update an answer properly; ICE credentials shouldn't change.
TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToIceReanswer) {
f1_.set_protocol(cricket::ICEPROTO_RFC5245);
f2_.set_protocol(cricket::ICEPROTO_RFC5245);
scoped_ptr<TransportDescription> offer(
f1_.CreateOffer(TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> old_desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
ASSERT_TRUE(old_desc.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(),
old_desc.get()));
ASSERT_TRUE(desc.get() != NULL);
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "",
old_desc->ice_ufrag, old_desc->ice_pwd, "");
}
// Test that we handle answering an offer with DTLS with no DTLS.
TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridToHybridDtls) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f1_.set_secure(cricket::SEC_ENABLED);
f1_.set_identity(id1_.get());
f2_.set_protocol(cricket::ICEPROTO_HYBRID);
scoped_ptr<TransportDescription> offer(
f1_.CreateOffer(TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
}
// Test that we handle answering an offer without DTLS if we have DTLS enabled,
// but fail if we require DTLS.
TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridDtlsToHybrid) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f2_.set_protocol(cricket::ICEPROTO_HYBRID);
f2_.set_secure(cricket::SEC_ENABLED);
f2_.set_identity(id2_.get());
scoped_ptr<TransportDescription> offer(
f1_.CreateOffer(TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
f2_.set_secure(cricket::SEC_REQUIRED);
desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
NULL));
ASSERT_TRUE(desc.get() == NULL);
}
// Test that we handle answering an DTLS offer with DTLS, both if we have
// DTLS enabled and required.
TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridDtlsToHybridDtls) {
f1_.set_protocol(cricket::ICEPROTO_HYBRID);
f1_.set_secure(cricket::SEC_ENABLED);
f1_.set_identity(id1_.get());
f2_.set_protocol(cricket::ICEPROTO_HYBRID);
f2_.set_secure(cricket::SEC_ENABLED);
f2_.set_identity(id2_.get());
// f2_ produces the answer that is being checked in this test, so the
// answer must contain fingerprint lines with id2_'s digest algorithm.
std::string digest_alg2;
ASSERT_TRUE(id2_->certificate().GetSignatureDigestAlgorithm(&digest_alg2));
scoped_ptr<TransportDescription> offer(
f1_.CreateOffer(TransportOptions(), NULL));
ASSERT_TRUE(offer.get() != NULL);
scoped_ptr<TransportDescription> desc(
f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", digest_alg2);
f2_.set_secure(cricket::SEC_REQUIRED);
desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
NULL));
CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", digest_alg2);
}
// Test that ice ufrag and password is changed in an updated offer and answer
// if |TransportDescriptionOptions::ice_restart| is true.
TEST_F(TransportDescriptionFactoryTest, TestIceRestart) {
TestIceRestart(false);
}
// Test that ice ufrag and password is changed in an updated offer and answer
// if |TransportDescriptionOptions::ice_restart| is true and DTLS is enabled.
TEST_F(TransportDescriptionFactoryTest, TestIceRestartWithDtls) {
TestIceRestart(true);
}

View File

@ -1,60 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#ifndef WEBRTC_P2P_BASE_TRANSPORTINFO_H_
#define WEBRTC_P2P_BASE_TRANSPORTINFO_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/transportdescription.h"
#include "webrtc/base/helpers.h"
namespace cricket {
// A TransportInfo is NOT a transport-info message. It is comparable
// to a "ContentInfo". A transport-infos message is basically just a
// collection of TransportInfos.
struct TransportInfo {
TransportInfo() {}
TransportInfo(const std::string& content_name,
const TransportDescription& description)
: content_name(content_name),
description(description) {}
std::string content_name;
TransportDescription description;
};
typedef std::vector<TransportInfo> TransportInfos;
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TRANSPORTINFO_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,254 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#ifndef WEBRTC_P2P_BASE_TURNPORT_H_
#define WEBRTC_P2P_BASE_TURNPORT_H_
#include <stdio.h>
#include <list>
#include <set>
#include <string>
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/client/basicportallocator.h"
#include "webrtc/base/asyncpacketsocket.h"
namespace rtc {
class AsyncResolver;
class SignalThread;
}
namespace cricket {
extern const char TURN_PORT_TYPE[];
class TurnAllocateRequest;
class TurnEntry;
class TurnPort : public Port {
public:
static TurnPort* Create(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
rtc::AsyncPacketSocket* socket,
const std::string& username, // ice username.
const std::string& password, // ice password.
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority) {
return new TurnPort(thread, factory, network, socket,
username, password, server_address,
credentials, server_priority);
}
static TurnPort* Create(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username, // ice username.
const std::string& password, // ice password.
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority) {
return new TurnPort(thread, factory, network, ip, min_port, max_port,
username, password, server_address, credentials,
server_priority);
}
virtual ~TurnPort();
const ProtocolAddress& server_address() const { return server_address_; }
bool connected() const { return connected_; }
const RelayCredentials& credentials() const { return credentials_; }
virtual void PrepareAddress();
virtual Connection* CreateConnection(
const Candidate& c, PortInterface::CandidateOrigin origin);
virtual int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload);
virtual int SetOption(rtc::Socket::Option opt, int value);
virtual int GetOption(rtc::Socket::Option opt, int* value);
virtual int GetError();
virtual bool HandleIncomingPacket(
rtc::AsyncPacketSocket* socket, const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
OnReadPacket(socket, data, size, remote_addr, packet_time);
return true;
}
virtual void OnReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
virtual void OnReadyToSend(rtc::AsyncPacketSocket* socket);
void OnSocketConnect(rtc::AsyncPacketSocket* socket);
void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
const std::string& hash() const { return hash_; }
const std::string& nonce() const { return nonce_; }
int error() const { return error_; }
void OnAllocateMismatch();
rtc::AsyncPacketSocket* socket() const {
return socket_;
}
// Signal with resolved server address.
// Parameters are port, server address and resolved server address.
// This signal will be sent only if server address is resolved successfully.
sigslot::signal3<TurnPort*,
const rtc::SocketAddress&,
const rtc::SocketAddress&> SignalResolvedServerAddress;
// This signal is only for testing purpose.
sigslot::signal3<TurnPort*, const rtc::SocketAddress&, int>
SignalCreatePermissionResult;
protected:
TurnPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
rtc::AsyncPacketSocket* socket,
const std::string& username,
const std::string& password,
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority);
TurnPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username,
const std::string& password,
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority);
private:
enum {
MSG_ERROR = MSG_FIRST_AVAILABLE,
MSG_ALLOCATE_MISMATCH
};
typedef std::list<TurnEntry*> EntryList;
typedef std::map<rtc::Socket::Option, int> SocketOptionsMap;
typedef std::set<rtc::SocketAddress> AttemptedServerSet;
virtual void OnMessage(rtc::Message* pmsg);
bool CreateTurnClientSocket();
void set_nonce(const std::string& nonce) { nonce_ = nonce; }
void set_realm(const std::string& realm) {
if (realm != realm_) {
realm_ = realm;
UpdateHash();
}
}
bool SetAlternateServer(const rtc::SocketAddress& address);
void ResolveTurnAddress(const rtc::SocketAddress& address);
void OnResolveResult(rtc::AsyncResolverInterface* resolver);
void AddRequestAuthInfo(StunMessage* msg);
void OnSendStunPacket(const void* data, size_t size, StunRequest* request);
// Stun address from allocate success response.
// Currently used only for testing.
void OnStunAddress(const rtc::SocketAddress& address);
void OnAllocateSuccess(const rtc::SocketAddress& address,
const rtc::SocketAddress& stun_address);
void OnAllocateError();
void OnAllocateRequestTimeout();
void HandleDataIndication(const char* data, size_t size,
const rtc::PacketTime& packet_time);
void HandleChannelData(int channel_id, const char* data, size_t size,
const rtc::PacketTime& packet_time);
void DispatchPacket(const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
ProtocolType proto, const rtc::PacketTime& packet_time);
bool ScheduleRefresh(int lifetime);
void SendRequest(StunRequest* request, int delay);
int Send(const void* data, size_t size,
const rtc::PacketOptions& options);
void UpdateHash();
bool UpdateNonce(StunMessage* response);
bool HasPermission(const rtc::IPAddress& ipaddr) const;
TurnEntry* FindEntry(const rtc::SocketAddress& address) const;
TurnEntry* FindEntry(int channel_id) const;
TurnEntry* CreateEntry(const rtc::SocketAddress& address);
void DestroyEntry(const rtc::SocketAddress& address);
void OnConnectionDestroyed(Connection* conn);
ProtocolAddress server_address_;
RelayCredentials credentials_;
AttemptedServerSet attempted_server_addresses_;
rtc::AsyncPacketSocket* socket_;
SocketOptionsMap socket_options_;
rtc::AsyncResolverInterface* resolver_;
int error_;
StunRequestManager request_manager_;
std::string realm_; // From 401/438 response message.
std::string nonce_; // From 401/438 response message.
std::string hash_; // Digest of username:realm:password
int next_channel_number_;
EntryList entries_;
bool connected_;
// By default the value will be set to 0. This value will be used in
// calculating the candidate priority.
int server_priority_;
// The number of retries made due to allocate mismatch error.
size_t allocate_mismatch_retries_;
friend class TurnEntry;
friend class TurnAllocateRequest;
friend class TurnRefreshRequest;
friend class TurnCreatePermissionRequest;
friend class TurnChannelBindRequest;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TURNPORT_H_

View File

@ -1,685 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#if defined(POSIX)
#include <dirent.h>
#endif
#include "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/tcpport.h"
#include "webrtc/p2p/base/testturnserver.h"
#include "webrtc/p2p/base/turnport.h"
#include "webrtc/p2p/base/udpport.h"
#include "webrtc/base/asynctcpsocket.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/dscp.h"
#include "webrtc/base/firewallsocketserver.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/virtualsocketserver.h"
using rtc::SocketAddress;
using cricket::Connection;
using cricket::Port;
using cricket::PortInterface;
using cricket::TurnPort;
using cricket::UDPPort;
static const SocketAddress kLocalAddr1("11.11.11.11", 0);
static const SocketAddress kLocalAddr2("22.22.22.22", 0);
static const SocketAddress kLocalIPv6Addr(
"2401:fa00:4:1000:be30:5bff:fee5:c3", 0);
static const SocketAddress kTurnUdpIntAddr("99.99.99.3",
cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnTcpIntAddr("99.99.99.4",
cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0);
static const SocketAddress kTurnAlternateUdpIntAddr(
"99.99.99.6", cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnUdpIPv6IntAddr(
"2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnUdpIPv6ExtAddr(
"2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0);
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIceUfrag2[] = "TESTICEUFRAG0002";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
static const char kTurnUsername[] = "test";
static const char kTurnPassword[] = "test";
static const unsigned int kTimeout = 1000;
static const cricket::ProtocolAddress kTurnUdpProtoAddr(
kTurnUdpIntAddr, cricket::PROTO_UDP);
static const cricket::ProtocolAddress kTurnTcpProtoAddr(
kTurnTcpIntAddr, cricket::PROTO_TCP);
static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr(
kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
static const unsigned int MSG_TESTFINISH = 0;
#if defined(LINUX)
static int GetFDCount() {
struct dirent *dp;
int fd_count = 0;
DIR *dir = opendir("/proc/self/fd/");
while ((dp = readdir(dir)) != NULL) {
if (dp->d_name[0] == '.')
continue;
++fd_count;
}
closedir(dir);
return fd_count;
}
#endif
class TurnPortTest : public testing::Test,
public sigslot::has_slots<>,
public rtc::MessageHandler {
public:
TurnPortTest()
: main_(rtc::Thread::Current()),
pss_(new rtc::PhysicalSocketServer),
ss_(new rtc::VirtualSocketServer(pss_.get())),
ss_scope_(ss_.get()),
network_("unittest", "unittest", rtc::IPAddress(INADDR_ANY), 32),
socket_factory_(rtc::Thread::Current()),
turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr),
turn_ready_(false),
turn_error_(false),
turn_unknown_address_(false),
turn_create_permission_success_(false),
udp_ready_(false),
test_finish_(false) {
network_.AddIP(rtc::IPAddress(INADDR_ANY));
}
virtual void OnMessage(rtc::Message* msg) {
ASSERT(msg->message_id == MSG_TESTFINISH);
if (msg->message_id == MSG_TESTFINISH)
test_finish_ = true;
}
void OnTurnPortComplete(Port* port) {
turn_ready_ = true;
}
void OnTurnPortError(Port* port) {
turn_error_ = true;
}
void OnTurnUnknownAddress(PortInterface* port, const SocketAddress& addr,
cricket::ProtocolType proto,
cricket::IceMessage* msg, const std::string& rf,
bool /*port_muxed*/) {
turn_unknown_address_ = true;
}
void OnTurnCreatePermissionResult(TurnPort* port, const SocketAddress& addr,
int code) {
// Ignoring the address.
if (code == 0) {
turn_create_permission_success_ = true;
}
}
void OnTurnReadPacket(Connection* conn, const char* data, size_t size,
const rtc::PacketTime& packet_time) {
turn_packets_.push_back(rtc::Buffer(data, size));
}
void OnUdpPortComplete(Port* port) {
udp_ready_ = true;
}
void OnUdpReadPacket(Connection* conn, const char* data, size_t size,
const rtc::PacketTime& packet_time) {
udp_packets_.push_back(rtc::Buffer(data, size));
}
void OnSocketReadPacket(rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
turn_port_->HandleIncomingPacket(socket, data, size, remote_addr,
packet_time);
}
rtc::AsyncSocket* CreateServerSocket(const SocketAddress addr) {
rtc::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM);
EXPECT_GE(socket->Bind(addr), 0);
EXPECT_GE(socket->Listen(5), 0);
return socket;
}
void CreateTurnPort(const std::string& username,
const std::string& password,
const cricket::ProtocolAddress& server_address) {
CreateTurnPort(kLocalAddr1, username, password, server_address);
}
void CreateTurnPort(const rtc::SocketAddress& local_address,
const std::string& username,
const std::string& password,
const cricket::ProtocolAddress& server_address) {
cricket::RelayCredentials credentials(username, password);
turn_port_.reset(TurnPort::Create(main_, &socket_factory_, &network_,
local_address.ipaddr(), 0, 0,
kIceUfrag1, kIcePwd1,
server_address, credentials, 0));
// Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be
// in Hybrid mode. Protocol type is necessary to send correct type STUN ping
// messages.
// This TURN port will be the controlling.
turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING);
ConnectSignals();
}
void CreateSharedTurnPort(const std::string& username,
const std::string& password,
const cricket::ProtocolAddress& server_address) {
ASSERT(server_address.proto == cricket::PROTO_UDP);
if (!socket_) {
socket_.reset(socket_factory_.CreateUdpSocket(
rtc::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0));
ASSERT_TRUE(socket_ != NULL);
socket_->SignalReadPacket.connect(
this, &TurnPortTest::OnSocketReadPacket);
}
cricket::RelayCredentials credentials(username, password);
turn_port_.reset(cricket::TurnPort::Create(
main_, &socket_factory_, &network_, socket_.get(),
kIceUfrag1, kIcePwd1, server_address, credentials, 0));
// Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be
// in Hybrid mode. Protocol type is necessary to send correct type STUN ping
// messages.
// This TURN port will be the controlling.
turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING);
ConnectSignals();
}
void ConnectSignals() {
turn_port_->SignalPortComplete.connect(this,
&TurnPortTest::OnTurnPortComplete);
turn_port_->SignalPortError.connect(this,
&TurnPortTest::OnTurnPortError);
turn_port_->SignalUnknownAddress.connect(this,
&TurnPortTest::OnTurnUnknownAddress);
turn_port_->SignalCreatePermissionResult.connect(this,
&TurnPortTest::OnTurnCreatePermissionResult);
}
void CreateUdpPort() {
udp_port_.reset(UDPPort::Create(main_, &socket_factory_, &network_,
kLocalAddr2.ipaddr(), 0, 0,
kIceUfrag2, kIcePwd2));
// Set protocol type to RFC5245, as turn port is also in same mode.
// UDP port will be controlled.
udp_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
udp_port_->SetIceRole(cricket::ICEROLE_CONTROLLED);
udp_port_->SignalPortComplete.connect(
this, &TurnPortTest::OnUdpPortComplete);
}
void TestTurnConnection() {
// Create ports and prepare addresses.
ASSERT_TRUE(turn_port_ != NULL);
turn_port_->PrepareAddress();
ASSERT_TRUE_WAIT(turn_ready_, kTimeout);
CreateUdpPort();
udp_port_->PrepareAddress();
ASSERT_TRUE_WAIT(udp_ready_, kTimeout);
// Send ping from UDP to TURN.
Connection* conn1 = udp_port_->CreateConnection(
turn_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
ASSERT_TRUE(conn1 != NULL);
conn1->Ping(0);
WAIT(!turn_unknown_address_, kTimeout);
EXPECT_FALSE(turn_unknown_address_);
EXPECT_EQ(Connection::STATE_READ_INIT, conn1->read_state());
EXPECT_EQ(Connection::STATE_WRITE_INIT, conn1->write_state());
// Send ping from TURN to UDP.
Connection* conn2 = turn_port_->CreateConnection(
udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
ASSERT_TRUE(conn2 != NULL);
ASSERT_TRUE_WAIT(turn_create_permission_success_, kTimeout);
conn2->Ping(0);
EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), kTimeout);
EXPECT_EQ(Connection::STATE_READABLE, conn1->read_state());
EXPECT_EQ(Connection::STATE_READ_INIT, conn2->read_state());
EXPECT_EQ(Connection::STATE_WRITE_INIT, conn1->write_state());
// Send another ping from UDP to TURN.
conn1->Ping(0);
EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), kTimeout);
EXPECT_EQ(Connection::STATE_READABLE, conn2->read_state());
}
void TestTurnSendData() {
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
CreateUdpPort();
udp_port_->PrepareAddress();
EXPECT_TRUE_WAIT(udp_ready_, kTimeout);
// Create connections and send pings.
Connection* conn1 = turn_port_->CreateConnection(
udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
Connection* conn2 = udp_port_->CreateConnection(
turn_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
ASSERT_TRUE(conn1 != NULL);
ASSERT_TRUE(conn2 != NULL);
conn1->SignalReadPacket.connect(static_cast<TurnPortTest*>(this),
&TurnPortTest::OnTurnReadPacket);
conn2->SignalReadPacket.connect(static_cast<TurnPortTest*>(this),
&TurnPortTest::OnUdpReadPacket);
conn1->Ping(0);
EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn1->write_state(), kTimeout);
conn2->Ping(0);
EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, conn2->write_state(), kTimeout);
// Send some data.
size_t num_packets = 256;
for (size_t i = 0; i < num_packets; ++i) {
unsigned char buf[256] = { 0 };
for (size_t j = 0; j < i + 1; ++j) {
buf[j] = 0xFF - static_cast<unsigned char>(j);
}
conn1->Send(buf, i + 1, options);
conn2->Send(buf, i + 1, options);
main_->ProcessMessages(0);
}
// Check the data.
ASSERT_EQ_WAIT(num_packets, turn_packets_.size(), kTimeout);
ASSERT_EQ_WAIT(num_packets, udp_packets_.size(), kTimeout);
for (size_t i = 0; i < num_packets; ++i) {
EXPECT_EQ(i + 1, turn_packets_[i].length());
EXPECT_EQ(i + 1, udp_packets_[i].length());
EXPECT_EQ(turn_packets_[i], udp_packets_[i]);
}
}
protected:
rtc::Thread* main_;
rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
rtc::scoped_ptr<rtc::VirtualSocketServer> ss_;
rtc::SocketServerScope ss_scope_;
rtc::Network network_;
rtc::BasicPacketSocketFactory socket_factory_;
rtc::scoped_ptr<rtc::AsyncPacketSocket> socket_;
cricket::TestTurnServer turn_server_;
rtc::scoped_ptr<TurnPort> turn_port_;
rtc::scoped_ptr<UDPPort> udp_port_;
bool turn_ready_;
bool turn_error_;
bool turn_unknown_address_;
bool turn_create_permission_success_;
bool udp_ready_;
bool test_finish_;
std::vector<rtc::Buffer> turn_packets_;
std::vector<rtc::Buffer> udp_packets_;
rtc::PacketOptions options;
};
// Do a normal TURN allocation.
TEST_F(TurnPortTest, TestTurnAllocate) {
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10*1024));
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
ASSERT_EQ(1U, turn_port_->Candidates().size());
EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
turn_port_->Candidates()[0].address().ipaddr());
EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
}
// Testing a normal UDP allocation using TCP connection.
TEST_F(TurnPortTest, TestTurnTcpAllocate) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10*1024));
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
ASSERT_EQ(1U, turn_port_->Candidates().size());
EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
turn_port_->Candidates()[0].address().ipaddr());
EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
}
// Testing turn port will attempt to create TCP socket on address resolution
// failure.
TEST_F(TurnPortTest, DISABLED_TestTurnTcpOnAddressResolveFailure) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress(
rtc::SocketAddress("www.webrtc-blah-blah.com", 3478),
cricket::PROTO_TCP));
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
// As VSS doesn't provide a DNS resolution, name resolve will fail. TurnPort
// will proceed in creating a TCP socket which will fail as there is no
// server on the above domain and error will be set to SOCKET_ERROR.
EXPECT_EQ(SOCKET_ERROR, turn_port_->error());
}
// In case of UDP on address resolve failure, TurnPort will not create socket
// and return allocate failure.
TEST_F(TurnPortTest, DISABLED_TestTurnUdpOnAdressResolveFailure) {
CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress(
rtc::SocketAddress("www.webrtc-blah-blah.com", 3478),
cricket::PROTO_UDP));
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
// Error from turn port will not be socket error.
EXPECT_NE(SOCKET_ERROR, turn_port_->error());
}
// Try to do a TURN allocation with an invalid password.
TEST_F(TurnPortTest, TestTurnAllocateBadPassword) {
CreateTurnPort(kTurnUsername, "bad", kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
ASSERT_EQ(0U, turn_port_->Candidates().size());
}
// Tests that a new local address is created after
// STUN_ERROR_ALLOCATION_MISMATCH.
TEST_F(TurnPortTest, TestTurnAllocateMismatch) {
// Do a normal allocation first.
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
// Forces the socket server to assign the same port.
ss_->SetNextPortForTesting(first_addr.port());
turn_ready_ = false;
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
// Verifies that the new port has the same address.
EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress());
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
// Verifies that the new port has a different address now.
EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
}
// Tests that a shared-socket-TurnPort creates its own socket after
// STUN_ERROR_ALLOCATION_MISMATCH.
TEST_F(TurnPortTest, TestSharedSocketAllocateMismatch) {
// Do a normal allocation first.
CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
turn_ready_ = false;
CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
// Verifies that the new port has the same address.
EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress());
EXPECT_TRUE(turn_port_->SharedSocket());
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
// Verifies that the new port has a different address now.
EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
EXPECT_FALSE(turn_port_->SharedSocket());
}
TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
// Do a normal allocation first.
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
// Forces the socket server to assign the same port.
ss_->SetNextPortForTesting(first_addr.port());
turn_ready_ = false;
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
turn_port_->PrepareAddress();
// Verifies that the new port has the same address.
EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress());
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
// Verifies that the new port has a different address now.
EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
}
// Do a TURN allocation and try to send a packet to it from the outside.
// The packet should be dropped. Then, try to send a packet from TURN to the
// outside. It should reach its destination. Finally, try again from the
// outside. It should now work as well.
TEST_F(TurnPortTest, TestTurnConnection) {
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
TestTurnConnection();
}
// Similar to above, except that this test will use the shared socket.
TEST_F(TurnPortTest, TestTurnConnectionUsingSharedSocket) {
CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
TestTurnConnection();
}
// Test that we can establish a TCP connection with TURN server.
TEST_F(TurnPortTest, TestTurnTcpConnection) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
TestTurnConnection();
}
// Test that we fail to create a connection when we want to use TLS over TCP.
// This test should be removed once we have TLS support.
TEST_F(TurnPortTest, TestTurnTlsTcpConnectionFails) {
cricket::ProtocolAddress secure_addr(kTurnTcpProtoAddr.address,
kTurnTcpProtoAddr.proto,
true);
CreateTurnPort(kTurnUsername, kTurnPassword, secure_addr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
ASSERT_EQ(0U, turn_port_->Candidates().size());
}
// Test try-alternate-server feature.
TEST_F(TurnPortTest, TestTurnAlternateServer) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
cricket::PROTO_UDP);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
// Retrieve the address before we run the state machine.
const SocketAddress old_addr = turn_port_->server_address().address;
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
// Retrieve the address again, the turn port's address should be
// changed.
const SocketAddress new_addr = turn_port_->server_address().address;
EXPECT_NE(old_addr, new_addr);
ASSERT_EQ(1U, turn_port_->Candidates().size());
EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
turn_port_->Candidates()[0].address().ipaddr());
EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
}
// Test that we fail when we redirect to an address different from
// current IP family.
TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnUdpIPv6IntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
cricket::PROTO_UDP);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
}
// Test that we fail to handle alternate-server response over TCP protocol.
TEST_F(TurnPortTest, TestTurnAlternateServerTcp) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.set_redirect_hook(&redirector);
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr, cricket::PROTO_TCP);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
}
// Test try-alternate-server catches the case of pingpong.
TEST_F(TurnPortTest, TestTurnAlternateServerPingPong) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
redirect_addresses.push_back(kTurnUdpIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
cricket::PROTO_UDP);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
ASSERT_EQ(0U, turn_port_->Candidates().size());
rtc::SocketAddress address;
// Verify that we have exhausted all alternate servers instead of
// failure caused by other errors.
EXPECT_FALSE(redirector.ShouldRedirect(address, &address));
}
// Test try-alternate-server catch the case of repeated server.
TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetition) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
cricket::PROTO_UDP);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
ASSERT_EQ(0U, turn_port_->Candidates().size());
}
// Run TurnConnectionTest with one-time-use nonce feature.
// Here server will send a 438 STALE_NONCE error message for
// every TURN transaction.
TEST_F(TurnPortTest, TestTurnConnectionUsingOTUNonce) {
turn_server_.set_enable_otu_nonce(true);
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
TestTurnConnection();
}
// Do a TURN allocation, establish a UDP connection, and send some data.
TEST_F(TurnPortTest, TestTurnSendDataTurnUdpToUdp) {
// Create ports and prepare addresses.
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
TestTurnSendData();
}
// Do a TURN allocation, establish a TCP connection, and send some data.
TEST_F(TurnPortTest, TestTurnSendDataTurnTcpToUdp) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
// Create ports and prepare addresses.
CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
TestTurnSendData();
}
// Test TURN fails to make a connection from IPv6 address to a server which has
// IPv4 address.
TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv4) {
turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
kTurnUdpProtoAddr);
turn_port_->PrepareAddress();
ASSERT_TRUE_WAIT(turn_error_, kTimeout);
EXPECT_TRUE(turn_port_->Candidates().empty());
}
// Test TURN make a connection from IPv6 address to a server which has
// IPv6 intenal address. But in this test external address is a IPv4 address,
// hence allocated address will be a IPv4 address.
TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) {
turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
kTurnUdpIPv6ProtoAddr);
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
ASSERT_EQ(1U, turn_port_->Candidates().size());
EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
turn_port_->Candidates()[0].address().ipaddr());
EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
}
// This test verifies any FD's are not leaked after TurnPort is destroyed.
// https://code.google.com/p/webrtc/issues/detail?id=2651
#if defined(LINUX)
TEST_F(TurnPortTest, TestResolverShutdown) {
turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
int last_fd_count = GetFDCount();
// Need to supply unresolved address to kick off resolver.
CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
cricket::ProtocolAddress(rtc::SocketAddress(
"stun.l.google.com", 3478), cricket::PROTO_UDP));
turn_port_->PrepareAddress();
ASSERT_TRUE_WAIT(turn_error_, kTimeout);
EXPECT_TRUE(turn_port_->Candidates().empty());
turn_port_.reset();
rtc::Thread::Current()->Post(this, MSG_TESTFINISH);
// Waiting for above message to be processed.
ASSERT_TRUE_WAIT(test_finish_, kTimeout);
EXPECT_EQ(last_fd_count, GetFDCount());
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,207 +0,0 @@
/*
* libjingle
* Copyright 2012, 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.
*/
#ifndef WEBRTC_P2P_BASE_TURNSERVER_H_
#define WEBRTC_P2P_BASE_TURNSERVER_H_
#include <list>
#include <map>
#include <set>
#include <string>
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socketaddress.h"
namespace rtc {
class ByteBuffer;
class PacketSocketFactory;
class Thread;
}
namespace cricket {
class StunMessage;
class TurnMessage;
// The default server port for TURN, as specified in RFC5766.
const int TURN_SERVER_PORT = 3478;
// An interface through which the MD5 credential hash can be retrieved.
class TurnAuthInterface {
public:
// Gets HA1 for the specified user and realm.
// HA1 = MD5(A1) = MD5(username:realm:password).
// Return true if the given username and realm are valid, or false if not.
virtual bool GetKey(const std::string& username, const std::string& realm,
std::string* key) = 0;
};
// An interface enables Turn Server to control redirection behavior.
class TurnRedirectInterface {
public:
virtual bool ShouldRedirect(const rtc::SocketAddress& address,
rtc::SocketAddress* out) = 0;
virtual ~TurnRedirectInterface() {}
};
// The core TURN server class. Give it a socket to listen on via
// AddInternalServerSocket, and a factory to create external sockets via
// SetExternalSocketFactory, and it's ready to go.
// Not yet wired up: TCP support.
class TurnServer : public sigslot::has_slots<> {
public:
explicit TurnServer(rtc::Thread* thread);
~TurnServer();
// Gets/sets the realm value to use for the server.
const std::string& realm() const { return realm_; }
void set_realm(const std::string& realm) { realm_ = realm; }
// Gets/sets the value for the SOFTWARE attribute for TURN messages.
const std::string& software() const { return software_; }
void set_software(const std::string& software) { software_ = software; }
// Sets the authentication callback; does not take ownership.
void set_auth_hook(TurnAuthInterface* auth_hook) { auth_hook_ = auth_hook; }
void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
redirect_hook_ = redirect_hook;
}
void set_enable_otu_nonce(bool enable) { enable_otu_nonce_ = enable; }
// Starts listening for packets from internal clients.
void AddInternalSocket(rtc::AsyncPacketSocket* socket,
ProtocolType proto);
// Starts listening for the connections on this socket. When someone tries
// to connect, the connection will be accepted and a new internal socket
// will be added.
void AddInternalServerSocket(rtc::AsyncSocket* socket,
ProtocolType proto);
// Specifies the factory to use for creating external sockets.
void SetExternalSocketFactory(rtc::PacketSocketFactory* factory,
const rtc::SocketAddress& address);
private:
// Encapsulates the client's connection to the server.
class Connection {
public:
Connection() : proto_(PROTO_UDP), socket_(NULL) {}
Connection(const rtc::SocketAddress& src,
ProtocolType proto,
rtc::AsyncPacketSocket* socket);
const rtc::SocketAddress& src() const { return src_; }
rtc::AsyncPacketSocket* socket() { return socket_; }
bool operator==(const Connection& t) const;
bool operator<(const Connection& t) const;
std::string ToString() const;
private:
rtc::SocketAddress src_;
rtc::SocketAddress dst_;
cricket::ProtocolType proto_;
rtc::AsyncPacketSocket* socket_;
};
class Allocation;
class Permission;
class Channel;
typedef std::map<Connection, Allocation*> AllocationMap;
void OnInternalPacket(rtc::AsyncPacketSocket* socket, const char* data,
size_t size, const rtc::SocketAddress& address,
const rtc::PacketTime& packet_time);
void OnNewInternalConnection(rtc::AsyncSocket* socket);
// Accept connections on this server socket.
void AcceptConnection(rtc::AsyncSocket* server_socket);
void OnInternalSocketClose(rtc::AsyncPacketSocket* socket, int err);
void HandleStunMessage(Connection* conn, const char* data, size_t size);
void HandleBindingRequest(Connection* conn, const StunMessage* msg);
void HandleAllocateRequest(Connection* conn, const TurnMessage* msg,
const std::string& key);
bool GetKey(const StunMessage* msg, std::string* key);
bool CheckAuthorization(Connection* conn, const StunMessage* msg,
const char* data, size_t size,
const std::string& key);
std::string GenerateNonce() const;
bool ValidateNonce(const std::string& nonce) const;
Allocation* FindAllocation(Connection* conn);
Allocation* CreateAllocation(Connection* conn, int proto,
const std::string& key);
void SendErrorResponse(Connection* conn, const StunMessage* req,
int code, const std::string& reason);
void SendErrorResponseWithRealmAndNonce(Connection* conn,
const StunMessage* req,
int code,
const std::string& reason);
void SendErrorResponseWithAlternateServer(Connection* conn,
const StunMessage* req,
const rtc::SocketAddress& addr);
void SendStun(Connection* conn, StunMessage* msg);
void Send(Connection* conn, const rtc::ByteBuffer& buf);
void OnAllocationDestroyed(Allocation* allocation);
void DestroyInternalSocket(rtc::AsyncPacketSocket* socket);
typedef std::map<rtc::AsyncPacketSocket*,
ProtocolType> InternalSocketMap;
typedef std::map<rtc::AsyncSocket*,
ProtocolType> ServerSocketMap;
rtc::Thread* thread_;
std::string nonce_key_;
std::string realm_;
std::string software_;
TurnAuthInterface* auth_hook_;
TurnRedirectInterface* redirect_hook_;
// otu - one-time-use. Server will respond with 438 if it's
// sees the same nonce in next transaction.
bool enable_otu_nonce_;
InternalSocketMap server_sockets_;
ServerSocketMap server_listen_sockets_;
rtc::scoped_ptr<rtc::PacketSocketFactory>
external_socket_factory_;
rtc::SocketAddress external_addr_;
AllocationMap allocations_;
};
} // namespace cricket
#endif // WEBRTC_P2P_BASE_TURNSERVER_H_

View File

@ -1,34 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_BASE_UDPPORT_H_
#define WEBRTC_P2P_BASE_UDPPORT_H_
// StunPort will be handling UDPPort functionality.
#include "webrtc/p2p/base/stunport.h"
#endif // WEBRTC_P2P_BASE_UDPPORT_H_

View File

@ -1,69 +0,0 @@
/*
* libjingle
* Copyright 2010, 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.
*/
#ifndef WEBRTC_P2P_CLIENT_AUTOPORTALLOCATOR_H_
#define WEBRTC_P2P_CLIENT_AUTOPORTALLOCATOR_H_
#include <string>
#include <vector>
#include "webrtc/p2p/client/httpportallocator.h"
#include "webrtc/libjingle/xmpp/jingleinfotask.h"
#include "webrtc/libjingle/xmpp/xmppclient.h"
#include "webrtc/base/sigslot.h"
// This class sets the relay and stun servers using XmppClient.
// It enables the client to traverse Proxy and NAT.
class AutoPortAllocator : public cricket::HttpPortAllocator {
public:
AutoPortAllocator(rtc::NetworkManager* network_manager,
const std::string& user_agent)
: cricket::HttpPortAllocator(network_manager, user_agent) {
}
// Creates and initiates a task to get relay token from XmppClient and set
// it appropriately.
void SetXmppClient(buzz::XmppClient* client) {
// The JingleInfoTask is freed by the task-runner.
buzz::JingleInfoTask* jit = new buzz::JingleInfoTask(client);
jit->SignalJingleInfo.connect(this, &AutoPortAllocator::OnJingleInfo);
jit->Start();
jit->RefreshJingleInfoNow();
}
private:
void OnJingleInfo(
const std::string& token,
const std::vector<std::string>& relay_hosts,
const std::vector<rtc::SocketAddress>& stun_hosts) {
SetRelayToken(token);
SetStunHosts(stun_hosts);
SetRelayHosts(relay_hosts);
}
};
#endif // WEBRTC_P2P_CLIENT_AUTOPORTALLOCATOR_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,258 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_P2P_CLIENT_BASICPORTALLOCATOR_H_
#define WEBRTC_P2P_CLIENT_BASICPORTALLOCATOR_H_
#include <string>
#include <vector>
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/network.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/thread.h"
namespace cricket {
struct RelayCredentials {
RelayCredentials() {}
RelayCredentials(const std::string& username,
const std::string& password)
: username(username),
password(password) {
}
std::string username;
std::string password;
};
typedef std::vector<ProtocolAddress> PortList;
struct RelayServerConfig {
RelayServerConfig(RelayType type) : type(type), priority(0) {}
RelayType type;
PortList ports;
RelayCredentials credentials;
int priority;
};
class BasicPortAllocator : public PortAllocator {
public:
BasicPortAllocator(rtc::NetworkManager* network_manager,
rtc::PacketSocketFactory* socket_factory);
explicit BasicPortAllocator(rtc::NetworkManager* network_manager);
BasicPortAllocator(rtc::NetworkManager* network_manager,
rtc::PacketSocketFactory* socket_factory,
const ServerAddresses& stun_servers);
BasicPortAllocator(rtc::NetworkManager* network_manager,
const ServerAddresses& stun_servers,
const rtc::SocketAddress& relay_server_udp,
const rtc::SocketAddress& relay_server_tcp,
const rtc::SocketAddress& relay_server_ssl);
virtual ~BasicPortAllocator();
rtc::NetworkManager* network_manager() { return network_manager_; }
// If socket_factory() is set to NULL each PortAllocatorSession
// creates its own socket factory.
rtc::PacketSocketFactory* socket_factory() { return socket_factory_; }
const ServerAddresses& stun_servers() const {
return stun_servers_;
}
const std::vector<RelayServerConfig>& relays() const {
return relays_;
}
virtual void AddRelay(const RelayServerConfig& relay) {
relays_.push_back(relay);
}
virtual PortAllocatorSession* CreateSessionInternal(
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd);
private:
void Construct();
rtc::NetworkManager* network_manager_;
rtc::PacketSocketFactory* socket_factory_;
const ServerAddresses stun_servers_;
std::vector<RelayServerConfig> relays_;
bool allow_tcp_listen_;
};
struct PortConfiguration;
class AllocationSequence;
class BasicPortAllocatorSession : public PortAllocatorSession,
public rtc::MessageHandler {
public:
BasicPortAllocatorSession(BasicPortAllocator* allocator,
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd);
~BasicPortAllocatorSession();
virtual BasicPortAllocator* allocator() { return allocator_; }
rtc::Thread* network_thread() { return network_thread_; }
rtc::PacketSocketFactory* socket_factory() { return socket_factory_; }
virtual void StartGettingPorts();
virtual void StopGettingPorts();
virtual bool IsGettingPorts() { return running_; }
protected:
// Starts the process of getting the port configurations.
virtual void GetPortConfigurations();
// Adds a port configuration that is now ready. Once we have one for each
// network (or a timeout occurs), we will start allocating ports.
virtual void ConfigReady(PortConfiguration* config);
// MessageHandler. Can be overriden if message IDs do not conflict.
virtual void OnMessage(rtc::Message *message);
private:
class PortData {
public:
PortData() : port_(NULL), sequence_(NULL), state_(STATE_INIT) {}
PortData(Port* port, AllocationSequence* seq)
: port_(port), sequence_(seq), state_(STATE_INIT) {
}
Port* port() { return port_; }
AllocationSequence* sequence() { return sequence_; }
bool ready() const { return state_ == STATE_READY; }
bool complete() const {
// Returns true if candidate allocation has completed one way or another.
return ((state_ == STATE_COMPLETE) || (state_ == STATE_ERROR));
}
void set_ready() { ASSERT(state_ == STATE_INIT); state_ = STATE_READY; }
void set_complete() {
state_ = STATE_COMPLETE;
}
void set_error() {
ASSERT(state_ == STATE_INIT || state_ == STATE_READY);
state_ = STATE_ERROR;
}
private:
enum State {
STATE_INIT, // No candidates allocated yet.
STATE_READY, // At least one candidate is ready for process.
STATE_COMPLETE, // All candidates allocated and ready for process.
STATE_ERROR // Error in gathering candidates.
};
Port* port_;
AllocationSequence* sequence_;
State state_;
};
void OnConfigReady(PortConfiguration* config);
void OnConfigStop();
void AllocatePorts();
void OnAllocate();
void DoAllocate();
void OnNetworksChanged();
void OnAllocationSequenceObjectsCreated();
void DisableEquivalentPhases(rtc::Network* network,
PortConfiguration* config, uint32* flags);
void AddAllocatedPort(Port* port, AllocationSequence* seq,
bool prepare_address);
void OnCandidateReady(Port* port, const Candidate& c);
void OnPortComplete(Port* port);
void OnPortError(Port* port);
void OnProtocolEnabled(AllocationSequence* seq, ProtocolType proto);
void OnPortDestroyed(PortInterface* port);
void OnShake();
void MaybeSignalCandidatesAllocationDone();
void OnPortAllocationComplete(AllocationSequence* seq);
PortData* FindPort(Port* port);
bool CheckCandidateFilter(const Candidate& c);
BasicPortAllocator* allocator_;
rtc::Thread* network_thread_;
rtc::scoped_ptr<rtc::PacketSocketFactory> owned_socket_factory_;
rtc::PacketSocketFactory* socket_factory_;
bool allocation_started_;
bool network_manager_started_;
bool running_; // set when StartGetAllPorts is called
bool allocation_sequences_created_;
std::vector<PortConfiguration*> configs_;
std::vector<AllocationSequence*> sequences_;
std::vector<PortData> ports_;
friend class AllocationSequence;
};
// Records configuration information useful in creating ports.
struct PortConfiguration : public rtc::MessageData {
// TODO(jiayl): remove |stun_address| when Chrome is updated.
rtc::SocketAddress stun_address;
ServerAddresses stun_servers;
std::string username;
std::string password;
typedef std::vector<RelayServerConfig> RelayList;
RelayList relays;
// TODO(jiayl): remove this ctor when Chrome is updated.
PortConfiguration(const rtc::SocketAddress& stun_address,
const std::string& username,
const std::string& password);
PortConfiguration(const ServerAddresses& stun_servers,
const std::string& username,
const std::string& password);
// TODO(jiayl): remove when |stun_address| is removed.
ServerAddresses StunServers();
// Adds another relay server, with the given ports and modifier, to the list.
void AddRelay(const RelayServerConfig& config);
// Determines whether the given relay server supports the given protocol.
bool SupportsProtocol(const RelayServerConfig& relay,
ProtocolType type) const;
bool SupportsProtocol(RelayType turn_type, ProtocolType type) const;
// Helper method returns the server addresses for the matching RelayType and
// Protocol type.
ServerAddresses GetRelayServerAddresses(
RelayType turn_type, ProtocolType type) const;
};
} // namespace cricket
#endif // WEBRTC_P2P_CLIENT_BASICPORTALLOCATOR_H_

View File

@ -1,584 +0,0 @@
/*
* libjingle
* Copyright 2011, 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 <string>
#include "webrtc/p2p/client/connectivitychecker.h"
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/common.h"
#include "webrtc/p2p/base/constants.h"
#include "webrtc/p2p/base/port.h"
#include "webrtc/p2p/base/relayport.h"
#include "webrtc/p2p/base/stunport.h"
#include "webrtc/base/asynchttprequest.h"
#include "webrtc/base/autodetectproxy.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/httpcommon-inl.h"
#include "webrtc/base/httpcommon.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/proxydetect.h"
#include "webrtc/base/thread.h"
namespace cricket {
static const char kDefaultStunHostname[] = "stun.l.google.com";
static const int kDefaultStunPort = 19302;
// Default maximum time in milliseconds we will wait for connections.
static const uint32 kDefaultTimeoutMs = 3000;
enum {
MSG_START = 1,
MSG_STOP = 2,
MSG_TIMEOUT = 3,
MSG_SIGNAL_RESULTS = 4
};
class TestHttpPortAllocator : public HttpPortAllocator {
public:
TestHttpPortAllocator(rtc::NetworkManager* network_manager,
const std::string& user_agent,
const std::string& relay_token) :
HttpPortAllocator(network_manager, user_agent) {
SetRelayToken(relay_token);
}
PortAllocatorSession* CreateSessionInternal(
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd) {
return new TestHttpPortAllocatorSession(this, content_name, component,
ice_ufrag, ice_pwd,
stun_hosts(), relay_hosts(),
relay_token(), user_agent());
}
};
void TestHttpPortAllocatorSession::ConfigReady(PortConfiguration* config) {
SignalConfigReady(username(), password(), config, proxy_);
delete config;
}
void TestHttpPortAllocatorSession::OnRequestDone(
rtc::SignalThread* data) {
rtc::AsyncHttpRequest* request =
static_cast<rtc::AsyncHttpRequest*>(data);
// Tell the checker that the request is complete.
SignalRequestDone(request);
// Pass on the response to super class.
HttpPortAllocatorSession::OnRequestDone(data);
}
ConnectivityChecker::ConnectivityChecker(
rtc::Thread* worker,
const std::string& jid,
const std::string& session_id,
const std::string& user_agent,
const std::string& relay_token,
const std::string& connection)
: worker_(worker),
jid_(jid),
session_id_(session_id),
user_agent_(user_agent),
relay_token_(relay_token),
connection_(connection),
proxy_detect_(NULL),
timeout_ms_(kDefaultTimeoutMs),
stun_address_(kDefaultStunHostname, kDefaultStunPort),
started_(false) {
}
ConnectivityChecker::~ConnectivityChecker() {
if (started_) {
// We try to clear the TIMEOUT below. But worker may still handle it and
// cause SignalCheckDone to happen on main-thread. So we finally clear any
// pending SIGNAL_RESULTS.
worker_->Clear(this, MSG_TIMEOUT);
worker_->Send(this, MSG_STOP);
nics_.clear();
main_->Clear(this, MSG_SIGNAL_RESULTS);
}
}
bool ConnectivityChecker::Initialize() {
network_manager_.reset(CreateNetworkManager());
socket_factory_.reset(CreateSocketFactory(worker_));
port_allocator_.reset(CreatePortAllocator(network_manager_.get(),
user_agent_, relay_token_));
uint32 new_allocator_flags = port_allocator_->flags();
new_allocator_flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG;
port_allocator_->set_flags(new_allocator_flags);
return true;
}
void ConnectivityChecker::Start() {
main_ = rtc::Thread::Current();
worker_->Post(this, MSG_START);
started_ = true;
}
void ConnectivityChecker::CleanUp() {
ASSERT(worker_ == rtc::Thread::Current());
if (proxy_detect_) {
proxy_detect_->Release();
proxy_detect_ = NULL;
}
for (uint32 i = 0; i < sessions_.size(); ++i) {
delete sessions_[i];
}
sessions_.clear();
for (uint32 i = 0; i < ports_.size(); ++i) {
delete ports_[i];
}
ports_.clear();
}
bool ConnectivityChecker::AddNic(const rtc::IPAddress& ip,
const rtc::SocketAddress& proxy_addr) {
NicMap::iterator i = nics_.find(NicId(ip, proxy_addr));
if (i != nics_.end()) {
// Already have it.
return false;
}
uint32 now = rtc::Time();
NicInfo info;
info.ip = ip;
info.proxy_info = GetProxyInfo();
info.stun.start_time_ms = now;
nics_.insert(std::pair<NicId, NicInfo>(NicId(ip, proxy_addr), info));
return true;
}
void ConnectivityChecker::SetProxyInfo(const rtc::ProxyInfo& proxy_info) {
port_allocator_->set_proxy(user_agent_, proxy_info);
AllocatePorts();
}
rtc::ProxyInfo ConnectivityChecker::GetProxyInfo() const {
rtc::ProxyInfo proxy_info;
if (proxy_detect_) {
proxy_info = proxy_detect_->proxy();
}
return proxy_info;
}
void ConnectivityChecker::CheckNetworks() {
network_manager_->SignalNetworksChanged.connect(
this, &ConnectivityChecker::OnNetworksChanged);
network_manager_->StartUpdating();
}
void ConnectivityChecker::OnMessage(rtc::Message *msg) {
switch (msg->message_id) {
case MSG_START:
ASSERT(worker_ == rtc::Thread::Current());
worker_->PostDelayed(timeout_ms_, this, MSG_TIMEOUT);
CheckNetworks();
break;
case MSG_STOP:
// We're being stopped, free resources.
CleanUp();
break;
case MSG_TIMEOUT:
// We need to signal results on the main thread.
main_->Post(this, MSG_SIGNAL_RESULTS);
break;
case MSG_SIGNAL_RESULTS:
ASSERT(main_ == rtc::Thread::Current());
SignalCheckDone(this);
break;
default:
LOG(LS_ERROR) << "Unknown message: " << msg->message_id;
}
}
void ConnectivityChecker::OnProxyDetect(rtc::SignalThread* thread) {
ASSERT(worker_ == rtc::Thread::Current());
if (proxy_detect_->proxy().type != rtc::PROXY_NONE) {
SetProxyInfo(proxy_detect_->proxy());
}
}
void ConnectivityChecker::OnRequestDone(rtc::AsyncHttpRequest* request) {
ASSERT(worker_ == rtc::Thread::Current());
// Since we don't know what nic were actually used for the http request,
// for now, just use the first one.
std::vector<rtc::Network*> networks;
network_manager_->GetNetworks(&networks);
if (networks.empty()) {
LOG(LS_ERROR) << "No networks while registering http start.";
return;
}
rtc::ProxyInfo proxy_info = request->proxy();
NicMap::iterator i =
#ifdef USE_WEBRTC_DEV_BRANCH
nics_.find(NicId(networks[0]->GetBestIP(), proxy_info.address));
#else // USE_WEBRTC_DEV_BRANCH
nics_.find(NicId(networks[0]->ip(), proxy_info.address));
#endif // USE_WEBRTC_DEV_BRANCH
if (i != nics_.end()) {
int port = request->port();
uint32 now = rtc::Time();
NicInfo* nic_info = &i->second;
if (port == rtc::HTTP_SECURE_PORT) {
nic_info->https.rtt = now - nic_info->https.start_time_ms;
} else {
LOG(LS_ERROR) << "Got response with unknown port: " << port;
}
} else {
LOG(LS_ERROR) << "No nic info found while receiving response.";
}
}
void ConnectivityChecker::OnConfigReady(
const std::string& username, const std::string& password,
const PortConfiguration* config, const rtc::ProxyInfo& proxy_info) {
ASSERT(worker_ == rtc::Thread::Current());
// Since we send requests on both HTTP and HTTPS we will get two
// configs per nic. Results from the second will overwrite the
// result from the first.
// TODO: Handle multiple pings on one nic.
CreateRelayPorts(username, password, config, proxy_info);
}
void ConnectivityChecker::OnRelayPortComplete(Port* port) {
ASSERT(worker_ == rtc::Thread::Current());
RelayPort* relay_port = reinterpret_cast<RelayPort*>(port);
const ProtocolAddress* address = relay_port->ServerAddress(0);
#ifdef USE_WEBRTC_DEV_BRANCH
rtc::IPAddress ip = port->Network()->GetBestIP();
#else // USE_WEBRTC_DEV_BRANCH
rtc::IPAddress ip = port->Network()->ip();
#endif // USE_WEBRTC_DEV_BRANCH
NicMap::iterator i = nics_.find(NicId(ip, port->proxy().address));
if (i != nics_.end()) {
// We have it already, add the new information.
NicInfo* nic_info = &i->second;
ConnectInfo* connect_info = NULL;
if (address) {
switch (address->proto) {
case PROTO_UDP:
connect_info = &nic_info->udp;
break;
case PROTO_TCP:
connect_info = &nic_info->tcp;
break;
case PROTO_SSLTCP:
connect_info = &nic_info->ssltcp;
break;
default:
LOG(LS_ERROR) << " relay address with bad protocol added";
}
if (connect_info) {
connect_info->rtt =
rtc::TimeSince(connect_info->start_time_ms);
}
}
} else {
LOG(LS_ERROR) << " got relay address for non-existing nic";
}
}
void ConnectivityChecker::OnStunPortComplete(Port* port) {
ASSERT(worker_ == rtc::Thread::Current());
const std::vector<Candidate> candidates = port->Candidates();
Candidate c = candidates[0];
#ifdef USE_WEBRTC_DEV_BRANCH
rtc::IPAddress ip = port->Network()->GetBestIP();
#else // USE_WEBRTC_DEV_BRANCH
rtc::IPAddress ip = port->Network()->ip();
#endif // USE_WEBRTC_DEV_BRANCH
NicMap::iterator i = nics_.find(NicId(ip, port->proxy().address));
if (i != nics_.end()) {
// We have it already, add the new information.
uint32 now = rtc::Time();
NicInfo* nic_info = &i->second;
nic_info->external_address = c.address();
nic_info->stun_server_addresses =
static_cast<StunPort*>(port)->server_addresses();
nic_info->stun.rtt = now - nic_info->stun.start_time_ms;
} else {
LOG(LS_ERROR) << "Got stun address for non-existing nic";
}
}
void ConnectivityChecker::OnStunPortError(Port* port) {
ASSERT(worker_ == rtc::Thread::Current());
LOG(LS_ERROR) << "Stun address error.";
#ifdef USE_WEBRTC_DEV_BRANCH
rtc::IPAddress ip = port->Network()->GetBestIP();
#else // USE_WEBRTC_DEV_BRANCH
rtc::IPAddress ip = port->Network()->ip();
#endif // USE_WEBRTC_DEV_BRANCH
NicMap::iterator i = nics_.find(NicId(ip, port->proxy().address));
if (i != nics_.end()) {
// We have it already, add the new information.
NicInfo* nic_info = &i->second;
nic_info->stun_server_addresses =
static_cast<StunPort*>(port)->server_addresses();
}
}
void ConnectivityChecker::OnRelayPortError(Port* port) {
ASSERT(worker_ == rtc::Thread::Current());
LOG(LS_ERROR) << "Relay address error.";
}
void ConnectivityChecker::OnNetworksChanged() {
ASSERT(worker_ == rtc::Thread::Current());
std::vector<rtc::Network*> networks;
network_manager_->GetNetworks(&networks);
if (networks.empty()) {
LOG(LS_ERROR) << "Machine has no networks; nothing to do";
return;
}
AllocatePorts();
}
HttpPortAllocator* ConnectivityChecker::CreatePortAllocator(
rtc::NetworkManager* network_manager,
const std::string& user_agent,
const std::string& relay_token) {
return new TestHttpPortAllocator(network_manager, user_agent, relay_token);
}
StunPort* ConnectivityChecker::CreateStunPort(
const std::string& username, const std::string& password,
const PortConfiguration* config, rtc::Network* network) {
return StunPort::Create(worker_,
socket_factory_.get(),
network,
#ifdef USE_WEBRTC_DEV_BRANCH
network->GetBestIP(),
#else // USE_WEBRTC_DEV_BRANCH
network->ip(),
#endif // USE_WEBRTC_DEV_BRANCH
0,
0,
username,
password,
config->stun_servers);
}
RelayPort* ConnectivityChecker::CreateRelayPort(
const std::string& username, const std::string& password,
const PortConfiguration* config, rtc::Network* network) {
return RelayPort::Create(worker_,
socket_factory_.get(),
network,
#ifdef USE_WEBRTC_DEV_BRANCH
network->GetBestIP(),
#else // USE_WEBRTC_DEV_BRANCH
network->ip(),
#endif // USE_WEBRTC_DEV_BRANCH
port_allocator_->min_port(),
port_allocator_->max_port(),
username,
password);
}
void ConnectivityChecker::CreateRelayPorts(
const std::string& username, const std::string& password,
const PortConfiguration* config, const rtc::ProxyInfo& proxy_info) {
PortConfiguration::RelayList::const_iterator relay;
std::vector<rtc::Network*> networks;
network_manager_->GetNetworks(&networks);
if (networks.empty()) {
LOG(LS_ERROR) << "Machine has no networks; no relay ports created.";
return;
}
for (relay = config->relays.begin();
relay != config->relays.end(); ++relay) {
for (uint32 i = 0; i < networks.size(); ++i) {
NicMap::iterator iter =
#ifdef USE_WEBRTC_DEV_BRANCH
nics_.find(NicId(networks[i]->GetBestIP(), proxy_info.address));
#else // USE_WEBRTC_DEV_BRANCH
nics_.find(NicId(networks[i]->ip(), proxy_info.address));
#endif // USE_WEBRTC_DEV_BRANCH
if (iter != nics_.end()) {
// TODO: Now setting the same start time for all protocols.
// This might affect accuracy, but since we are mainly looking for
// connect failures or number that stick out, this is good enough.
uint32 now = rtc::Time();
NicInfo* nic_info = &iter->second;
nic_info->udp.start_time_ms = now;
nic_info->tcp.start_time_ms = now;
nic_info->ssltcp.start_time_ms = now;
// Add the addresses of this protocol.
PortList::const_iterator relay_port;
for (relay_port = relay->ports.begin();
relay_port != relay->ports.end();
++relay_port) {
RelayPort* port = CreateRelayPort(username, password,
config, networks[i]);
port->AddServerAddress(*relay_port);
port->AddExternalAddress(*relay_port);
nic_info->media_server_address = port->ServerAddress(0)->address;
// Listen to network events.
port->SignalPortComplete.connect(
this, &ConnectivityChecker::OnRelayPortComplete);
port->SignalPortError.connect(
this, &ConnectivityChecker::OnRelayPortError);
port->set_proxy(user_agent_, proxy_info);
// Start fetching an address for this port.
port->PrepareAddress();
ports_.push_back(port);
}
} else {
LOG(LS_ERROR) << "Failed to find nic info when creating relay ports.";
}
}
}
}
void ConnectivityChecker::AllocatePorts() {
const std::string username = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
const std::string password = rtc::CreateRandomString(ICE_PWD_LENGTH);
ServerAddresses stun_servers;
stun_servers.insert(stun_address_);
PortConfiguration config(stun_servers, username, password);
std::vector<rtc::Network*> networks;
network_manager_->GetNetworks(&networks);
if (networks.empty()) {
LOG(LS_ERROR) << "Machine has no networks; no ports will be allocated";
return;
}
rtc::ProxyInfo proxy_info = GetProxyInfo();
bool allocate_relay_ports = false;
for (uint32 i = 0; i < networks.size(); ++i) {
#ifdef USE_WEBRTC_DEV_BRANCH
if (AddNic(networks[i]->GetBestIP(), proxy_info.address)) {
#else // USE_WEBRTC_DEV_BRANCH
if (AddNic(networks[i]->ip(), proxy_info.address)) {
#endif // USE_WEBRTC_DEV_BRANCH
Port* port = CreateStunPort(username, password, &config, networks[i]);
if (port) {
// Listen to network events.
port->SignalPortComplete.connect(
this, &ConnectivityChecker::OnStunPortComplete);
port->SignalPortError.connect(
this, &ConnectivityChecker::OnStunPortError);
port->set_proxy(user_agent_, proxy_info);
port->PrepareAddress();
ports_.push_back(port);
allocate_relay_ports = true;
}
}
}
// If any new ip/proxy combinations were added, send a relay allocate.
if (allocate_relay_ports) {
AllocateRelayPorts();
}
// Initiate proxy detection.
InitiateProxyDetection();
}
void ConnectivityChecker::InitiateProxyDetection() {
// Only start if we haven't been started before.
if (!proxy_detect_) {
proxy_detect_ = new rtc::AutoDetectProxy(user_agent_);
rtc::Url<char> host_url("/", "relay.google.com",
rtc::HTTP_SECURE_PORT);
host_url.set_secure(true);
proxy_detect_->set_server_url(host_url.url());
proxy_detect_->SignalWorkDone.connect(
this, &ConnectivityChecker::OnProxyDetect);
proxy_detect_->Start();
}
}
void ConnectivityChecker::AllocateRelayPorts() {
// Currently we are using the 'default' nic for http(s) requests.
TestHttpPortAllocatorSession* allocator_session =
reinterpret_cast<TestHttpPortAllocatorSession*>(
port_allocator_->CreateSessionInternal(
"connectivity checker test content",
ICE_CANDIDATE_COMPONENT_RTP,
rtc::CreateRandomString(ICE_UFRAG_LENGTH),
rtc::CreateRandomString(ICE_PWD_LENGTH)));
allocator_session->set_proxy(port_allocator_->proxy());
allocator_session->SignalConfigReady.connect(
this, &ConnectivityChecker::OnConfigReady);
allocator_session->SignalRequestDone.connect(
this, &ConnectivityChecker::OnRequestDone);
// Try https only since using http would result in credentials being sent
// over the network unprotected.
RegisterHttpStart(rtc::HTTP_SECURE_PORT);
allocator_session->SendSessionRequest("relay.l.google.com",
rtc::HTTP_SECURE_PORT);
sessions_.push_back(allocator_session);
}
void ConnectivityChecker::RegisterHttpStart(int port) {
// Since we don't know what nic were actually used for the http request,
// for now, just use the first one.
std::vector<rtc::Network*> networks;
network_manager_->GetNetworks(&networks);
if (networks.empty()) {
LOG(LS_ERROR) << "No networks while registering http start.";
return;
}
rtc::ProxyInfo proxy_info = GetProxyInfo();
NicMap::iterator i =
#ifdef USE_WEBRTC_DEV_BRANCH
nics_.find(NicId(networks[0]->GetBestIP(), proxy_info.address));
#else // USE_WEBRTC_DEV_BRANCH
nics_.find(NicId(networks[0]->ip(), proxy_info.address));
#endif // USE_WEBRTC_DEV_BRANCH
if (i != nics_.end()) {
uint32 now = rtc::Time();
NicInfo* nic_info = &i->second;
if (port == rtc::HTTP_SECURE_PORT) {
nic_info->https.start_time_ms = now;
} else {
LOG(LS_ERROR) << "Registering start time for unknown port: " << port;
}
} else {
LOG(LS_ERROR) << "Error, no nic info found while registering http start.";
}
}
} // namespace rtc

View File

@ -1,274 +0,0 @@
// Copyright 2011 Google Inc. All Rights Reserved.
#ifndef WEBRTC_P2P_CLIENT_CONNECTIVITYCHECKER_H_
#define WEBRTC_P2P_CLIENT_CONNECTIVITYCHECKER_H_
#include <map>
#include <string>
#include "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/client/httpportallocator.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/messagehandler.h"
#include "webrtc/base/network.h"
#include "webrtc/base/proxyinfo.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/socketaddress.h"
namespace rtc {
class AsyncHttpRequest;
class AutoDetectProxy;
class BasicPacketSocketFactory;
class NetworkManager;
class PacketSocketFactory;
class SignalThread;
class TestHttpPortAllocatorSession;
class Thread;
}
namespace cricket {
class HttpPortAllocator;
class Port;
class PortAllocatorSession;
struct PortConfiguration;
class RelayPort;
class StunPort;
// Contains details about a discovered firewall that are of interest
// when debugging call failures.
struct FirewallInfo {
std::string brand;
std::string model;
// TODO: List of current port mappings.
};
// Contains details about a specific connect attempt.
struct ConnectInfo {
ConnectInfo()
: rtt(-1), error(0) {}
// Time when the connection was initiated. Needed for calculating
// the round trip time.
uint32 start_time_ms;
// Round trip time in milliseconds or -1 for failed connection.
int32 rtt;
// Error code representing low level errors like socket errors.
int error;
};
// Identifier for a network interface and proxy address pair.
struct NicId {
NicId(const rtc::IPAddress& ip,
const rtc::SocketAddress& proxy_address)
: ip(ip),
proxy_address(proxy_address) {
}
rtc::IPAddress ip;
rtc::SocketAddress proxy_address;
};
// Comparator implementation identifying unique network interface and
// proxy address pairs.
class NicIdComparator {
public:
int compare(const NicId &first, const NicId &second) const {
if (first.ip == second.ip) {
// Compare proxy address.
if (first.proxy_address == second.proxy_address) {
return 0;
} else {
return first.proxy_address < second.proxy_address? -1 : 1;
}
}
return first.ip < second.ip ? -1 : 1;
}
bool operator()(const NicId &first, const NicId &second) const {
return (compare(first, second) < 0);
}
};
// Contains information of a network interface and proxy address pair.
struct NicInfo {
NicInfo() {}
rtc::IPAddress ip;
rtc::ProxyInfo proxy_info;
rtc::SocketAddress external_address;
ServerAddresses stun_server_addresses;
rtc::SocketAddress media_server_address;
ConnectInfo stun;
ConnectInfo http;
ConnectInfo https;
ConnectInfo udp;
ConnectInfo tcp;
ConnectInfo ssltcp;
FirewallInfo firewall;
};
// Holds the result of the connectivity check.
class NicMap : public std::map<NicId, NicInfo, NicIdComparator> {
};
class TestHttpPortAllocatorSession : public HttpPortAllocatorSession {
public:
TestHttpPortAllocatorSession(
HttpPortAllocator* allocator,
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd,
const std::vector<rtc::SocketAddress>& stun_hosts,
const std::vector<std::string>& relay_hosts,
const std::string& relay_token,
const std::string& user_agent)
: HttpPortAllocatorSession(
allocator, content_name, component, ice_ufrag, ice_pwd, stun_hosts,
relay_hosts, relay_token, user_agent) {
}
void set_proxy(const rtc::ProxyInfo& proxy) {
proxy_ = proxy;
}
void ConfigReady(PortConfiguration* config);
void OnRequestDone(rtc::SignalThread* data);
sigslot::signal4<const std::string&, const std::string&,
const PortConfiguration*,
const rtc::ProxyInfo&> SignalConfigReady;
sigslot::signal1<rtc::AsyncHttpRequest*> SignalRequestDone;
private:
rtc::ProxyInfo proxy_;
};
// Runs a request/response check on all network interface and proxy
// address combinations. The check is considered done either when all
// checks has been successful or when the check times out.
class ConnectivityChecker
: public rtc::MessageHandler, public sigslot::has_slots<> {
public:
ConnectivityChecker(rtc::Thread* worker,
const std::string& jid,
const std::string& session_id,
const std::string& user_agent,
const std::string& relay_token,
const std::string& connection);
virtual ~ConnectivityChecker();
// Virtual for gMock.
virtual bool Initialize();
virtual void Start();
// MessageHandler implementation.
virtual void OnMessage(rtc::Message *msg);
// Instruct checker to stop and wait until that's done.
// Virtual for gMock.
virtual void Stop() {
worker_->Stop();
}
const NicMap& GetResults() const {
return nics_;
}
void set_timeout_ms(uint32 timeout) {
timeout_ms_ = timeout;
}
void set_stun_address(const rtc::SocketAddress& stun_address) {
stun_address_ = stun_address;
}
const std::string& connection() const {
return connection_;
}
const std::string& jid() const {
return jid_;
}
const std::string& session_id() const {
return session_id_;
}
// Context: Main Thread. Signalled when the connectivity check is complete.
sigslot::signal1<ConnectivityChecker*> SignalCheckDone;
protected:
// Can be overridden for test.
virtual rtc::NetworkManager* CreateNetworkManager() {
return new rtc::BasicNetworkManager();
}
virtual rtc::BasicPacketSocketFactory* CreateSocketFactory(
rtc::Thread* thread) {
return new rtc::BasicPacketSocketFactory(thread);
}
virtual HttpPortAllocator* CreatePortAllocator(
rtc::NetworkManager* network_manager,
const std::string& user_agent,
const std::string& relay_token);
virtual StunPort* CreateStunPort(
const std::string& username, const std::string& password,
const PortConfiguration* config, rtc::Network* network);
virtual RelayPort* CreateRelayPort(
const std::string& username, const std::string& password,
const PortConfiguration* config, rtc::Network* network);
virtual void InitiateProxyDetection();
virtual void SetProxyInfo(const rtc::ProxyInfo& info);
virtual rtc::ProxyInfo GetProxyInfo() const;
rtc::Thread* worker() {
return worker_;
}
private:
bool AddNic(const rtc::IPAddress& ip,
const rtc::SocketAddress& proxy_address);
void AllocatePorts();
void AllocateRelayPorts();
void CheckNetworks();
void CreateRelayPorts(
const std::string& username, const std::string& password,
const PortConfiguration* config, const rtc::ProxyInfo& proxy_info);
// Must be called by the worker thread.
void CleanUp();
void OnRequestDone(rtc::AsyncHttpRequest* request);
void OnRelayPortComplete(Port* port);
void OnStunPortComplete(Port* port);
void OnRelayPortError(Port* port);
void OnStunPortError(Port* port);
void OnNetworksChanged();
void OnProxyDetect(rtc::SignalThread* thread);
void OnConfigReady(
const std::string& username, const std::string& password,
const PortConfiguration* config, const rtc::ProxyInfo& proxy);
void OnConfigWithProxyReady(const PortConfiguration*);
void RegisterHttpStart(int port);
rtc::Thread* worker_;
std::string jid_;
std::string session_id_;
std::string user_agent_;
std::string relay_token_;
std::string connection_;
rtc::AutoDetectProxy* proxy_detect_;
rtc::scoped_ptr<rtc::NetworkManager> network_manager_;
rtc::scoped_ptr<rtc::BasicPacketSocketFactory> socket_factory_;
rtc::scoped_ptr<HttpPortAllocator> port_allocator_;
NicMap nics_;
std::vector<Port*> ports_;
std::vector<PortAllocatorSession*> sessions_;
uint32 timeout_ms_;
rtc::SocketAddress stun_address_;
rtc::Thread* main_;
bool started_;
};
} // namespace cricket
#endif // WEBRTC_P2P_CLIENT_CONNECTIVITYCHECKER_H_

View File

@ -1,392 +0,0 @@
/*
* libjingle
* Copyright 2011, 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 <string>
#include "webrtc/p2p/base/basicpacketsocketfactory.h"
#include "webrtc/p2p/base/relayport.h"
#include "webrtc/p2p/base/stunport.h"
#include "webrtc/p2p/client/connectivitychecker.h"
#include "webrtc/p2p/client/httpportallocator.h"
#include "webrtc/base/asynchttprequest.h"
#include "webrtc/base/fakenetwork.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/socketaddress.h"
namespace cricket {
static const rtc::SocketAddress kClientAddr1("11.11.11.11", 0);
static const rtc::SocketAddress kClientAddr2("22.22.22.22", 0);
static const rtc::SocketAddress kExternalAddr("33.33.33.33", 3333);
static const rtc::SocketAddress kStunAddr("44.44.44.44", 4444);
static const rtc::SocketAddress kRelayAddr("55.55.55.55", 5555);
static const rtc::SocketAddress kProxyAddr("66.66.66.66", 6666);
static const rtc::ProxyType kProxyType = rtc::PROXY_HTTPS;
static const char kRelayHost[] = "relay.google.com";
static const char kRelayToken[] =
"CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6";
static const char kBrowserAgent[] = "browser_test";
static const char kJid[] = "a.b@c";
static const char kUserName[] = "testuser";
static const char kPassword[] = "testpassword";
static const char kMagicCookie[] = "testcookie";
static const char kRelayUdpPort[] = "4444";
static const char kRelayTcpPort[] = "5555";
static const char kRelaySsltcpPort[] = "6666";
static const char kSessionId[] = "testsession";
static const char kConnection[] = "testconnection";
static const int kMinPort = 1000;
static const int kMaxPort = 2000;
// Fake implementation to mock away real network usage.
class FakeRelayPort : public RelayPort {
public:
FakeRelayPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network, const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username, const std::string& password)
: RelayPort(thread, factory, network, ip, min_port, max_port,
username, password) {
}
// Just signal that we are done.
virtual void PrepareAddress() {
SignalPortComplete(this);
}
};
// Fake implementation to mock away real network usage.
class FakeStunPort : public StunPort {
public:
FakeStunPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username, const std::string& password,
const ServerAddresses& server_addr)
: StunPort(thread, factory, network, ip, min_port, max_port,
username, password, server_addr) {
}
// Just set external address and signal that we are done.
virtual void PrepareAddress() {
AddAddress(kExternalAddr, kExternalAddr, rtc::SocketAddress(), "udp", "",
STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, true);
SignalPortComplete(this);
}
};
// Fake implementation to mock away real network usage by responding
// to http requests immediately.
class FakeHttpPortAllocatorSession : public TestHttpPortAllocatorSession {
public:
FakeHttpPortAllocatorSession(
HttpPortAllocator* allocator,
const std::string& content_name,
int component,
const std::string& ice_ufrag, const std::string& ice_pwd,
const std::vector<rtc::SocketAddress>& stun_hosts,
const std::vector<std::string>& relay_hosts,
const std::string& relay_token,
const std::string& agent)
: TestHttpPortAllocatorSession(allocator,
content_name,
component,
ice_ufrag,
ice_pwd,
stun_hosts,
relay_hosts,
relay_token,
agent) {
}
virtual void SendSessionRequest(const std::string& host, int port) {
FakeReceiveSessionResponse(host, port);
}
// Pass results to the real implementation.
void FakeReceiveSessionResponse(const std::string& host, int port) {
rtc::AsyncHttpRequest* response = CreateAsyncHttpResponse(port);
TestHttpPortAllocatorSession::OnRequestDone(response);
response->Destroy(true);
}
private:
// Helper method for creating a response to a relay session request.
rtc::AsyncHttpRequest* CreateAsyncHttpResponse(int port) {
rtc::AsyncHttpRequest* request =
new rtc::AsyncHttpRequest(kBrowserAgent);
std::stringstream ss;
ss << "username=" << kUserName << std::endl
<< "password=" << kPassword << std::endl
<< "magic_cookie=" << kMagicCookie << std::endl
<< "relay.ip=" << kRelayAddr.ipaddr().ToString() << std::endl
<< "relay.udp_port=" << kRelayUdpPort << std::endl
<< "relay.tcp_port=" << kRelayTcpPort << std::endl
<< "relay.ssltcp_port=" << kRelaySsltcpPort << std::endl;
request->response().document.reset(
new rtc::MemoryStream(ss.str().c_str()));
request->response().set_success();
request->set_port(port);
request->set_secure(port == rtc::HTTP_SECURE_PORT);
return request;
}
};
// Fake implementation for creating fake http sessions.
class FakeHttpPortAllocator : public HttpPortAllocator {
public:
FakeHttpPortAllocator(rtc::NetworkManager* network_manager,
const std::string& user_agent)
: HttpPortAllocator(network_manager, user_agent) {
}
virtual PortAllocatorSession* CreateSessionInternal(
const std::string& content_name, int component,
const std::string& ice_ufrag, const std::string& ice_pwd) {
std::vector<rtc::SocketAddress> stun_hosts;
stun_hosts.push_back(kStunAddr);
std::vector<std::string> relay_hosts;
relay_hosts.push_back(kRelayHost);
return new FakeHttpPortAllocatorSession(this,
content_name,
component,
ice_ufrag,
ice_pwd,
stun_hosts,
relay_hosts,
kRelayToken,
kBrowserAgent);
}
};
class ConnectivityCheckerForTest : public ConnectivityChecker {
public:
ConnectivityCheckerForTest(rtc::Thread* worker,
const std::string& jid,
const std::string& session_id,
const std::string& user_agent,
const std::string& relay_token,
const std::string& connection)
: ConnectivityChecker(worker,
jid,
session_id,
user_agent,
relay_token,
connection),
proxy_initiated_(false) {
}
rtc::FakeNetworkManager* network_manager() const {
return network_manager_;
}
FakeHttpPortAllocator* port_allocator() const {
return fake_port_allocator_;
}
protected:
// Overridden methods for faking a real network.
virtual rtc::NetworkManager* CreateNetworkManager() {
network_manager_ = new rtc::FakeNetworkManager();
return network_manager_;
}
virtual rtc::BasicPacketSocketFactory* CreateSocketFactory(
rtc::Thread* thread) {
// Create socket factory, for simplicity, let it run on the current thread.
socket_factory_ =
new rtc::BasicPacketSocketFactory(rtc::Thread::Current());
return socket_factory_;
}
virtual HttpPortAllocator* CreatePortAllocator(
rtc::NetworkManager* network_manager,
const std::string& user_agent,
const std::string& relay_token) {
fake_port_allocator_ =
new FakeHttpPortAllocator(network_manager, user_agent);
return fake_port_allocator_;
}
virtual StunPort* CreateStunPort(
const std::string& username, const std::string& password,
const PortConfiguration* config, rtc::Network* network) {
return new FakeStunPort(worker(),
socket_factory_,
network,
#ifdef USE_WEBRTC_DEV_BRANCH
network->GetBestIP(),
#else // USE_WEBRTC_DEV_BRANCH
network->ip(),
#endif // USE_WEBRTC_DEV_BRANCH
kMinPort,
kMaxPort,
username,
password,
config->stun_servers);
}
virtual RelayPort* CreateRelayPort(
const std::string& username, const std::string& password,
const PortConfiguration* config, rtc::Network* network) {
return new FakeRelayPort(worker(),
socket_factory_,
network,
#ifdef USE_WEBRTC_DEV_BRANCH
network->GetBestIP(),
#else // USE_WEBRTC_DEV_BRANCH
network->ip(),
#endif // USE_WEBRTC_DEV_BRANCH
kMinPort,
kMaxPort,
username,
password);
}
virtual void InitiateProxyDetection() {
if (!proxy_initiated_) {
proxy_initiated_ = true;
proxy_info_.address = kProxyAddr;
proxy_info_.type = kProxyType;
SetProxyInfo(proxy_info_);
}
}
virtual rtc::ProxyInfo GetProxyInfo() const {
return proxy_info_;
}
private:
rtc::BasicPacketSocketFactory* socket_factory_;
FakeHttpPortAllocator* fake_port_allocator_;
rtc::FakeNetworkManager* network_manager_;
rtc::ProxyInfo proxy_info_;
bool proxy_initiated_;
};
class ConnectivityCheckerTest : public testing::Test {
protected:
void VerifyNic(const NicInfo& info,
const rtc::SocketAddress& local_address) {
// Verify that the external address has been set.
EXPECT_EQ(kExternalAddr, info.external_address);
// Verify that the stun server address has been set.
EXPECT_EQ(1U, info.stun_server_addresses.size());
EXPECT_EQ(kStunAddr, *(info.stun_server_addresses.begin()));
// Verify that the media server address has been set. Don't care
// about port since it is different for different protocols.
EXPECT_EQ(kRelayAddr.ipaddr(), info.media_server_address.ipaddr());
// Verify that local ip matches.
EXPECT_EQ(local_address.ipaddr(), info.ip);
// Verify that we have received responses for our
// pings. Unsuccessful ping has rtt value -1, successful >= 0.
EXPECT_GE(info.stun.rtt, 0);
EXPECT_GE(info.udp.rtt, 0);
EXPECT_GE(info.tcp.rtt, 0);
EXPECT_GE(info.ssltcp.rtt, 0);
// If proxy has been set, verify address and type.
if (!info.proxy_info.address.IsNil()) {
EXPECT_EQ(kProxyAddr, info.proxy_info.address);
EXPECT_EQ(kProxyType, info.proxy_info.type);
}
}
};
// Tests a configuration with two network interfaces. Verifies that 4
// combinations of ip/proxy are created and that all protocols are
// tested on each combination.
TEST_F(ConnectivityCheckerTest, TestStart) {
ConnectivityCheckerForTest connectivity_checker(rtc::Thread::Current(),
kJid,
kSessionId,
kBrowserAgent,
kRelayToken,
kConnection);
connectivity_checker.Initialize();
connectivity_checker.set_stun_address(kStunAddr);
connectivity_checker.network_manager()->AddInterface(kClientAddr1);
connectivity_checker.network_manager()->AddInterface(kClientAddr2);
connectivity_checker.Start();
rtc::Thread::Current()->ProcessMessages(1000);
NicMap nics = connectivity_checker.GetResults();
// There should be 4 nics in our map. 2 for each interface added,
// one with proxy set and one without.
EXPECT_EQ(4U, nics.size());
// First verify interfaces without proxy.
rtc::SocketAddress nilAddress;
// First lookup the address of the first nic combined with no proxy.
NicMap::iterator i = nics.find(NicId(kClientAddr1.ipaddr(), nilAddress));
ASSERT(i != nics.end());
NicInfo info = i->second;
VerifyNic(info, kClientAddr1);
// Then make sure the second device has been tested without proxy.
i = nics.find(NicId(kClientAddr2.ipaddr(), nilAddress));
ASSERT(i != nics.end());
info = i->second;
VerifyNic(info, kClientAddr2);
// Now verify both interfaces with proxy.
i = nics.find(NicId(kClientAddr1.ipaddr(), kProxyAddr));
ASSERT(i != nics.end());
info = i->second;
VerifyNic(info, kClientAddr1);
i = nics.find(NicId(kClientAddr2.ipaddr(), kProxyAddr));
ASSERT(i != nics.end());
info = i->second;
VerifyNic(info, kClientAddr2);
};
// Tests that nothing bad happens if thera are no network interfaces
// available to check.
TEST_F(ConnectivityCheckerTest, TestStartNoNetwork) {
ConnectivityCheckerForTest connectivity_checker(rtc::Thread::Current(),
kJid,
kSessionId,
kBrowserAgent,
kRelayToken,
kConnection);
connectivity_checker.Initialize();
connectivity_checker.Start();
rtc::Thread::Current()->ProcessMessages(1000);
NicMap nics = connectivity_checker.GetResults();
// Verify that no nics where checked.
EXPECT_EQ(0U, nics.size());
}
} // namespace cricket

Some files were not shown because too many files have changed in this diff Show More