blob: 4687c19e38e0e5747a5798866730f359b51d82c6 [file] [log] [blame]
#include "talk/base/helpers.h"
#include "talk/base/host.h"
#include "talk/base/natserver.h"
#include "talk/base/natsocketfactory.h"
#include "talk/base/socketaddress.h"
#include "talk/base/thread.h"
#include "talk/base/virtualsocketserver.h"
#include "talk/p2p/base/udpport.h"
#include "talk/p2p/base/relayport.h"
#include "talk/p2p/base/relayserver.h"
#include "talk/p2p/base/stunport.h"
#include "talk/p2p/base/stunserver.h"
#include <iostream>
using namespace cricket;
const uint32 MSG_CONNECT = 1;
const uint32 MSG_PREP_ADDRESS = 2;
const uint32 MSG_CREATE_CONN = 3;
const uint32 MSG_ACCEPT_CONN = 4;
const uint32 MSG_PING = 5;
Candidate GetCandidate(Port* port) {
assert(port->candidates().size() == 1);
return port->candidates()[0];
}
talk_base::SocketAddress GetAddress(Port* port) {
return GetCandidate(port).address();
}
struct Foo : public talk_base::MessageHandler, public sigslot::has_slots<> {
int count;
talk_base::SocketAddress address;
StunMessage* request;
std::string remote_frag;
talk_base::Thread* thread;
Port* port1;
Port* port2;
Connection* conn;
Foo(talk_base::Thread* th, Port* p1, Port* p2)
: count(0), thread(th), port1(p1), port2(p2), conn(0) {
}
void OnAddressReady(Port* port) {
count += 1;
}
void OnUnknownAddress(
Port* port, const talk_base::SocketAddress& addr, StunMessage* msg,
const std::string& rf) {
assert(port == port1);
if (!address.IsAny()) {
assert(addr == address);
delete request;
}
address = addr;
request = msg;
remote_frag = rf;
}
void OnMessage(talk_base::Message* pmsg) {
assert(talk_base::Thread::Current() == thread);
switch (pmsg->message_id) {
case MSG_CONNECT:
port1->SignalAddressReady.connect(this, &Foo::OnAddressReady);
port1->SignalUnknownAddress.connect(this, &Foo::OnUnknownAddress);
break;
case MSG_PREP_ADDRESS:
port1->PrepareAddress();
break;
case MSG_CREATE_CONN:
conn = port1->CreateConnection(GetCandidate(port2), Port::ORIGIN_MESSAGE);
assert(conn);
conn->Ping(0);
break;
case MSG_PING:
assert(conn);
conn->Ping(0);
break;
case MSG_ACCEPT_CONN: {
Candidate c = GetCandidate(port2);
c.set_address(address);
conn = port1->CreateConnection(c, Port::ORIGIN_MESSAGE);
assert(conn);
port1->SendBindingResponse(request, address);
conn->Ping(0);
delete request;
break;
}
default:
assert(false);
break;
}
}
};
void test(talk_base::Thread* pthMain, const char* name1, Port* port1,
talk_base::Thread* pthBack, const char* name2, Port* port2,
bool accept = true, bool same_addr = true) {
Foo* foo1 = new Foo(pthMain, port1, port2);
Foo* foo2 = new Foo(pthBack, port2, port1);
std::cout << "Test: " << name1 << " to " << name2 << ": ";
std::cout.flush();
pthBack->Start();
pthMain->Post(foo1, MSG_CONNECT);
pthBack->Post(foo2, MSG_CONNECT);
pthMain->ProcessMessages(10);
assert(foo1->count == 0);
assert(foo2->count == 0);
pthMain->Post(foo1, MSG_PREP_ADDRESS);
pthMain->ProcessMessages(200);
assert(foo1->count == 1);
pthBack->Post(foo2, MSG_PREP_ADDRESS);
pthMain->ProcessMessages(200);
assert(foo2->count == 1);
pthMain->Post(foo1, MSG_CREATE_CONN);
pthMain->ProcessMessages(200);
if (accept) {
assert(foo1->address.IsAny());
assert(foo2->remote_frag == port1->username_fragment());
assert(!same_addr || (foo2->address == GetAddress(port1)));
pthBack->Post(foo2, MSG_ACCEPT_CONN);
pthMain->ProcessMessages(200);
} else {
assert(foo1->address.IsAny());
assert(foo2->address.IsAny());
pthBack->Post(foo2, MSG_CREATE_CONN);
pthMain->ProcessMessages(200);
if (same_addr) {
assert(foo1->conn->read_state() == Connection::STATE_READABLE);
assert(foo2->conn->write_state() == Connection::STATE_WRITABLE);
// First connection may not be writable if the first ping did not get
// through. So we will have to do another.
if (foo1->conn->write_state() == Connection::STATE_WRITE_CONNECT) {
pthMain->Post(foo1, MSG_PING);
pthMain->ProcessMessages(200);
}
} else {
assert(foo1->address.IsAny());
assert(foo2->address.IsAny());
pthMain->Post(foo1, MSG_PING);
pthMain->ProcessMessages(200);
assert(foo1->address.IsAny());
assert(!foo2->address.IsAny());
pthBack->Post(foo2, MSG_ACCEPT_CONN);
pthMain->ProcessMessages(200);
}
}
assert(foo1->conn->read_state() == Connection::STATE_READABLE);
assert(foo1->conn->write_state() == Connection::STATE_WRITABLE);
assert(foo2->conn->read_state() == Connection::STATE_READABLE);
assert(foo2->conn->write_state() == Connection::STATE_WRITABLE);
pthBack->Stop();
delete port1;
delete port2;
delete foo1;
delete foo2;
std::cout << "PASS" << std::endl;
}
const talk_base::SocketAddress local_addr = talk_base::SocketAddress("127.0.0.1", 0);
const talk_base::SocketAddress relay_int_addr = talk_base::SocketAddress("127.0.0.1", 5000);
const talk_base::SocketAddress relay_ext_addr = talk_base::SocketAddress("127.0.0.1", 5001);
const talk_base::SocketAddress stun_addr = talk_base::SocketAddress("127.0.0.1", STUN_SERVER_PORT);
const talk_base::SocketAddress nat_addr = talk_base::SocketAddress("127.0.0.1", talk_base::NAT_SERVER_PORT);
void test_udp() {
talk_base::Thread* pthMain = talk_base::Thread::Current();
talk_base::Thread* pthBack = new talk_base::Thread();
talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
test(pthMain, "udp", new UDPPort(pthMain, NULL, network, local_addr),
pthBack, "udp", new UDPPort(pthBack, NULL, network, local_addr));
delete pthBack;
}
void test_relay() {
talk_base::Thread* pthMain = talk_base::Thread::Current();
talk_base::Thread* pthBack = new talk_base::Thread();
talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
RelayServer relay_server(pthBack);
talk_base::AsyncUDPSocket* int_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver());
assert(int_socket->Bind(relay_int_addr) >= 0);
relay_server.AddInternalSocket(int_socket);
talk_base::AsyncUDPSocket* ext_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver());
assert(ext_socket->Bind(relay_ext_addr) >= 0);
relay_server.AddExternalSocket(ext_socket);
std::string username = CreateRandomString(16);
std::string password = CreateRandomString(16);
RelayPort* rport =
new RelayPort(pthBack, NULL, network, local_addr, username, password, "");
rport->AddServerAddress(ProtocolAddress(relay_int_addr, PROTO_UDP));
test(pthMain, "udp", new UDPPort(pthMain, NULL, network, local_addr),
pthBack, "relay", rport);
delete pthBack;
}
const char* NATName(talk_base::NATType type) {
switch (type) {
case talk_base::NAT_OPEN_CONE: return "open cone";
case talk_base::NAT_ADDR_RESTRICTED: return "addr restricted";
case talk_base::NAT_PORT_RESTRICTED: return "port restricted";
case talk_base::NAT_SYMMETRIC: return "symmetric";
default:
assert(false);
return 0;
}
}
void test_stun(talk_base::NATType nat_type) {
talk_base::Thread* pthMain = talk_base::Thread::Current();
talk_base::Thread* pthBack = new talk_base::Thread();
talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
talk_base::NATServer* nat = new talk_base::NATServer(
nat_type, pthMain->socketserver(), nat_addr,
pthMain->socketserver(), nat_addr);
talk_base::NATSocketFactory* nat_factory =
new talk_base::NATSocketFactory(pthMain->socketserver(), nat_addr);
StunPort* stun_port =
new StunPort(pthMain, nat_factory, network, local_addr, stun_addr);
talk_base::AsyncUDPSocket* stun_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
assert(stun_socket->Bind(stun_addr) >= 0);
StunServer* stun_server = new StunServer(stun_socket);
char name[256];
sprintf(name, "stun (%s)", NATName(nat_type));
test(pthMain, name, stun_port,
pthBack, "udp", new UDPPort(pthBack, NULL, network, local_addr),
true, nat_type != talk_base::NAT_SYMMETRIC);
delete stun_server;
delete stun_socket;
delete nat;
delete nat_factory;
delete pthBack;
}
void test_stun(talk_base::NATType nat1_type, talk_base::NATType nat2_type) {
talk_base::Thread* pthMain = talk_base::Thread::Current();
talk_base::Thread* pthBack = new talk_base::Thread();
talk_base::Network* network = new talk_base::Network("network", local_addr.ip());
talk_base::SocketAddress local_addr1(talk_base::LocalHost().networks()[0]->ip(), 0);
talk_base::SocketAddress local_addr2(talk_base::LocalHost().networks()[1]->ip(), 0);
talk_base::SocketAddress nat1_addr(local_addr1.ip(), talk_base::NAT_SERVER_PORT);
talk_base::SocketAddress nat2_addr(local_addr2.ip(), talk_base::NAT_SERVER_PORT);
talk_base::SocketAddress stun1_addr(local_addr1.ip(), STUN_SERVER_PORT);
talk_base::SocketAddress stun2_addr(local_addr2.ip(), STUN_SERVER_PORT);
talk_base::NATServer* nat1 = new talk_base::NATServer(
nat1_type, pthMain->socketserver(), nat1_addr,
pthMain->socketserver(), nat1_addr);
talk_base::NATSocketFactory* nat1_factory =
new talk_base::NATSocketFactory(pthMain->socketserver(), nat1_addr);
StunPort* stun1_port =
new StunPort(pthMain, nat1_factory, network, local_addr1, stun1_addr);
talk_base::NATServer* nat2 = new talk_base::NATServer(
nat2_type, pthBack->socketserver(), nat2_addr,
pthBack->socketserver(), nat2_addr);
talk_base::NATSocketFactory* nat2_factory =
new talk_base::NATSocketFactory(pthBack->socketserver(), nat2_addr);
StunPort* stun2_port =
new StunPort(pthMain, nat2_factory, network, local_addr2, stun2_addr);
talk_base::AsyncUDPSocket* stun1_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver());
assert(stun1_socket->Bind(stun_addr) >= 0);
StunServer* stun1_server = new StunServer(stun1_socket);
talk_base::AsyncUDPSocket* stun2_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver());
assert(stun2_socket->Bind(stun2_addr) >= 0);
StunServer* stun2_server = new StunServer(stun2_socket);
char name1[256], name2[256];
sprintf(name1, "stun (%s)", NATName(nat1_type));
sprintf(name2, "stun (%s)", NATName(nat2_type));
test(pthMain, name1, stun1_port,
pthBack, name2, stun2_port,
nat2_type == talk_base::NAT_OPEN_CONE, nat1_type != talk_base::NAT_SYMMETRIC);
delete stun1_server;
delete stun1_socket;
delete stun2_server;
delete stun2_socket;
delete nat1;
delete nat1_factory;
delete nat2;
delete nat2_factory;
delete pthBack;
}
int main(int argc, char* argv[]) {
InitRandom(NULL, 0);
test_udp();
test_relay();
test_stun(talk_base::NAT_OPEN_CONE);
test_stun(talk_base::NAT_ADDR_RESTRICTED);
test_stun(talk_base::NAT_PORT_RESTRICTED);
test_stun(talk_base::NAT_SYMMETRIC);
test_stun(talk_base::NAT_OPEN_CONE, talk_base::NAT_OPEN_CONE);
test_stun(talk_base::NAT_ADDR_RESTRICTED, talk_base::NAT_OPEN_CONE);
test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_OPEN_CONE);
test_stun(talk_base::NAT_SYMMETRIC, talk_base::NAT_OPEN_CONE);
test_stun(talk_base::NAT_ADDR_RESTRICTED, talk_base::NAT_ADDR_RESTRICTED);
test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_ADDR_RESTRICTED);
test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_PORT_RESTRICTED);
test_stun(talk_base::NAT_SYMMETRIC, talk_base::NAT_ADDR_RESTRICTED);
return 0;
}