mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
continued STUN requests for keepalive, propagate socket changes
This commit is contained in:
parent
8d8278daef
commit
32680bbaad
4 changed files with 173 additions and 128 deletions
|
@ -33,17 +33,8 @@ Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* local
|
|||
_linkedData(NULL),
|
||||
_isAlive(true)
|
||||
{
|
||||
if (publicSocket) {
|
||||
_publicSocket = new sockaddr(*publicSocket);
|
||||
} else {
|
||||
_publicSocket = NULL;
|
||||
}
|
||||
|
||||
if (localSocket) {
|
||||
_localSocket = new sockaddr(*localSocket);
|
||||
} else {
|
||||
_localSocket = NULL;
|
||||
}
|
||||
setPublicSocket(publicSocket);
|
||||
setLocalSocket(localSocket);
|
||||
|
||||
pthread_mutex_init(&_mutex, 0);
|
||||
}
|
||||
|
@ -95,6 +86,32 @@ const char* Node::getTypeName() const {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::setPublicSocket(sockaddr* publicSocket) {
|
||||
if (_activeSocket == _publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (publicSocket) {
|
||||
_publicSocket = new sockaddr(*publicSocket);
|
||||
} else {
|
||||
_publicSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::setLocalSocket(sockaddr* localSocket) {
|
||||
if (_activeSocket == _localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (localSocket) {
|
||||
_localSocket = new sockaddr(*localSocket);
|
||||
} else {
|
||||
_localSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::activateLocalSocket() {
|
||||
qDebug() << "Activating local socket for node" << *this << "\n";
|
||||
_activeSocket = _localSocket;
|
||||
|
|
|
@ -47,9 +47,9 @@ public:
|
|||
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
|
||||
sockaddr* getPublicSocket() const { return _publicSocket; }
|
||||
void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; }
|
||||
void setPublicSocket(sockaddr* publicSocket);
|
||||
sockaddr* getLocalSocket() const { return _localSocket; }
|
||||
void setLocalSocket(sockaddr* localSocket) { _localSocket = localSocket; }
|
||||
void setLocalSocket(sockaddr* localSocket);
|
||||
|
||||
sockaddr* getActiveSocket() const { return _activeSocket; }
|
||||
|
||||
|
|
|
@ -71,11 +71,10 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
|||
_ownerUUID(QUuid::createUuid()),
|
||||
_numNoReplyDomainCheckIns(0),
|
||||
_assignmentServerSocket(NULL),
|
||||
_checkInPacket(NULL),
|
||||
_numBytesCheckInPacket(0),
|
||||
_publicAddress(),
|
||||
_publicPort(0),
|
||||
_shouldUseDomainServerAsSTUN(0)
|
||||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -288,11 +287,6 @@ void NodeList::reset() {
|
|||
clear();
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
delete[] _checkInPacket;
|
||||
_checkInPacket = NULL;
|
||||
|
||||
_numBytesCheckInPacket = 0;
|
||||
|
||||
delete _nodeTypesOfInterest;
|
||||
_nodeTypesOfInterest = NULL;
|
||||
|
||||
|
@ -316,62 +310,67 @@ void NodeList::sendSTUNRequest() {
|
|||
const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io";
|
||||
const unsigned short STUN_SERVER_PORT = 3478;
|
||||
|
||||
static int failedStunRequests = 0;
|
||||
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
||||
|
||||
if (failedStunRequests < NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
||||
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
||||
|
||||
int packetIndex = 0;
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||
|
||||
// leading zeros + message type
|
||||
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001);
|
||||
memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
|
||||
packetIndex += sizeof(REQUEST_MESSAGE_TYPE);
|
||||
|
||||
// message length (no additional attributes are included)
|
||||
uint16_t messageLength = 0;
|
||||
memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
|
||||
packetIndex += sizeof(messageLength);
|
||||
|
||||
memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER));
|
||||
packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
|
||||
// transaction ID (random 12-byte unsigned integer)
|
||||
const uint NUM_TRANSACTION_ID_BYTES = 12;
|
||||
unsigned char transactionID[NUM_TRANSACTION_ID_BYTES];
|
||||
loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES);
|
||||
memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID));
|
||||
|
||||
// lookup the IP for the STUN server
|
||||
static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME);
|
||||
|
||||
for (int i = 0; i < stunInfo.addresses().size(); i++) {
|
||||
if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
QString stunIPAddress = stunInfo.addresses()[i].toString();
|
||||
|
||||
qDebug("Sending a stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
|
||||
|
||||
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
|
||||
STUN_SERVER_PORT,
|
||||
stunRequestPacket,
|
||||
sizeof(stunRequestPacket));
|
||||
|
||||
break;
|
||||
int packetIndex = 0;
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||
|
||||
// leading zeros + message type
|
||||
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001);
|
||||
memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
|
||||
packetIndex += sizeof(REQUEST_MESSAGE_TYPE);
|
||||
|
||||
// message length (no additional attributes are included)
|
||||
uint16_t messageLength = 0;
|
||||
memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
|
||||
packetIndex += sizeof(messageLength);
|
||||
|
||||
memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER));
|
||||
packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
|
||||
// transaction ID (random 12-byte unsigned integer)
|
||||
const uint NUM_TRANSACTION_ID_BYTES = 12;
|
||||
unsigned char transactionID[NUM_TRANSACTION_ID_BYTES];
|
||||
loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES);
|
||||
memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID));
|
||||
|
||||
// lookup the IP for the STUN server
|
||||
static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME);
|
||||
|
||||
for (int i = 0; i < stunInfo.addresses().size(); i++) {
|
||||
if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
QString stunIPAddress = stunInfo.addresses()[i].toString();
|
||||
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qDebug("Sending intial stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
|
||||
STUN_SERVER_PORT,
|
||||
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;
|
||||
_stunRequestsSinceSuccess++;
|
||||
|
||||
if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
// 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);
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
}
|
||||
|
||||
// reset the public address and port
|
||||
_publicAddress = QHostAddress::Null;
|
||||
_publicPort = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) {
|
||||
|
@ -395,6 +394,9 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
|
|||
const int NUM_BYTES_FAMILY_ALIGN = 1;
|
||||
const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8;
|
||||
|
||||
// reset the number of failed STUN requests since last success
|
||||
_stunRequestsSinceSuccess = 0;
|
||||
|
||||
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
||||
|
||||
uint8_t addressFamily = 0;
|
||||
|
@ -416,12 +418,20 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
|
|||
memcpy(&xorMappedAddress, packetData + byteIndex, sizeof(xorMappedAddress));
|
||||
|
||||
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
_publicAddress = QHostAddress(stunAddress);
|
||||
|
||||
qDebug("Public socket received from STUN server is %s:%hu\n",
|
||||
_publicAddress.toString().toLocal8Bit().constData(),
|
||||
_publicPort);
|
||||
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||
|
||||
if (newPublicAddress != _publicAddress) {
|
||||
_publicAddress = newPublicAddress;
|
||||
|
||||
qDebug("New public socket received from STUN server is %s:%hu\n",
|
||||
_publicAddress.toString().toLocal8Bit().constData(),
|
||||
_publicPort);
|
||||
|
||||
}
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -469,62 +479,66 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
printedDomainServerIP = true;
|
||||
}
|
||||
|
||||
if (_publicAddress.isNull() && !_shouldUseDomainServerAsSTUN) {
|
||||
if (_publicAddress.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||
// 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
|
||||
sendSTUNRequest();
|
||||
} else {
|
||||
// construct the DS check in packet if we need to
|
||||
if (!_checkInPacket) {
|
||||
int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0;
|
||||
|
||||
const int IP_ADDRESS_BYTES = 4;
|
||||
|
||||
// check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination
|
||||
int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) +
|
||||
NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) +
|
||||
numBytesNodesOfInterest + sizeof(unsigned char);
|
||||
|
||||
_checkInPacket = new unsigned char[numPacketBytes];
|
||||
unsigned char* packetPosition = _checkInPacket;
|
||||
|
||||
PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
|
||||
? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
|
||||
: PACKET_TYPE_DOMAIN_LIST_REQUEST;
|
||||
|
||||
packetPosition += populateTypeAndVersion(packetPosition, nodePacketType);
|
||||
|
||||
*(packetPosition++) = _ownerType;
|
||||
|
||||
// send our owner UUID or the null one
|
||||
QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122();
|
||||
memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size());
|
||||
packetPosition += rfcOwnerUUID.size();
|
||||
|
||||
// pack our public address to send to domain-server
|
||||
packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket),
|
||||
htonl(_publicAddress.toIPv4Address()), htons(_publicPort));
|
||||
|
||||
// pack our local address to send to domain-server
|
||||
packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket),
|
||||
getLocalAddress(),
|
||||
htons(_nodeSocket.getListeningPort()));
|
||||
|
||||
// add the number of bytes for node types of interest
|
||||
*(packetPosition++) = numBytesNodesOfInterest;
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
if (numBytesNodesOfInterest > 0) {
|
||||
memcpy(packetPosition,
|
||||
_nodeTypesOfInterest,
|
||||
numBytesNodesOfInterest);
|
||||
packetPosition += numBytesNodesOfInterest;
|
||||
}
|
||||
|
||||
_numBytesCheckInPacket = packetPosition - _checkInPacket;
|
||||
int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0;
|
||||
|
||||
const int IP_ADDRESS_BYTES = 4;
|
||||
|
||||
// check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination
|
||||
int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) +
|
||||
NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) +
|
||||
numBytesNodesOfInterest + sizeof(unsigned char);
|
||||
|
||||
unsigned char* checkInPacket = new unsigned char[numPacketBytes];
|
||||
unsigned char* packetPosition = checkInPacket;
|
||||
|
||||
PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
|
||||
? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
|
||||
: PACKET_TYPE_DOMAIN_LIST_REQUEST;
|
||||
|
||||
packetPosition += populateTypeAndVersion(packetPosition, nodePacketType);
|
||||
|
||||
*(packetPosition++) = _ownerType;
|
||||
|
||||
// send our owner UUID or the null one
|
||||
QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122();
|
||||
memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size());
|
||||
packetPosition += rfcOwnerUUID.size();
|
||||
|
||||
// pack our public address to send to domain-server
|
||||
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
|
||||
htonl(_publicAddress.toIPv4Address()), htons(_publicPort));
|
||||
|
||||
// pack our local address to send to domain-server
|
||||
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
|
||||
getLocalAddress(),
|
||||
htons(_nodeSocket.getListeningPort()));
|
||||
|
||||
// add the number of bytes for node types of interest
|
||||
*(packetPosition++) = numBytesNodesOfInterest;
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
if (numBytesNodesOfInterest > 0) {
|
||||
memcpy(packetPosition,
|
||||
_nodeTypesOfInterest,
|
||||
numBytesNodesOfInterest);
|
||||
packetPosition += numBytesNodesOfInterest;
|
||||
}
|
||||
|
||||
_nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, _checkInPacket, _numBytesCheckInPacket);
|
||||
_nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, checkInPacket, packetPosition - checkInPacket);
|
||||
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
static unsigned int numDomainCheckins = 0;
|
||||
|
||||
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
||||
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
||||
sendSTUNRequest();
|
||||
}
|
||||
|
||||
// increment the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns++;
|
||||
|
@ -622,6 +636,8 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publ
|
|||
|
||||
return newNode;
|
||||
} else {
|
||||
node->lock();
|
||||
|
||||
if (node->getType() == NODE_TYPE_AUDIO_MIXER ||
|
||||
node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
// until the Audio class also uses our nodeList, we need to update
|
||||
|
@ -629,6 +645,19 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publ
|
|||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
||||
// check if we need to change this node's public or local sockets
|
||||
if (!socketMatch(publicSocket, node->getPublicSocket())) {
|
||||
node->setPublicSocket(publicSocket);
|
||||
qDebug() << "Public socket change for node" << *node << "\n";
|
||||
}
|
||||
|
||||
if (!socketMatch(localSocket, node->getLocalSocket())) {
|
||||
node->setLocalSocket(localSocket);
|
||||
qDebug() << "Local socket change for node" << *node << "\n";
|
||||
}
|
||||
|
||||
node->unlock();
|
||||
|
||||
// we had this node already, do nothing for now
|
||||
return &*node;
|
||||
}
|
||||
|
|
|
@ -167,11 +167,10 @@ private:
|
|||
pthread_t checkInWithDomainServerThread;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
sockaddr* _assignmentServerSocket;
|
||||
uchar* _checkInPacket;
|
||||
int _numBytesCheckInPacket;
|
||||
QHostAddress _publicAddress;
|
||||
uint16_t _publicPort;
|
||||
bool _shouldUseDomainServerAsSTUN;
|
||||
bool _hasCompletedInitialSTUNFailure;
|
||||
unsigned int _stunRequestsSinceSuccess;
|
||||
|
||||
void activateSocketFromNodeCommunication(sockaddr *nodeAddress);
|
||||
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
||||
|
|
Loading…
Reference in a new issue