Merge pull request #1078 from birarda/graceful-stun-fail

fallback for STUN communication error/failure
This commit is contained in:
ZappoMan 2013-10-17 15:35:50 -07:00
commit c80ae942c2
3 changed files with 71 additions and 41 deletions

View file

@ -518,6 +518,19 @@ int DomainServer::run() {
int numBytesPrivateSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodePublicAddress); int numBytesPrivateSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodePublicAddress);
packetIndex += numBytesPrivateSocket; packetIndex += numBytesPrivateSocket;
if (nodePublicAddress.sin_addr.s_addr == 0) {
// this node wants to use us its STUN server
// so set the node public address to whatever we perceive the public address to be
nodePublicAddress = senderAddress;
// if the sender is on our box then leave its public address to 0 so that
// other users attempt to reach it on the same address they have for the domain-server
if (senderAddress.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
nodePublicAddress.sin_addr.s_addr = 0;
}
}
int numBytesPublicSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodeLocalAddress); int numBytesPublicSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodeLocalAddress);
packetIndex += numBytesPublicSocket; packetIndex += numBytesPublicSocket;

View file

@ -74,7 +74,8 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
_checkInPacket(NULL), _checkInPacket(NULL),
_numBytesCheckInPacket(0), _numBytesCheckInPacket(0),
_publicAddress(), _publicAddress(),
_publicPort(0) _publicPort(0),
_shouldUseDomainServerAsSTUN(0)
{ {
} }
@ -311,53 +312,68 @@ void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNo
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;
const int NUM_STUN_REQUESTS_BEFORE_FALLBACK = 5;
void NodeList::sendSTUNRequest() { void NodeList::sendSTUNRequest() {
const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io"; const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io";
const unsigned short STUN_SERVER_PORT = 3478; const unsigned short STUN_SERVER_PORT = 3478;
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER]; static int failedStunRequests = 0;
int packetIndex = 0; if (failedStunRequests < NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
int packetIndex = 0;
// leading zeros + message type
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001); const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
packetIndex += sizeof(REQUEST_MESSAGE_TYPE); // leading zeros + message type
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001);
// message length (no additional attributes are included) memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
uint16_t messageLength = 0; packetIndex += sizeof(REQUEST_MESSAGE_TYPE);
memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
packetIndex += sizeof(messageLength); // message length (no additional attributes are included)
uint16_t messageLength = 0;
memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)); memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); packetIndex += sizeof(messageLength);
// transaction ID (random 12-byte unsigned integer) memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER));
const uint NUM_TRANSACTION_ID_BYTES = 12; packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
unsigned char transactionID[NUM_TRANSACTION_ID_BYTES];
loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES); // transaction ID (random 12-byte unsigned integer)
memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID)); const uint NUM_TRANSACTION_ID_BYTES = 12;
unsigned char transactionID[NUM_TRANSACTION_ID_BYTES];
// lookup the IP for the STUN server loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES);
static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME); memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID));
for (int i = 0; i < stunInfo.addresses().size(); i++) { // lookup the IP for the STUN server
if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME);
QString stunIPAddress = stunInfo.addresses()[i].toString();
for (int i = 0; i < stunInfo.addresses().size(); i++) {
qDebug("Sending a stun request to %s\n", stunIPAddress.toLocal8Bit().constData()); if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
QString stunIPAddress = stunInfo.addresses()[i].toString();
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
STUN_SERVER_PORT, qDebug("Sending a stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
stunRequestPacket,
sizeof(stunRequestPacket)); _nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
STUN_SERVER_PORT,
break; stunRequestPacket,
sizeof(stunRequestPacket));
break;
}
} }
failedStunRequests++;
return;
} }
// if we're here this was the last failed STUN request
// use our DS as our stun server
qDebug("Failed to lookup public address via STUN server at %s:%hu. Using DS for STUN.\n",
STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
_shouldUseDomainServerAsSTUN = true;
} }
void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) { void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) {
@ -455,7 +471,7 @@ void NodeList::sendDomainServerCheckIn() {
printedDomainServerIP = true; printedDomainServerIP = true;
} }
if (_publicAddress.isNull()) { if (_publicAddress.isNull() && !_shouldUseDomainServerAsSTUN) {
// we don't know our public socket and we need to send it to the domain server // we don't know our public socket and we need to send it to the domain server
// send a STUN request to figure it out // send a STUN request to figure it out
sendSTUNRequest(); sendSTUNRequest();

View file

@ -170,6 +170,7 @@ private:
int _numBytesCheckInPacket; int _numBytesCheckInPacket;
QHostAddress _publicAddress; QHostAddress _publicAddress;
uint16_t _publicPort; uint16_t _publicPort;
bool _shouldUseDomainServerAsSTUN;
void activateSocketFromPingReply(sockaddr *nodeAddress); void activateSocketFromPingReply(sockaddr *nodeAddress);
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData); void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);