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:
parent
d819803d45
commit
c4922316b4
@ -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
|
@ -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_
|
@ -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
|
@ -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
|
@ -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_
|
@ -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_
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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_
|
@ -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(¤t_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
|
@ -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_
|
@ -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());
|
||||
}
|
@ -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_
|
@ -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
|
@ -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
@ -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
@ -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_
|
@ -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
|
@ -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
@ -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
@ -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
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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;
|
||||
}
|
@ -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_
|
@ -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
|
@ -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
@ -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_
|
@ -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);
|
||||
}
|
||||
*/
|
@ -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)
|
@ -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_
|
@ -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)
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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();
|
||||
}
|
@ -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
|
@ -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_
|
@ -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
@ -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
@ -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_
|
@ -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
|
@ -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_
|
@ -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_
|
@ -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
|
@ -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
@ -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_
|
@ -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
|
@ -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
@ -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
|
@ -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_
|
@ -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());
|
||||
}
|
@ -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
|
@ -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_
|
@ -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;
|
||||
}
|
@ -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
|
@ -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_
|
@ -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);
|
||||
}
|
@ -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
|
@ -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_
|
@ -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_
|
@ -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_
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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);
|
||||
}
|
@ -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
|
@ -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_
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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
|
||||
|
@ -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_
|
@ -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);
|
||||
}
|
@ -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
@ -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_
|
@ -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
@ -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_
|
@ -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_
|
@ -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
@ -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_
|
@ -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
|
@ -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_
|
@ -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
Loading…
x
Reference in New Issue
Block a user