ice test-client uses stun server to get public address

This commit is contained in:
Seth Alves 2016-09-16 17:21:28 -07:00
parent 793687bb08
commit 2e0cc158de
3 changed files with 177 additions and 107 deletions

View file

@ -745,24 +745,8 @@ void LimitedNodeList::removeSilentNodes() {
const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442; const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442;
const int NUM_BYTES_STUN_HEADER = 20; const int NUM_BYTES_STUN_HEADER = 20;
void LimitedNodeList::sendSTUNRequest() {
if (!_stunSockAddr.getAddress().isNull()) {
const int NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL = 10;
if (!_hasCompletedInitialSTUN) {
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
if (_numInitialSTUNRequests > NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL) {
// we're still trying to do our initial STUN we're over the fail threshold
stopInitialSTUNUpdate(false);
}
++_numInitialSTUNRequests;
}
char stunRequestPacket[NUM_BYTES_STUN_HEADER];
void LimitedNodeList::makeSTUNRequestPacket(char* stunRequestPacket) {
int packetIndex = 0; int packetIndex = 0;
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE); const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
@ -784,14 +768,32 @@ void LimitedNodeList::sendSTUNRequest() {
const uint NUM_TRANSACTION_ID_BYTES = 12; const uint NUM_TRANSACTION_ID_BYTES = 12;
QUuid randomUUID = QUuid::createUuid(); QUuid randomUUID = QUuid::createUuid();
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
}
void LimitedNodeList::sendSTUNRequest() {
if (!_stunSockAddr.getAddress().isNull()) {
const int NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL = 10;
if (!_hasCompletedInitialSTUN) {
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
if (_numInitialSTUNRequests > NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL) {
// we're still trying to do our initial STUN we're over the fail threshold
stopInitialSTUNUpdate(false);
}
++_numInitialSTUNRequests;
}
char stunRequestPacket[NUM_BYTES_STUN_HEADER];
makeSTUNRequestPacket(stunRequestPacket);
flagTimeForConnectionStep(ConnectionStep::SendSTUNRequest); flagTimeForConnectionStep(ConnectionStep::SendSTUNRequest);
_nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); _nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr);
} }
} }
void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet) { bool LimitedNodeList::parseSTUNResponse(udt::BasePacket* packet,
QHostAddress& newPublicAddress, uint16_t& newPublicPort) {
// check the cookie to make sure this is actually a STUN response // check the cookie to make sure this is actually a STUN response
// and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4; const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4;
@ -803,11 +805,12 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
if (memcmp(packet->getData() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH, if (memcmp(packet->getData() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
&RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER,
sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) { sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) != 0) {
return false;
}
// enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE // enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE
while (attributeStartIndex < packet->getDataSize()) { while (attributeStartIndex < packet->getDataSize()) {
if (memcmp(packet->getData() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) { if (memcmp(packet->getData() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) {
const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4; const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4;
const int NUM_BYTES_FAMILY_ALIGN = 1; const int NUM_BYTES_FAMILY_ALIGN = 1;
@ -825,7 +828,7 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
uint16_t xorMappedPort = 0; uint16_t xorMappedPort = 0;
memcpy(&xorMappedPort, packet->getData() + byteIndex, sizeof(xorMappedPort)); memcpy(&xorMappedPort, packet->getData() + byteIndex, sizeof(xorMappedPort));
uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16); newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
byteIndex += sizeof(xorMappedPort); byteIndex += sizeof(xorMappedPort);
@ -835,7 +838,30 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
QHostAddress newPublicAddress(stunAddress); // QHostAddress newPublicAddress(stunAddress);
newPublicAddress = QHostAddress(stunAddress);
return true;
}
} else {
// push forward attributeStartIndex by the length of this attribute
const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
uint16_t attributeLength = 0;
memcpy(&attributeLength, packet->getData() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
sizeof(attributeLength));
attributeLength = ntohs(attributeLength);
attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength;
}
}
return false;
}
void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet) {
uint16_t newPublicPort;
QHostAddress newPublicAddress;
if (parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) {
if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) { if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) {
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
@ -853,22 +879,6 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN); flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN);
} }
// we're done reading the packet so we can return now
return;
}
} else {
// push forward attributeStartIndex by the length of this attribute
const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
uint16_t attributeLength = 0;
memcpy(&attributeLength, packet->getData() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
sizeof(attributeLength));
attributeLength = ntohs(attributeLength);
attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength;
}
}
} }
} }

View file

@ -146,6 +146,7 @@ public:
const NodePermissions& permissions = DEFAULT_AGENT_PERMISSIONS, const NodePermissions& permissions = DEFAULT_AGENT_PERMISSIONS,
const QUuid& connectionSecret = QUuid()); const QUuid& connectionSecret = QUuid());
static bool parseSTUNResponse(udt::BasePacket* packet, QHostAddress& newPublicAddress, uint16_t& newPublicPort);
bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; }
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
@ -232,6 +233,9 @@ public:
bool packetVersionMatch(const udt::Packet& packet); bool packetVersionMatch(const udt::Packet& packet);
bool isPacketVerified(const udt::Packet& packet); bool isPacketVerified(const udt::Packet& packet);
static void makeSTUNRequestPacket(char* stunRequestPacket);
public slots: public slots:
void reset(); void reset();
void eraseAllNodes(); void eraseAllNodes();

View file

@ -16,7 +16,10 @@
#include "ICEClientApp.h" #include "ICEClientApp.h"
ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv) { ICEClientApp::ICEClientApp(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT)
{
// parse command-line // parse command-line
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity ICE client"); parser.setApplicationDescription("High Fidelity ICE client");
@ -80,7 +83,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv
qDebug() << "ICE-server address is" << _iceServerAddr; qDebug() << "ICE-server address is" << _iceServerAddr;
_state = 0; setState(lookUpStunServer);
QTimer* doTimer = new QTimer(this); QTimer* doTimer = new QTimer(this);
connect(doTimer, &QTimer::timeout, this, &ICEClientApp::doSomething); connect(doTimer, &QTimer::timeout, this, &ICEClientApp::doSomething);
@ -91,43 +94,71 @@ ICEClientApp::~ICEClientApp() {
delete _socket; delete _socket;
} }
void ICEClientApp::setState(int newState) {
// qDebug() << "state: " << _state << " --> " << newState;
_state = newState;
}
void ICEClientApp::doSomething() { void ICEClientApp::doSomething() {
if (_actionMax > 0 && _actionCount >= _actionMax) { if (_actionMax > 0 && _actionCount >= _actionMax) {
// time to stop.
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
} else if (_state == lookUpStunServer) {
// lookup STUN server address
if (!_stunSockAddr.getAddress().isNull()) {
qDebug() << "stun server is" << _stunSockAddr;
setState(sendStunRequestPacket);
} }
if (_state == 0) { } else if (_state == sendStunRequestPacket) {
// send STUN request packet
_domainServerPeerSet = false; _domainServerPeerSet = false;
unsigned int localPort = 0; unsigned int localPort = 0;
delete _socket;
_socket = new udt::Socket(); _socket = new udt::Socket();
_socket->bind(QHostAddress::AnyIPv4, localPort); _socket->bind(QHostAddress::AnyIPv4, localPort);
_socket->setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); }); _socket->setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });
_socket->addUnfilteredHandler(_stunSockAddr,
[this](std::unique_ptr<udt::BasePacket> packet) {
processSTUNResponse(std::move(packet));
});
qDebug() << "local port is" << _socket->localPort(); qDebug() << "local port is" << _socket->localPort();
_localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort());
_publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort());
// QUuid peerID = QUuid("75cd162a-53dc-4292-aaa5-1304ab1bb0f2"); const int NUM_BYTES_STUN_HEADER = 20;
char stunRequestPacket[NUM_BYTES_STUN_HEADER];
LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket);
qDebug() << "sending STUN request";
_socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr);
setState(waitForStunResponse);
} else if (_state == talkToIceServer) {
QUuid peerID; QUuid peerID;
if (_domainID == QUuid()) { if (_domainID == QUuid()) {
// pick a random domain-id // pick a random domain-id which will fail
peerID = QUuid::createUuid(); peerID = QUuid::createUuid();
_state = 2; setState(pause0);
} else { } else {
// use the domain UUID given on the command-line // use the domain UUID given on the command-line
peerID = _domainID; peerID = _domainID;
_state = 1; setState(waitForIceReply);
} }
_sessionUUID = QUuid::createUuid(); _sessionUUID = QUuid::createUuid();
qDebug() << "I am" << _sessionUUID;
sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID);
_actionCount++; _actionCount++;
} else if (_state == 2) { } else if (_state == pause0) {
_state = 3; setState(pause1);
} else if (_state == 3) { } else if (_state == pause1) {
qDebug() << ""; qDebug() << "";
_state = 0; setState(sendStunRequestPacket);
delete _socket; delete _socket;
_socket = nullptr; _socket = nullptr;
} }
@ -158,7 +189,7 @@ void ICEClientApp::icePingDomainServer() {
return; return;
} }
qDebug() << "ice-pinging domain-server"; qDebug() << "ice-pinging domain-server: " << _domainServerPeer;
auto localPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Local, _sessionUUID); auto localPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Local, _sessionUUID);
_socket->writePacket(*localPingPacket, _domainServerPeer.getLocalSocket()); _socket->writePacket(*localPingPacket, _domainServerPeer.getLocalSocket());
@ -167,29 +198,52 @@ void ICEClientApp::icePingDomainServer() {
_socket->writePacket(*publicPingPacket, _domainServerPeer.getPublicSocket()); _socket->writePacket(*publicPingPacket, _domainServerPeer.getPublicSocket());
} }
void ICEClientApp::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet) {
qDebug() << "got stun response";
if (_state != waitForStunResponse) {
qDebug() << "got unexpected stun response";
return;
}
uint16_t newPublicPort;
QHostAddress newPublicAddress;
if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) {
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
qDebug() << "My public address is" << _publicSockAddr;
setState(talkToIceServer);
}
}
void ICEClientApp::processPacket(std::unique_ptr<udt::Packet> packet) { void ICEClientApp::processPacket(std::unique_ptr<udt::Packet> packet) {
auto nlPacket = NLPacket::fromBase(std::move(packet)); std::unique_ptr<NLPacket> nlPacket = NLPacket::fromBase(std::move(packet));
if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) { if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) {
qDebug() << "got a short packet."; qDebug() << "got a short packet.";
return; return;
} }
qDebug() << "here" << nlPacket->getType();
QSharedPointer<ReceivedMessage> message = QSharedPointer<ReceivedMessage>::create(*nlPacket); QSharedPointer<ReceivedMessage> message = QSharedPointer<ReceivedMessage>::create(*nlPacket);
const HifiSockAddr& senderAddr = message->getSenderSockAddr(); const HifiSockAddr& senderAddr = message->getSenderSockAddr();
if (nlPacket->getType() == PacketType::ICEServerPeerInformation) { if (nlPacket->getType() == PacketType::ICEServerPeerInformation) {
QDataStream iceResponseStream(message->getMessage()); QDataStream iceResponseStream(message->getMessage());
if (!_domainServerPeerSet) {
iceResponseStream >> _domainServerPeer; iceResponseStream >> _domainServerPeer;
qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer;
_domainServerPeerSet = true; _domainServerPeerSet = true;
icePingDomainServer(); icePingDomainServer();
_pingDomainTimer = new QTimer(this); _pingDomainTimer = new QTimer(this);
connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer); connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer);
_pingDomainTimer->start(1000); _pingDomainTimer->start(500);
} else {
qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer.getUUID(); // NetworkPeer domainServerPeer;
// iceResponseStream >> domainServerPeer;
// qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer;
}
} else if (nlPacket->getType() == PacketType::ICEPing) { } else if (nlPacket->getType() == PacketType::ICEPing) {
qDebug() << "got packet: " << nlPacket->getType(); qDebug() << "got packet: " << nlPacket->getType();
@ -198,14 +252,16 @@ void ICEClientApp::processPacket(std::unique_ptr<udt::Packet> packet) {
} else if (nlPacket->getType() == PacketType::ICEPingReply) { } else if (nlPacket->getType() == PacketType::ICEPingReply) {
qDebug() << "got packet: " << nlPacket->getType(); qDebug() << "got packet: " << nlPacket->getType();
if (_domainServerPeerSet && _state == 1 && if (_domainServerPeerSet && _state == waitForIceReply &&
(senderAddr == _domainServerPeer.getLocalSocket() || (senderAddr == _domainServerPeer.getLocalSocket() ||
senderAddr == _domainServerPeer.getPublicSocket())) { senderAddr == _domainServerPeer.getPublicSocket())) {
delete _pingDomainTimer; delete _pingDomainTimer;
_pingDomainTimer = nullptr; _pingDomainTimer = nullptr;
_state = 2; setState(pause0);
} else {
qDebug() << "got unexpected ICEPingReply" << senderAddr;
} }
} else { } else {