mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 06:22:14 +02:00
Merge pull request #2637 from birarda/authentication
add optional DTLS option for domain-server to node communication
This commit is contained in:
commit
5904d19603
86 changed files with 2779 additions and 1790 deletions
|
@ -29,4 +29,18 @@ link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
|||
# link in the hifi voxels library
|
||||
link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link the hifi networking library
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# include the GnuTLS dir
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
# link GnuTLS
|
||||
target_link_libraries(${TARGET_NAME} "${GNUTLS_LIBRARY}")
|
|
@ -726,12 +726,12 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
|
|||
::wantLocalDomain = cmdOptionExists(argc, (const char**) argv,local);
|
||||
if (::wantLocalDomain) {
|
||||
printf("Local Domain MODE!\n");
|
||||
nodeList->getDomainInfo().setIPToLocalhost();
|
||||
nodeList->getDomainHandler().setIPToLocalhost();
|
||||
}
|
||||
|
||||
const char* domainHostname = getCmdOption(argc, (const char**) argv, "--domain");
|
||||
if (domainHostname) {
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(domainHostname);
|
||||
NodeList::getInstance()->getDomainHandler().setHostname(domainHostname);
|
||||
}
|
||||
|
||||
const char* packetsPerSecondCommand = getCmdOption(argc, (const char**) argv, "--pps");
|
||||
|
@ -800,11 +800,11 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
|
|||
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS);
|
||||
|
||||
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||
}
|
||||
|
|
|
@ -30,9 +30,19 @@ link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
|||
link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
|
||||
endif (UNIX)
|
||||
|
@ -41,4 +51,4 @@ IF (WIN32)
|
|||
target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
|
||||
ENDIF(WIN32)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script "${GNUTLS_LIBRARY}")
|
|
@ -149,7 +149,7 @@ void Agent::run() {
|
|||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QString scriptURLString("http://%1:8080/assignment/%2");
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainInfo().getIP().toString(),
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainHandler().getIP().toString(),
|
||||
uuidStringWithoutCurlyBraces(_uuid));
|
||||
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
#include <Logging.h>
|
||||
|
@ -32,64 +34,58 @@ int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
|||
|
||||
AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
||||
QCoreApplication(argc, argv),
|
||||
_currentAssignment()
|
||||
_currentAssignment(),
|
||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
|
||||
{
|
||||
DTLSClientSession::globalInit();
|
||||
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("assignment-client");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
QStringList argumentList = arguments();
|
||||
|
||||
// register meta type is required for queued invoke method on Assignment subclasses
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
||||
const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t";
|
||||
const char* assignmentTypeString = getCmdOption(argc, (const char**)argv, ASSIGNMENT_TYPE_OVVERIDE_OPTION);
|
||||
const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t";
|
||||
int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION);
|
||||
|
||||
Assignment::Type requestAssignmentType = Assignment::AllTypes;
|
||||
|
||||
if (assignmentTypeString) {
|
||||
// the user is asking to only be assigned to a particular type of assignment
|
||||
// so set that as the ::overridenAssignmentType to be used in requests
|
||||
requestAssignmentType = (Assignment::Type) atoi(assignmentTypeString);
|
||||
if (argumentIndex != -1) {
|
||||
requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt();
|
||||
}
|
||||
|
||||
const char ASSIGNMENT_POOL_OPTION[] = "--pool";
|
||||
const char* requestAssignmentPool = getCmdOption(argc, (const char**) argv, ASSIGNMENT_POOL_OPTION);
|
||||
const QString ASSIGNMENT_POOL_OPTION = "--pool";
|
||||
|
||||
argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION);
|
||||
|
||||
QString assignmentPool;
|
||||
|
||||
if (argumentIndex != -1) {
|
||||
assignmentPool = argumentList[argumentIndex + 1];
|
||||
}
|
||||
// setup our _requestAssignment member variable from the passed arguments
|
||||
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool);
|
||||
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);
|
||||
|
||||
// create a NodeList as an unassigned client
|
||||
NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned);
|
||||
|
||||
const char CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION[] = "-a";
|
||||
const char CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION[] = "-p";
|
||||
// check for an overriden assignment server hostname
|
||||
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a";
|
||||
|
||||
// grab the overriden assignment-server hostname from argv, if it exists
|
||||
const char* customAssignmentServerHostname = getCmdOption(argc, (const char**)argv, CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION);
|
||||
const char* customAssignmentServerPortString = getCmdOption(argc,(const char**)argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION);
|
||||
argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION);
|
||||
|
||||
HifiSockAddr customAssignmentSocket;
|
||||
|
||||
if (customAssignmentServerHostname || customAssignmentServerPortString) {
|
||||
if (argumentIndex != -1) {
|
||||
_assignmentServerHostname = argumentList[argumentIndex + 1];
|
||||
|
||||
// set the custom port or default if it wasn't passed
|
||||
unsigned short assignmentServerPort = customAssignmentServerPortString
|
||||
? atoi(customAssignmentServerPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
||||
// set the custom assignment socket on our NodeList
|
||||
HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT);
|
||||
|
||||
// set the custom hostname or default if it wasn't passed
|
||||
if (!customAssignmentServerHostname) {
|
||||
customAssignmentServerHostname = DEFAULT_ASSIGNMENT_SERVER_HOSTNAME;
|
||||
}
|
||||
|
||||
customAssignmentSocket = HifiSockAddr(customAssignmentServerHostname, assignmentServerPort);
|
||||
}
|
||||
|
||||
// set the custom assignment socket if we have it
|
||||
if (!customAssignmentSocket.isNull()) {
|
||||
nodeList->setAssignmentServerSocket(customAssignmentSocket);
|
||||
}
|
||||
|
||||
|
@ -108,6 +104,10 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
this, &AssignmentClient::handleAuthenticationRequest);
|
||||
}
|
||||
|
||||
AssignmentClient::~AssignmentClient() {
|
||||
DTLSClientSession::globalDeinit();
|
||||
}
|
||||
|
||||
void AssignmentClient::sendAssignmentRequest() {
|
||||
if (!_currentAssignment) {
|
||||
NodeList::getInstance()->sendAssignment(_requestAssignment);
|
||||
|
@ -133,12 +133,12 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
if (_currentAssignment) {
|
||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||
|
||||
// switch our nodelist domain IP and port to whoever sent us the assignment
|
||||
// switch our DomainHandler hostname and port to whoever sent us the assignment
|
||||
|
||||
nodeList->getDomainInfo().setSockAddr(senderSockAddr);
|
||||
nodeList->getDomainInfo().setAssignmentUUID(_currentAssignment->getUUID());
|
||||
nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname);
|
||||
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
|
||||
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString();
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||
|
||||
// start the deployed assignment
|
||||
AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this);
|
||||
|
|
|
@ -20,6 +20,7 @@ class AssignmentClient : public QCoreApplication {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AssignmentClient(int &argc, char **argv);
|
||||
~AssignmentClient();
|
||||
private slots:
|
||||
void sendAssignmentRequest();
|
||||
void readPendingDatagrams();
|
||||
|
@ -28,6 +29,7 @@ private slots:
|
|||
private:
|
||||
Assignment _requestAssignment;
|
||||
SharedAssignmentPointer _currentAssignment;
|
||||
QString _assignmentServerHostname;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentClient_h
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#include "Systime.h"
|
||||
#include <math.h>
|
||||
#else
|
||||
|
@ -372,7 +371,7 @@ void AudioMixer::sendStatsPacket() {
|
|||
statsObject["average_mixes_per_listener"] = 0.0;
|
||||
}
|
||||
|
||||
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||
// ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||
|
||||
_sumListeners = 0;
|
||||
_sumMixes = 0;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "AvatarMixerClientData.h"
|
||||
|
||||
AvatarMixerClientData::AvatarMixerClientData() :
|
||||
|
|
41
cmake/modules/FindGnuTLS.cmake
Normal file
41
cmake/modules/FindGnuTLS.cmake
Normal file
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# FindGnuTLS.cmake
|
||||
#
|
||||
# Try to find the GnuTLS library
|
||||
#
|
||||
# You can provide a GNUTLS_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# GNUTLS_FOUND - system found GnuTLS
|
||||
# GNUTLS_INCLUDE_DIR - the GnuTLS include directory
|
||||
# GNUTLS_LIBRARY - Link this to use GnuTLS
|
||||
#
|
||||
# Created on 3/31/2014 by Stephen Birarda
|
||||
# Copyright (c) 2014 High Fidelity
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
if (GNUTLS_LIBRARY AND GNUTLS_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(GNUTLS_FOUND TRUE)
|
||||
else ()
|
||||
set(GNUTLS_SEARCH_DIRS "${GNUTLS_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/gnutls")
|
||||
|
||||
find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h PATH_SUFFIXES include HINTS ${GNUTLS_SEARCH_DIRS})
|
||||
|
||||
find_library(GNUTLS_LIBRARY NAMES gnutls libgnutls libgnutls-28 PATH_SUFFIXES lib HINTS ${GNUTLS_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GNUTLS DEFAULT_MSG GNUTLS_INCLUDE_DIR GNUTLS_LIBRARY)
|
||||
|
||||
if (WIN32 AND NOT GNUTLS_FOUND)
|
||||
message(STATUS "If you're generating a MSVC environment, you'll need to run the command")
|
||||
message(STATUS "$GnuTLS-DIR\\bin>lib /def:libgnutls-28.def")
|
||||
message(STATUS "From the MSVC command prompt to generate the .lib file and copy it into")
|
||||
message(STATUS "the GnuTLS lib folder. Replace $GnuTLS-DIR in the command with the directory")
|
||||
message(STATUS "containing GnuTLS.")
|
||||
endif ()
|
||||
endif ()
|
|
@ -9,7 +9,7 @@
|
|||
#
|
||||
# QXMPP_FOUND - system found qxmpp
|
||||
# QXMPP_INCLUDE_DIRS - the qxmpp include directory
|
||||
# QXMPP_LIBRARIES - Link this to use qxmpp
|
||||
# QXMPP_LIBRARY - Link this to use qxmpp
|
||||
#
|
||||
# Created on 3/10/2014 by Stephen Birarda
|
||||
# Copyright 2014 High Fidelity, Inc.
|
||||
|
@ -31,14 +31,4 @@ else ()
|
|||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIR QXMPP_LIBRARY)
|
||||
|
||||
if (QXMPP_FOUND)
|
||||
if (NOT QXMPP_FIND_QUIETLY)
|
||||
message(STATUS "Found qxmpp: ${QXMPP_LIBRARY}")
|
||||
endif ()
|
||||
else ()
|
||||
if (QXMPP_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find qxmpp")
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
|
@ -17,6 +17,7 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
|
|||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
|
||||
|
@ -33,11 +34,21 @@ add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
|||
|
||||
# link the shared hifi library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# include the GnuTLS dir
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
IF (WIN32)
|
||||
target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
|
||||
ENDIF(WIN32)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network)
|
||||
# link QtNetwork and GnuTLS
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network "${GNUTLS_LIBRARY}")
|
18
domain-server/src/DTLSServerSession.cpp
Normal file
18
domain-server/src/DTLSServerSession.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// DTLSServerSession.cpp
|
||||
// domain-server/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DTLSServerSession.h"
|
||||
|
||||
DTLSServerSession::DTLSServerSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket) :
|
||||
DTLSSession(GNUTLS_SERVER, dtlsSocket, destinationSocket)
|
||||
{
|
||||
|
||||
}
|
24
domain-server/src/DTLSServerSession.h
Normal file
24
domain-server/src/DTLSServerSession.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// DTLSServerSession.h
|
||||
// domain-server/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DTLSServerSession_h
|
||||
#define hifi_DTLSServerSession_h
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
#include <DTLSSession.h>
|
||||
|
||||
class DTLSServerSession : public DTLSSession {
|
||||
public:
|
||||
DTLSServerSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket);
|
||||
};
|
||||
|
||||
#endif // hifi_DTLSServerSession_h
|
|
@ -9,8 +9,6 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
|
@ -19,13 +17,17 @@
|
|||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <HifiConfigVariantMap.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
#include "DummyDTLSSession.h"
|
||||
|
||||
#include "DomainServer.h"
|
||||
|
||||
|
@ -36,126 +38,180 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_staticAssignmentHash(),
|
||||
_assignmentQueue(),
|
||||
_nodeAuthenticationURL(),
|
||||
_redeemedTokenResponses()
|
||||
_isUsingDTLS(false),
|
||||
_x509Credentials(NULL),
|
||||
_dhParams(NULL),
|
||||
_priorityCache(NULL),
|
||||
_dtlsSessions()
|
||||
{
|
||||
gnutls_global_init();
|
||||
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("domain-server");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
_argumentList = arguments();
|
||||
int argumentIndex = 0;
|
||||
_argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
|
||||
|
||||
// check if this domain server should use no authentication or a custom hostname for authentication
|
||||
const QString DEFAULT_AUTH_OPTION = "--defaultAuth";
|
||||
const QString CUSTOM_AUTH_OPTION = "--customAuth";
|
||||
if ((argumentIndex = _argumentList.indexOf(DEFAULT_AUTH_OPTION) != -1)) {
|
||||
_nodeAuthenticationURL = QUrl(DEFAULT_NODE_AUTH_URL);
|
||||
} else if ((argumentIndex = _argumentList.indexOf(CUSTOM_AUTH_OPTION)) != -1) {
|
||||
_nodeAuthenticationURL = QUrl(_argumentList.value(argumentIndex + 1));
|
||||
if (optionallySetupDTLS()) {
|
||||
// we either read a certificate and private key or were not passed one, good to load assignments
|
||||
// and set up the node list
|
||||
qDebug() << "Setting up LimitedNodeList and assignments.";
|
||||
setupNodeListAndAssignments();
|
||||
|
||||
if (_isUsingDTLS) {
|
||||
// we're using DTLS and our NodeList socket is good to go, so make the required DTLS changes
|
||||
// DTLS requires that IP_DONTFRAG be set
|
||||
// This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
#if defined(IP_DONTFRAG) || defined(IP_MTU_DISCOVER)
|
||||
qDebug() << "Making required DTLS changes to NodeList DTLS socket.";
|
||||
|
||||
int socketHandle = LimitedNodeList::getInstance()->getDTLSSocket().socketDescriptor();
|
||||
#if defined(IP_DONTFRAG)
|
||||
int optValue = 1;yea
|
||||
setsockopt(socketHandle, IPPROTO_IP, IP_DONTFRAG, (const void*) optValue, sizeof(optValue));
|
||||
#elif defined(IP_MTU_DISCOVER)
|
||||
int optValue = 1;
|
||||
setsockopt(socketHandle, IPPROTO_IP, IP_MTU_DISCOVER, (const void*) optValue, sizeof(optValue));
|
||||
#endif
|
||||
#endif
|
||||
// connect our socket to read datagrams received on the DTLS socket
|
||||
connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DomainServer::~DomainServer() {
|
||||
if (_x509Credentials) {
|
||||
gnutls_certificate_free_credentials(*_x509Credentials);
|
||||
gnutls_priority_deinit(*_priorityCache);
|
||||
gnutls_dh_params_deinit(*_dhParams);
|
||||
|
||||
delete _x509Credentials;
|
||||
delete _priorityCache;
|
||||
delete _dhParams;
|
||||
delete _cookieKey;
|
||||
}
|
||||
gnutls_global_deinit();
|
||||
}
|
||||
|
||||
bool DomainServer::optionallySetupDTLS() {
|
||||
if (readX509KeyAndCertificate()) {
|
||||
if (_x509Credentials) {
|
||||
qDebug() << "Generating Diffie-Hellman parameters.";
|
||||
|
||||
// generate Diffie-Hellman parameters
|
||||
// When short bit length is used, it might be wise to regenerate parameters often.
|
||||
int dhBits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
|
||||
|
||||
_dhParams = new gnutls_dh_params_t;
|
||||
gnutls_dh_params_init(_dhParams);
|
||||
gnutls_dh_params_generate2(*_dhParams, dhBits);
|
||||
|
||||
qDebug() << "Successfully generated Diffie-Hellman parameters.";
|
||||
|
||||
// set the D-H paramters on the X509 credentials
|
||||
gnutls_certificate_set_dh_params(*_x509Credentials, *_dhParams);
|
||||
|
||||
// setup the key used for cookie verification
|
||||
_cookieKey = new gnutls_datum_t;
|
||||
gnutls_key_generate(_cookieKey, GNUTLS_COOKIE_KEY_SIZE);
|
||||
|
||||
_priorityCache = new gnutls_priority_t;
|
||||
const char DTLS_PRIORITY_STRING[] = "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.2:%SERVER_PRECEDENCE";
|
||||
gnutls_priority_init(_priorityCache, DTLS_PRIORITY_STRING, NULL);
|
||||
|
||||
_isUsingDTLS = true;
|
||||
|
||||
qDebug() << "Initial DTLS setup complete.";
|
||||
}
|
||||
|
||||
if (!_nodeAuthenticationURL.isEmpty()) {
|
||||
const QString DATA_SERVER_USERNAME_ENV = "HIFI_DS_USERNAME";
|
||||
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_DS_PASSWORD";
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DomainServer::readX509KeyAndCertificate() {
|
||||
const QString X509_CERTIFICATE_OPTION = "cert";
|
||||
const QString X509_PRIVATE_KEY_OPTION = "key";
|
||||
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
|
||||
|
||||
QString certPath = _argumentVariantMap.value(X509_CERTIFICATE_OPTION).toString();
|
||||
QString keyPath = _argumentVariantMap.value(X509_PRIVATE_KEY_OPTION).toString();
|
||||
|
||||
if (!certPath.isEmpty() && !keyPath.isEmpty()) {
|
||||
// the user wants to use DTLS to encrypt communication with nodes
|
||||
// let's make sure we can load the key and certificate
|
||||
_x509Credentials = new gnutls_certificate_credentials_t;
|
||||
gnutls_certificate_allocate_credentials(_x509Credentials);
|
||||
|
||||
// this node will be using an authentication server, let's make sure we have a username/password
|
||||
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
||||
QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV);
|
||||
|
||||
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
||||
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
||||
qDebug() << "Reading certificate file at" << certPath << "for DTLS.";
|
||||
qDebug() << "Reading key file at" << keyPath << "for DTLS.";
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.setAuthURL(_nodeAuthenticationURL);
|
||||
int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials,
|
||||
certPath.toLocal8Bit().constData(),
|
||||
keyPath.toLocal8Bit().constData(),
|
||||
GNUTLS_X509_FMT_PEM,
|
||||
keyPassphraseString.toLocal8Bit().constData(),
|
||||
0);
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
|
||||
connect(&accountManager, &AccountManager::loginComplete, this, &DomainServer::requestCreationFromDataServer);
|
||||
|
||||
// ask the account manager to log us in from the env variables
|
||||
accountManager.requestAccessToken(username, password);
|
||||
} else {
|
||||
qDebug() << "Authentication was requested against" << qPrintable(_nodeAuthenticationURL.toString())
|
||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Qutting!";
|
||||
|
||||
// bail out
|
||||
if (gnutlsReturn < 0) {
|
||||
qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit.";
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// auth is not requested for domain-server, setup NodeList and assignments now
|
||||
setupNodeListAndAssignments();
|
||||
qDebug() << "Successfully read certificate and private key.";
|
||||
|
||||
} else if (!certPath.isEmpty() || !keyPath.isEmpty()) {
|
||||
qDebug() << "Missing certificate or private key. domain-server will now quit.";
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestCreationFromDataServer() {
|
||||
// this slot is fired when we get a valid access token from the data-server
|
||||
// now let's ask it to set us up with a UUID
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "processCreateResponseFromDataServer";
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest("/api/v1/domains/create",
|
||||
QNetworkAccessManager::PostOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void DomainServer::processCreateResponseFromDataServer(const QJsonObject& jsonObject) {
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
// pull out the UUID the data-server is telling us to use, and complete our setup with it
|
||||
QUuid newSessionUUID = QUuid(jsonObject["data"].toObject()["uuid"].toString());
|
||||
setupNodeListAndAssignments(newSessionUUID);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processTokenRedeemResponse(const QJsonObject& jsonObject) {
|
||||
// pull out the registration token this is associated with
|
||||
QString registrationToken = jsonObject["data"].toObject()["registration_token"].toString();
|
||||
|
||||
// if we have a registration token add it to our hash of redeemed token responses
|
||||
if (!registrationToken.isEmpty()) {
|
||||
qDebug() << "Redeemed registration token" << registrationToken;
|
||||
_redeemedTokenResponses.insert(registrationToken, jsonObject);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
||||
|
||||
int argumentIndex = 0;
|
||||
|
||||
const QString CUSTOM_PORT_OPTION = "-p";
|
||||
const QString CUSTOM_PORT_OPTION = "port";
|
||||
unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
|
||||
if ((argumentIndex = _argumentList.indexOf(CUSTOM_PORT_OPTION)) != -1) {
|
||||
domainServerPort = _argumentList.value(argumentIndex + 1).toUShort();
|
||||
if (_argumentVariantMap.contains(CUSTOM_PORT_OPTION)) {
|
||||
domainServerPort = (unsigned short) _argumentVariantMap.value(CUSTOM_PORT_OPTION).toUInt();
|
||||
}
|
||||
|
||||
unsigned short domainServerDTLSPort = 0;
|
||||
|
||||
if (_isUsingDTLS) {
|
||||
domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT;
|
||||
|
||||
const QString CUSTOM_DTLS_PORT_OPTION = "dtls-port";
|
||||
|
||||
if (_argumentVariantMap.contains(CUSTOM_DTLS_PORT_OPTION)) {
|
||||
domainServerDTLSPort = (unsigned short) _argumentVariantMap.value(CUSTOM_DTLS_PORT_OPTION).toUInt();
|
||||
}
|
||||
}
|
||||
|
||||
QSet<Assignment::Type> parsedTypes(QSet<Assignment::Type>() << Assignment::AgentType);
|
||||
parseCommandLineTypeConfigs(_argumentList, parsedTypes);
|
||||
|
||||
const QString CONFIG_FILE_OPTION = "--configFile";
|
||||
if ((argumentIndex = _argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) {
|
||||
QString configFilePath = _argumentList.value(argumentIndex + 1);
|
||||
readConfigFile(configFilePath, parsedTypes);
|
||||
}
|
||||
parseAssignmentConfigs(parsedTypes);
|
||||
|
||||
populateDefaultStaticAssignmentsExcludingTypes(parsedTypes);
|
||||
|
||||
NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort);
|
||||
LimitedNodeList* nodeList = LimitedNodeList::createInstance(domainServerPort, domainServerDTLSPort);
|
||||
|
||||
// create a random UUID for this session for the domain-server
|
||||
nodeList->setSessionUUID(sessionUUID);
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
connect(nodeList, &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList, &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS);
|
||||
|
||||
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams()));
|
||||
|
||||
|
@ -163,130 +219,78 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
addStaticAssignmentsToQueue();
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes) {
|
||||
void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes) {
|
||||
// check for configs from the command line, these take precedence
|
||||
const QString CONFIG_TYPE_OPTION = "--configType";
|
||||
int clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION);
|
||||
const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)";
|
||||
QRegExp assignmentConfigRegex(ASSIGNMENT_CONFIG_REGEX_STRING);
|
||||
|
||||
// enumerate all CL config overrides and parse them to files
|
||||
while (clConfigIndex != -1) {
|
||||
int clConfigType = argumentList.value(clConfigIndex + 1).toInt();
|
||||
if (clConfigType < Assignment::AllTypes && !excludedTypes.contains((Assignment::Type) clConfigIndex)) {
|
||||
Assignment::Type assignmentType = (Assignment::Type) clConfigType;
|
||||
createStaticAssignmentsForTypeGivenConfigString((Assignment::Type) assignmentType,
|
||||
argumentList.value(clConfigIndex + 2));
|
||||
excludedTypes.insert(assignmentType);
|
||||
}
|
||||
// scan for assignment config keys
|
||||
QStringList variantMapKeys = _argumentVariantMap.keys();
|
||||
int configIndex = variantMapKeys.indexOf(assignmentConfigRegex);
|
||||
|
||||
while (configIndex != -1) {
|
||||
// figure out which assignment type this matches
|
||||
Assignment::Type assignmentType = (Assignment::Type) assignmentConfigRegex.cap(1).toInt();
|
||||
|
||||
clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION, clConfigIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to read configuration from specified path
|
||||
// returns true on success, false otherwise
|
||||
void DomainServer::readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes) {
|
||||
if (path.isEmpty()) {
|
||||
// config file not specified
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QFile::exists(path)) {
|
||||
qWarning("Specified configuration file does not exist!");
|
||||
return;
|
||||
}
|
||||
|
||||
QFile configFile(path);
|
||||
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qWarning("Can't open specified configuration file!");
|
||||
return;
|
||||
} else {
|
||||
qDebug() << "Reading configuration from" << path;
|
||||
}
|
||||
|
||||
QTextStream configStream(&configFile);
|
||||
QByteArray configStringByteArray = configStream.readAll().toUtf8();
|
||||
QJsonObject configDocObject = QJsonDocument::fromJson(configStringByteArray).object();
|
||||
configFile.close();
|
||||
|
||||
QSet<Assignment::Type> appendedExcludedTypes = excludedTypes;
|
||||
|
||||
foreach (const QString& rootStringValue, configDocObject.keys()) {
|
||||
int possibleConfigType = rootStringValue.toInt();
|
||||
|
||||
if (possibleConfigType < Assignment::AllTypes
|
||||
&& !excludedTypes.contains((Assignment::Type) possibleConfigType)) {
|
||||
// this is an appropriate config type and isn't already in our excluded types
|
||||
// we are good to parse it
|
||||
Assignment::Type assignmentType = (Assignment::Type) possibleConfigType;
|
||||
QString configString = readServerAssignmentConfig(configDocObject, rootStringValue);
|
||||
createStaticAssignmentsForTypeGivenConfigString(assignmentType, configString);
|
||||
if (assignmentType < Assignment::AllTypes && !excludedTypes.contains(assignmentType)) {
|
||||
QVariant mapValue = _argumentVariantMap[variantMapKeys[configIndex]];
|
||||
|
||||
if (mapValue.type() == QVariant::String) {
|
||||
QJsonDocument deserializedDocument = QJsonDocument::fromJson(mapValue.toString().toUtf8());
|
||||
createStaticAssignmentsForType(assignmentType, deserializedDocument.array());
|
||||
} else {
|
||||
createStaticAssignmentsForType(assignmentType, mapValue.toJsonValue().toArray());
|
||||
}
|
||||
|
||||
excludedTypes.insert(assignmentType);
|
||||
}
|
||||
|
||||
configIndex = variantMapKeys.indexOf(assignmentConfigRegex, configIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// find assignment configurations on the specified node name and json object
|
||||
// returns a string in the form of its equivalent cmd line params
|
||||
QString DomainServer::readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName) {
|
||||
QJsonArray nodeArray = jsonObject[nodeName].toArray();
|
||||
|
||||
QStringList serverConfig;
|
||||
foreach (const QJsonValue& childValue, nodeArray) {
|
||||
QString cmdParams;
|
||||
QJsonObject childObject = childValue.toObject();
|
||||
QStringList keys = childObject.keys();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
QString key = keys[i];
|
||||
QString value = childObject[key].toString();
|
||||
// both cmd line params and json keys are the same
|
||||
cmdParams += QString("--%1 %2 ").arg(key, value);
|
||||
}
|
||||
serverConfig << cmdParams;
|
||||
}
|
||||
|
||||
// according to split() calls from DomainServer::prepopulateStaticAssignmentFile
|
||||
// we shold simply join them with semicolons
|
||||
return serverConfig.join(';');
|
||||
}
|
||||
|
||||
void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) {
|
||||
qDebug() << "Inserting assignment" << *newAssignment << "to static assignment hash.";
|
||||
_staticAssignmentHash.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment));
|
||||
}
|
||||
|
||||
void DomainServer::createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString) {
|
||||
void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const QJsonArray& configArray) {
|
||||
// we have a string for config for this type
|
||||
qDebug() << "Parsing command line config for assignment type" << type;
|
||||
qDebug() << "Parsing config for assignment type" << type;
|
||||
|
||||
QStringList multiConfigList = configString.split(";", QString::SkipEmptyParts);
|
||||
int configCounter = 0;
|
||||
|
||||
const QString ASSIGNMENT_CONFIG_POOL_REGEX = "--pool\\s*([\\w-]+)";
|
||||
QRegExp poolRegex(ASSIGNMENT_CONFIG_POOL_REGEX);
|
||||
|
||||
// read each config to a payload for this type of assignment
|
||||
for (int i = 0; i < multiConfigList.size(); i++) {
|
||||
QString config = multiConfigList.at(i);
|
||||
|
||||
// check the config string for a pool
|
||||
QString assignmentPool;
|
||||
|
||||
int poolIndex = poolRegex.indexIn(config);
|
||||
|
||||
if (poolIndex != -1) {
|
||||
assignmentPool = poolRegex.cap(1);
|
||||
foreach(const QJsonValue& jsonValue, configArray) {
|
||||
if (jsonValue.isObject()) {
|
||||
QJsonObject jsonObject = jsonValue.toObject();
|
||||
|
||||
// remove the pool from the config string, the assigned node doesn't need it
|
||||
config.remove(poolIndex, poolRegex.matchedLength());
|
||||
// check the config string for a pool
|
||||
const QString ASSIGNMENT_POOL_KEY = "pool";
|
||||
QString assignmentPool;
|
||||
|
||||
QJsonValue poolValue = jsonObject[ASSIGNMENT_POOL_KEY];
|
||||
if (!poolValue.isUndefined()) {
|
||||
assignmentPool = poolValue.toString();
|
||||
|
||||
jsonObject.remove(ASSIGNMENT_POOL_KEY);
|
||||
}
|
||||
|
||||
++configCounter;
|
||||
qDebug() << "Type" << type << "config" << configCounter << "=" << jsonObject;
|
||||
|
||||
Assignment* configAssignment = new Assignment(Assignment::CreateCommand, type, assignmentPool);
|
||||
|
||||
// setup the payload as a semi-colon separated list of key = value
|
||||
QStringList payloadStringList;
|
||||
foreach(const QString& payloadKey, jsonObject.keys()) {
|
||||
QString dashes = payloadKey.size() == 1 ? "-" : "--";
|
||||
payloadStringList << QString("%1%2 %3").arg(dashes).arg(payloadKey).arg(jsonObject[payloadKey].toString());
|
||||
}
|
||||
|
||||
configAssignment->setPayload(payloadStringList.join(' ').toUtf8());
|
||||
|
||||
addStaticAssignmentToAssignmentHash(configAssignment);
|
||||
}
|
||||
|
||||
qDebug("Type %d config[%d] = %s", type, i, config.toLocal8Bit().constData());
|
||||
|
||||
Assignment* configAssignment = new Assignment(Assignment::CreateCommand, type, assignmentPool);
|
||||
|
||||
configAssignment->setPayload(config.toUtf8());
|
||||
|
||||
addStaticAssignmentToAssignmentHash(configAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,21 +306,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr) {
|
||||
// this is a node we do not recognize and we need authentication - ask them to do so
|
||||
// by providing them the hostname they should authenticate with
|
||||
QByteArray authenticationRequestPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerAuthRequest);
|
||||
|
||||
QDataStream authPacketStream(&authenticationRequestPacket, QIODevice::Append);
|
||||
authPacketStream << _nodeAuthenticationURL;
|
||||
|
||||
qDebug() << "Asking node at" << senderSockAddr << "to authenticate.";
|
||||
|
||||
// send the authentication request back to the node
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram(authenticationRequestPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
}
|
||||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
|
||||
<< NodeType::MetavoxelServer;
|
||||
|
@ -347,10 +336,14 @@ void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packe
|
|||
// create a new session UUID for this node
|
||||
QUuid nodeUUID = QUuid::createUuid();
|
||||
|
||||
SharedNodePointer newNode = NodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr);
|
||||
SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr);
|
||||
|
||||
// when the newNode is created the linked data is also created, if this was a static assignment set the UUID
|
||||
reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData())->setStaticAssignmentUUID(assignmentUUID);
|
||||
// when the newNode is created the linked data is also created
|
||||
// if this was a static assignment set the UUID, set the sendingSockAddr
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||
|
||||
nodeData->setStaticAssignmentUUID(assignmentUUID);
|
||||
nodeData->setSendingSockAddr(senderSockAddr);
|
||||
|
||||
if (!authJsonObject.isEmpty()) {
|
||||
// pull the connection secret from the authJsonObject and set it as the connection secret for this node
|
||||
|
@ -430,10 +423,13 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
if (nodeInterestList.size() > 0) {
|
||||
|
||||
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
unsigned int dataMTU = dtlsSession ? gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
|
||||
|
||||
// if the node has any interest types, send back those nodes as well
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
|
||||
|
@ -463,11 +459,15 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
|
||||
nodeDataStream << secretUUID;
|
||||
|
||||
if (broadcastPacket.size() + nodeByteArray.size() > MAX_PACKET_SIZE) {
|
||||
if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) {
|
||||
// we need to break here and start a new packet
|
||||
// so send the current one
|
||||
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
if (!dtlsSession) {
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
} else {
|
||||
dtlsSession->writeDatagram(broadcastPacket);
|
||||
}
|
||||
|
||||
// reset the broadcastPacket structure
|
||||
broadcastPacket.resize(numBroadcastPacketLeadBytes);
|
||||
|
@ -478,78 +478,180 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
broadcastPacket.append(nodeByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
// always write the last broadcastPacket
|
||||
if (!dtlsSession) {
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
} else {
|
||||
dtlsSession->writeDatagram(broadcastPacket);
|
||||
}
|
||||
}
|
||||
|
||||
// always write the last broadcastPacket
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
HifiSockAddr senderSockAddr;
|
||||
QByteArray receivedPacket;
|
||||
|
||||
static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment);
|
||||
static int numAssignmentPacketHeaderBytes = assignmentPacket.size();
|
||||
|
||||
QByteArray receivedPacket;
|
||||
|
||||
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
||||
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType requestType = packetTypeForPacket(receivedPacket);
|
||||
if (packetTypeForPacket(receivedPacket) == PacketTypeRequestAssignment
|
||||
&& nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
|
||||
// construct the requested assignment from the packet data
|
||||
Assignment requestAssignment(receivedPacket);
|
||||
|
||||
if (requestType == PacketTypeDomainConnectRequest) {
|
||||
QDataStream packetStream(receivedPacket);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
|
||||
// Suppress these for Assignment::AgentType to once per 5 seconds
|
||||
static quint64 lastNoisyMessage = usecTimestampNow();
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
const quint64 NOISY_TIME_ELAPSED = 5 * USECS_PER_SECOND;
|
||||
bool noisyMessage = false;
|
||||
if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) {
|
||||
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
|
||||
<< "from" << senderSockAddr;
|
||||
noisyMessage = true;
|
||||
}
|
||||
|
||||
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
||||
|
||||
if (assignmentToDeploy) {
|
||||
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr;
|
||||
|
||||
quint8 hasRegistrationToken;
|
||||
packetStream >> hasRegistrationToken;
|
||||
// give this assignment out, either the type matches or the requestor said they will take any
|
||||
assignmentPacket.resize(numAssignmentPacketHeaderBytes);
|
||||
|
||||
if (requiresAuthentication() && !hasRegistrationToken) {
|
||||
// we need authentication and this node did not give us a registration token - tell it to auth
|
||||
requestAuthenticationFromPotentialNode(senderSockAddr);
|
||||
} else if (requiresAuthentication()) {
|
||||
QByteArray registrationToken;
|
||||
packetStream >> registrationToken;
|
||||
|
||||
QString registrationTokenString(registrationToken.toHex());
|
||||
QJsonObject jsonForRedeemedToken = _redeemedTokenResponses.value(registrationTokenString);
|
||||
|
||||
// check if we have redeemed this token and are ready to check the node in
|
||||
if (jsonForRedeemedToken.isEmpty()) {
|
||||
// make a request against the data-server to get information required to connect to this node
|
||||
JSONCallbackParameters tokenCallbackParams;
|
||||
tokenCallbackParams.jsonCallbackReceiver = this;
|
||||
tokenCallbackParams.jsonCallbackMethod = "processTokenRedeemResponse";
|
||||
|
||||
QString redeemURLString = QString("/api/v1/nodes/redeem/%1.json").arg(registrationTokenString);
|
||||
accountManager.authenticatedRequest(redeemURLString, QNetworkAccessManager::GetOperation,
|
||||
tokenCallbackParams);
|
||||
} else if (jsonForRedeemedToken["status"].toString() != "success") {
|
||||
// we redeemed the token, but it was invalid - get the node to get another
|
||||
requestAuthenticationFromPotentialNode(senderSockAddr);
|
||||
} else {
|
||||
// we've redeemed the token for this node and are ready to start communicating with it
|
||||
// add the node to our NodeList
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr, jsonForRedeemedToken);
|
||||
}
|
||||
|
||||
// if it exists, remove this response from the in-memory hash
|
||||
_redeemedTokenResponses.remove(registrationTokenString);
|
||||
|
||||
} else {
|
||||
// we don't require authentication - add this node to our NodeList
|
||||
// and send back session UUID right away
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr);
|
||||
QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
|
||||
|
||||
assignmentStream << *assignmentToDeploy.data();
|
||||
|
||||
nodeList->getNodeSocket().writeDatagram(assignmentPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
} else {
|
||||
if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) {
|
||||
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
|
||||
<< "from" << senderSockAddr;
|
||||
noisyMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (noisyMessage) {
|
||||
lastNoisyMessage = timeNow;
|
||||
}
|
||||
} else if (!_isUsingDTLS) {
|
||||
// not using DTLS, process datagram normally
|
||||
processDatagram(receivedPacket, senderSockAddr);
|
||||
} else {
|
||||
// we're using DTLS, so tell the sender to get back to us using DTLS
|
||||
static QByteArray dtlsRequiredPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS);
|
||||
static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS);
|
||||
|
||||
if (dtlsRequiredPacket.size() == numBytesDTLSHeader) {
|
||||
// pack the port that we accept DTLS traffic on
|
||||
unsigned short dtlsPort = nodeList->getDTLSSocket().localPort();
|
||||
dtlsRequiredPacket.replace(numBytesDTLSHeader, sizeof(dtlsPort), reinterpret_cast<const char*>(&dtlsPort));
|
||||
}
|
||||
|
||||
nodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDTLSDatagrams() {
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
QUdpSocket& dtlsSocket = nodeList->getDTLSSocket();
|
||||
|
||||
static sockaddr senderSockAddr;
|
||||
static socklen_t sockAddrSize = sizeof(senderSockAddr);
|
||||
|
||||
while (dtlsSocket.hasPendingDatagrams()) {
|
||||
// check if we have an active DTLS session for this sender
|
||||
QByteArray peekDatagram(dtlsSocket.pendingDatagramSize(), 0);
|
||||
|
||||
recvfrom(dtlsSocket.socketDescriptor(), peekDatagram.data(), dtlsSocket.pendingDatagramSize(),
|
||||
MSG_PEEK, &senderSockAddr, &sockAddrSize);
|
||||
|
||||
HifiSockAddr senderHifiSockAddr(&senderSockAddr);
|
||||
DTLSServerSession* existingSession = _dtlsSessions.value(senderHifiSockAddr);
|
||||
|
||||
if (existingSession) {
|
||||
if (!existingSession->completedHandshake()) {
|
||||
// check if we have completed handshake with this user
|
||||
int handshakeReturn = gnutls_handshake(*existingSession->getGnuTLSSession());
|
||||
|
||||
} else if (requestType == PacketTypeDomainListRequest) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||
if (handshakeReturn == 0) {
|
||||
existingSession->setCompletedHandshake(true);
|
||||
} else if (gnutls_error_is_fatal(handshakeReturn)) {
|
||||
// this was a fatal error handshaking, so remove this session
|
||||
qDebug() << "Fatal error -" << gnutls_strerror(handshakeReturn) << "- during DTLS handshake with"
|
||||
<< senderHifiSockAddr;
|
||||
_dtlsSessions.remove(senderHifiSockAddr);
|
||||
}
|
||||
} else {
|
||||
// pull the data from this user off the stack and process it
|
||||
int receivedBytes = gnutls_record_recv(*existingSession->getGnuTLSSession(),
|
||||
peekDatagram.data(), peekDatagram.size());
|
||||
if (receivedBytes > 0) {
|
||||
processDatagram(peekDatagram.left(receivedBytes), senderHifiSockAddr);
|
||||
} else if (gnutls_error_is_fatal(receivedBytes)) {
|
||||
qDebug() << "Fatal error -" << gnutls_strerror(receivedBytes) << "- during DTLS handshake with"
|
||||
<< senderHifiSockAddr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// first we verify the cookie
|
||||
// see http://gnutls.org/manual/html_node/DTLS-sessions.html for why this is required
|
||||
gnutls_dtls_prestate_st prestate;
|
||||
memset(&prestate, 0, sizeof(prestate));
|
||||
int cookieValid = gnutls_dtls_cookie_verify(_cookieKey, &senderSockAddr, sizeof(senderSockAddr),
|
||||
peekDatagram.data(), peekDatagram.size(), &prestate);
|
||||
|
||||
if (cookieValid < 0) {
|
||||
// the cookie sent by the client was not valid
|
||||
// send a valid one
|
||||
DummyDTLSSession tempServerSession(LimitedNodeList::getInstance()->getDTLSSocket(), senderHifiSockAddr);
|
||||
|
||||
gnutls_dtls_cookie_send(_cookieKey, &senderSockAddr, sizeof(senderSockAddr), &prestate,
|
||||
&tempServerSession, DTLSSession::socketPush);
|
||||
|
||||
// acutally pull the peeked data off the network stack so that it gets discarded
|
||||
dtlsSocket.readDatagram(peekDatagram.data(), peekDatagram.size());
|
||||
} else {
|
||||
// cookie valid but no existing session - set up a new session now
|
||||
DTLSServerSession* newServerSession = new DTLSServerSession(LimitedNodeList::getInstance()->getDTLSSocket(),
|
||||
senderHifiSockAddr);
|
||||
gnutls_session_t* gnutlsSession = newServerSession->getGnuTLSSession();
|
||||
|
||||
gnutls_priority_set(*gnutlsSession, *_priorityCache);
|
||||
gnutls_credentials_set(*gnutlsSession, GNUTLS_CRD_CERTIFICATE, *_x509Credentials);
|
||||
gnutls_dtls_prestate_set(*gnutlsSession, &prestate);
|
||||
|
||||
// handshake to begin the session
|
||||
gnutls_handshake(*gnutlsSession);
|
||||
|
||||
qDebug() << "Beginning DTLS session with node at" << senderHifiSockAddr;
|
||||
_dtlsSessions[senderHifiSockAddr] = newServerSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType requestType = packetTypeForPacket(receivedPacket);
|
||||
|
||||
if (requestType == PacketTypeDomainListRequest) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||
|
||||
if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) {
|
||||
NodeType_t throwawayNodeType;
|
||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
||||
|
||||
|
@ -557,60 +659,22 @@ void DomainServer::readAvailableDatagrams() {
|
|||
receivedPacket, senderSockAddr);
|
||||
|
||||
SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress);
|
||||
|
||||
|
||||
// update last receive to now
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
|
||||
|
||||
sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes));
|
||||
|
||||
} else if (requestType == PacketTypeRequestAssignment) {
|
||||
|
||||
// construct the requested assignment from the packet data
|
||||
Assignment requestAssignment(receivedPacket);
|
||||
|
||||
// Suppress these for Assignment::AgentType to once per 5 seconds
|
||||
static quint64 lastNoisyMessage = usecTimestampNow();
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
const quint64 NOISY_TIME_ELAPSED = 5 * USECS_PER_SECOND;
|
||||
bool noisyMessage = false;
|
||||
if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) {
|
||||
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
|
||||
<< "from" << senderSockAddr;
|
||||
noisyMessage = true;
|
||||
}
|
||||
|
||||
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
||||
|
||||
if (assignmentToDeploy) {
|
||||
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr;
|
||||
|
||||
// give this assignment out, either the type matches or the requestor said they will take any
|
||||
assignmentPacket.resize(numAssignmentPacketHeaderBytes);
|
||||
|
||||
QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
|
||||
|
||||
assignmentStream << *assignmentToDeploy.data();
|
||||
|
||||
nodeList->getNodeSocket().writeDatagram(assignmentPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
} else {
|
||||
if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) {
|
||||
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
|
||||
<< "from" << senderSockAddr;
|
||||
noisyMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (noisyMessage) {
|
||||
lastNoisyMessage = timeNow;
|
||||
}
|
||||
} else if (requestType == PacketTypeNodeJsonStats) {
|
||||
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (matchingNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->parseJSONStatsPacket(receivedPacket);
|
||||
}
|
||||
} else {
|
||||
// new node - add this node to our NodeList
|
||||
// and send back session UUID right away
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr);
|
||||
}
|
||||
|
||||
} else if (requestType == PacketTypeNodeJsonStats) {
|
||||
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (matchingNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->parseJSONStatsPacket(receivedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -679,7 +743,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QJsonObject assignedNodesJSON;
|
||||
|
||||
// enumerate the NodeList to find the assigned nodes
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) {
|
||||
if (_staticAssignmentHash.value(node->getUUID())) {
|
||||
// add the node using the UUID as the key
|
||||
QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||
|
@ -721,7 +785,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QJsonArray nodesJSONArray;
|
||||
|
||||
// enumerate the NodeList to find the assigned nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
// add the node using the UUID as the key
|
||||
|
@ -745,7 +809,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QUuid matchingUUID = QUuid(nodeShowRegex.cap(1));
|
||||
|
||||
// see if we have a node that matches this ID
|
||||
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(matchingUUID);
|
||||
SharedNodePointer matchingNode = LimitedNodeList::getInstance()->nodeWithUUID(matchingUUID);
|
||||
if (matchingNode) {
|
||||
// create a QJsonDocument with the stats QJsonObject
|
||||
QJsonObject statsObject =
|
||||
|
@ -824,14 +888,14 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
// pull the captured string, if it exists
|
||||
QUuid deleteUUID = QUuid(nodeDeleteRegex.cap(1));
|
||||
|
||||
SharedNodePointer nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
|
||||
SharedNodePointer nodeToKill = LimitedNodeList::getInstance()->nodeWithUUID(deleteUUID);
|
||||
|
||||
if (nodeToKill) {
|
||||
// start with a 200 response
|
||||
connection->respond(HTTPConnection::StatusCode200);
|
||||
|
||||
// we have a valid UUID and node - kill the node that has this assignment
|
||||
QMetaObject::invokeMethod(NodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID));
|
||||
QMetaObject::invokeMethod(LimitedNodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID));
|
||||
|
||||
// successfully processed request
|
||||
return true;
|
||||
|
@ -840,7 +904,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
return true;
|
||||
} else if (allNodesDeleteRegex.indexIn(url.path()) != -1) {
|
||||
qDebug() << "Received request to kill all nodes.";
|
||||
NodeList::getInstance()->eraseAllNodes();
|
||||
LimitedNodeList::getInstance()->eraseAllNodes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -875,6 +939,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
|||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
if (nodeData) {
|
||||
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||
if (!nodeData->getStaticAssignmentUUID().isNull()) {
|
||||
|
@ -887,11 +952,19 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
|
|||
|
||||
// cleanup the connection secrets that we set up for this node (on the other nodes)
|
||||
foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) {
|
||||
SharedNodePointer otherNode = NodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID);
|
||||
SharedNodePointer otherNode = LimitedNodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID);
|
||||
if (otherNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
if (_isUsingDTLS) {
|
||||
// check if we need to remove a DTLS session from our in-memory hash
|
||||
DTLSServerSession* existingSession = _dtlsSessions.take(nodeData->getSendingSockAddr());
|
||||
if (existingSession) {
|
||||
delete existingSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,7 +1045,7 @@ void DomainServer::addStaticAssignmentsToQueue() {
|
|||
bool foundMatchingAssignment = false;
|
||||
|
||||
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getUUID() == staticAssignment->data()->getUUID()) {
|
||||
foundMatchingAssignment = true;
|
||||
}
|
||||
|
|
|
@ -20,18 +20,21 @@
|
|||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <HTTPManager.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "DTLSServerSession.h"
|
||||
|
||||
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
|
||||
|
||||
class DomainServer : public QCoreApplication, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DomainServer(int argc, char* argv[]);
|
||||
|
||||
bool requiresAuthentication() const { return !_nodeAuthenticationURL.isEmpty(); }
|
||||
~DomainServer();
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
|
@ -43,10 +46,17 @@ public slots:
|
|||
/// Called by NodeList to inform us a node has been killed
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
private slots:
|
||||
|
||||
void readAvailableDatagrams();
|
||||
void readAvailableDTLSDatagrams();
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
bool optionallySetupDTLS();
|
||||
bool readX509KeyAndCertificate();
|
||||
|
||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
void requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr);
|
||||
void addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr,
|
||||
const QJsonObject& authJsonObject = QJsonObject());
|
||||
int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
|
@ -55,11 +65,9 @@ private:
|
|||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||
const NodeSet& nodeInterestList);
|
||||
|
||||
void parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes);
|
||||
void readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes);
|
||||
QString readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName);
|
||||
void parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes);
|
||||
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
|
||||
void createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString);
|
||||
void createStaticAssignmentsForType(Assignment::Type type, const QJsonArray& configArray);
|
||||
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
|
||||
|
||||
SharedAssignmentPointer matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
|
||||
|
@ -76,17 +84,15 @@ private:
|
|||
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
|
||||
QQueue<SharedAssignmentPointer> _assignmentQueue;
|
||||
|
||||
QUrl _nodeAuthenticationURL;
|
||||
QVariantMap _argumentVariantMap;
|
||||
|
||||
QStringList _argumentList;
|
||||
bool _isUsingDTLS;
|
||||
gnutls_certificate_credentials_t* _x509Credentials;
|
||||
gnutls_dh_params_t* _dhParams;
|
||||
gnutls_datum_t* _cookieKey;
|
||||
gnutls_priority_t* _priorityCache;
|
||||
|
||||
QHash<QString, QJsonObject> _redeemedTokenResponses;
|
||||
private slots:
|
||||
void requestCreationFromDataServer();
|
||||
void processCreateResponseFromDataServer(const QJsonObject& jsonObject);
|
||||
void processTokenRedeemResponse(const QJsonObject& jsonObject);
|
||||
|
||||
void readAvailableDatagrams();
|
||||
QHash<HifiSockAddr, DTLSServerSession*> _dtlsSessions;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainServer_h
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
DomainServerNodeData::DomainServerNodeData() :
|
||||
_sessionSecretHash(),
|
||||
_staticAssignmentUUID(),
|
||||
_statsJSONObject()
|
||||
_statsJSONObject(),
|
||||
_sendingSockAddr()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtCore/QHash>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <NodeData.h>
|
||||
|
||||
class DomainServerNodeData : public NodeData {
|
||||
|
@ -29,6 +30,9 @@ public:
|
|||
void setStaticAssignmentUUID(const QUuid& staticAssignmentUUID) { _staticAssignmentUUID = staticAssignmentUUID; }
|
||||
const QUuid& getStaticAssignmentUUID() const { return _staticAssignmentUUID; }
|
||||
|
||||
void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; }
|
||||
const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; }
|
||||
|
||||
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
||||
private:
|
||||
QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject);
|
||||
|
@ -36,6 +40,7 @@ private:
|
|||
QHash<QUuid, QUuid> _sessionSecretHash;
|
||||
QUuid _staticAssignmentUUID;
|
||||
QJsonObject _statsJSONObject;
|
||||
HifiSockAddr _sendingSockAddr;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainServerNodeData_h
|
||||
|
|
|
@ -26,7 +26,7 @@ int main(int argc, char* argv[]) {
|
|||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
|
||||
qInstallMessageHandler(Logging::verboseMessageHandler);
|
||||
DomainServer domainServer(argc, argv);
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ elseif (UNIX)
|
|||
# include the right GL headers for UNIX
|
||||
set(GL_HEADERS "#include <GL/gl.h>\n#include <GL/glut.h>\n#include <GL/glext.h>")
|
||||
elseif (WIN32)
|
||||
add_definitions( -D_USE_MATH_DEFINES ) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions( -DWINDOWS_LEAN_AND_MEAN ) # needed to make sure windows doesn't go to crazy with its defines
|
||||
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
|
||||
|
||||
set(GL_HEADERS "#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/glut.h>")
|
||||
endif ()
|
||||
|
@ -68,6 +68,7 @@ foreach(EXTERNAL_SOURCE_SUBDIR ${EXTERNAL_SOURCE_SUBDIRS})
|
|||
endforeach(EXTERNAL_SOURCE_SUBDIR)
|
||||
|
||||
find_package(Qt5 COMPONENTS Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# grab the ui files in resources/ui
|
||||
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
||||
|
@ -120,6 +121,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
|||
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
@ -190,9 +192,14 @@ endif (QXMPP_FOUND AND NOT DISABLE_QXMPP)
|
|||
# include headers for interface and InterfaceConfig.
|
||||
include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes")
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
# include external library headers
|
||||
# use system flag so warnings are supressed
|
||||
include_directories(SYSTEM "${FACESHIFT_INCLUDE_DIRS}")
|
||||
include_directories(SYSTEM "${FACESHIFT_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}")
|
||||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
|
@ -200,6 +207,7 @@ target_link_libraries(
|
|||
"${ZLIB_LIBRARIES}"
|
||||
Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Network Qt5::OpenGL
|
||||
Qt5::Script Qt5::Svg Qt5::WebKit Qt5::WebKitWidgets Qt5::Xml Qt5::UiTools
|
||||
"${GNUTLS_LIBRARY}"
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
|
|
@ -170,6 +170,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_previousScriptLocation(),
|
||||
_logger(new FileLogger(this))
|
||||
{
|
||||
// init GnuTLS for DTLS with domain-servers
|
||||
DTLSClientSession::globalInit();
|
||||
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
||||
|
@ -227,8 +230,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
audioThread->start();
|
||||
|
||||
connect(&nodeList->getDomainInfo(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
connect(&nodeList->getDomainInfo(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
|
||||
connect(&nodeList->getDomainHandler(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled);
|
||||
|
@ -275,7 +278,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
QTimer* silentNodeTimer = new QTimer();
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->moveToThread(_nodeThread);
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS);
|
||||
|
||||
// send the identity packet for our avatar each second to our avatar mixer
|
||||
QTimer* identityPacketTimer = new QTimer();
|
||||
|
@ -398,6 +401,8 @@ Application::~Application() {
|
|||
delete _glWidget;
|
||||
|
||||
AccountManager::getInstance().destroy();
|
||||
|
||||
DTLSClientSession::globalDeinit();
|
||||
}
|
||||
|
||||
void Application::saveSettings() {
|
||||
|
@ -3046,8 +3051,7 @@ void Application::updateWindowTitle(){
|
|||
|
||||
QString username = AccountManager::getInstance().getUsername();
|
||||
QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString()
|
||||
+ " @ " + nodeList->getDomainInfo().getHostname() + buildVersion;
|
||||
|
||||
+ " @ " + nodeList->getDomainHandler().getHostname() + buildVersion;
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_Audio_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WANT_TIMEVAL
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -782,23 +782,22 @@ void Menu::editPreferences() {
|
|||
}
|
||||
|
||||
void Menu::goToDomain(const QString newDomain) {
|
||||
if (NodeList::getInstance()->getDomainInfo().getHostname() != newDomain) {
|
||||
|
||||
if (NodeList::getInstance()->getDomainHandler().getHostname() != newDomain) {
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
Application::getInstance()->getAvatar()->sendKillAvatar();
|
||||
|
||||
// give our nodeList the new domain-server hostname
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(newDomain);
|
||||
NodeList::getInstance()->getDomainHandler().setHostname(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::goToDomainDialog() {
|
||||
|
||||
QString currentDomainHostname = NodeList::getInstance()->getDomainInfo().getHostname();
|
||||
QString currentDomainHostname = NodeList::getInstance()->getDomainHandler().getHostname();
|
||||
|
||||
if (NodeList::getInstance()->getDomainInfo().getPort() != DEFAULT_DOMAIN_SERVER_PORT) {
|
||||
if (NodeList::getInstance()->getDomainHandler().getPort() != DEFAULT_DOMAIN_SERVER_PORT) {
|
||||
// add the port to the currentDomainHostname string if it is custom
|
||||
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainInfo().getPort()));
|
||||
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainHandler().getPort()));
|
||||
}
|
||||
|
||||
QInputDialog domainDialog(Application::getInstance()->getWindow());
|
||||
|
@ -991,7 +990,7 @@ void Menu::nameLocation() {
|
|||
connect(manager, &LocationManager::creationCompleted, this, &Menu::namedLocationCreated);
|
||||
NamedLocation* location = new NamedLocation(locationName,
|
||||
myAvatar->getPosition(), myAvatar->getOrientation(),
|
||||
NodeList::getInstance()->getDomainInfo().getHostname());
|
||||
NodeList::getInstance()->getDomainHandler().getHostname());
|
||||
manager->createNamedLocation(location);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1163,7 +1163,7 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
|||
QStringList coordinateItems = positionString.split(',');
|
||||
QStringList orientationItems = orientationString.split(',');
|
||||
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(domainHostnameString);
|
||||
NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString);
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
//
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WANT_TIMEVAL
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
//
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WANT_TIMEVAL
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_BandwidthMeter_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WANT_TIMEVAL
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
|
|||
shot.setText(ORIENTATION_Z, QString::number(orientation.z));
|
||||
shot.setText(ORIENTATION_W, QString::number(orientation.w));
|
||||
|
||||
shot.setText(DOMAIN_KEY, NodeList::getInstance()->getDomainInfo().getHostname());
|
||||
shot.setText(DOMAIN_KEY, NodeList::getInstance()->getDomainHandler().getHostname());
|
||||
|
||||
QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z);
|
||||
// replace decimal . with '-'
|
||||
|
|
|
@ -20,4 +20,16 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
|
|||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link GnuTLS
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${GNUTLS_LIBRARY}")
|
|
@ -20,6 +20,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <qendian.h>
|
||||
|
||||
#include <LimitedNodeList.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AudioRingBuffer.h"
|
||||
|
|
|
@ -26,5 +26,15 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
|||
# link in the hifi voxels library
|
||||
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Script)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Script "${GNUTLS_LIBRARY}")
|
|
@ -20,8 +20,19 @@ auto_mtc(${TARGET_NAME} "${ROOT_DIR}")
|
|||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME} "${AUTOMTC_SRC}")
|
||||
|
||||
# link in the networking library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script "${GNUTLS_LIBRARY}")
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include <QtDebug>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <LimitedNodeList.h>
|
||||
|
||||
#include "DatagramSequencer.h"
|
||||
#include "MetavoxelMessages.h"
|
||||
|
|
29
libraries/networking/CMakeLists.txt
Normal file
29
libraries/networking/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
if (WIN32)
|
||||
cmake_policy (SET CMP0020 NEW)
|
||||
endif (WIN32)
|
||||
|
||||
set(ROOT_DIR ../..)
|
||||
set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
|
||||
|
||||
set(TARGET_NAME networking)
|
||||
project(${TARGET_NAME})
|
||||
|
||||
find_package(Qt5 COMPONENTS Network)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
||||
# include GLM
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network "${GNUTLS_LIBRARY}")
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AccountManager.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AccountManager.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Assignment.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 8/22/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Assignment.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 8/22/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
83
libraries/networking/src/DTLSClientSession.cpp
Normal file
83
libraries/networking/src/DTLSClientSession.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// DTLSClientSession.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DomainHandler.h"
|
||||
|
||||
#include "DTLSClientSession.h"
|
||||
|
||||
gnutls_certificate_credentials_t DTLSClientSession::_x509CACredentials;
|
||||
|
||||
void DTLSClientSession::globalInit() {
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
gnutls_global_init();
|
||||
gnutls_certificate_allocate_credentials(&_x509CACredentials);
|
||||
|
||||
gnutls_certificate_set_x509_trust_mem(_x509CACredentials, DTLSSession::highFidelityCADatum(), GNUTLS_X509_FMT_PEM);
|
||||
gnutls_certificate_set_verify_function(_x509CACredentials, DTLSClientSession::verifyServerCertificate);
|
||||
}
|
||||
}
|
||||
|
||||
void DTLSClientSession::globalDeinit() {
|
||||
gnutls_certificate_free_credentials(_x509CACredentials);
|
||||
|
||||
gnutls_global_deinit();
|
||||
}
|
||||
|
||||
int DTLSClientSession::verifyServerCertificate(gnutls_session_t session) {
|
||||
unsigned int verifyStatus = 0;
|
||||
|
||||
// grab the hostname from the domain handler that this session is associated with
|
||||
DomainHandler* domainHandler = reinterpret_cast<DomainHandler*>(gnutls_session_get_ptr(session));
|
||||
qDebug() << "Checking for" << domainHandler->getHostname() << "from cert.";
|
||||
|
||||
int certReturn = gnutls_certificate_verify_peers3(session,
|
||||
domainHandler->getHostname().toLocal8Bit().constData(),
|
||||
&verifyStatus);
|
||||
|
||||
if (certReturn < 0) {
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
gnutls_certificate_type_t typeReturn = gnutls_certificate_type_get(session);
|
||||
|
||||
gnutls_datum_t printOut;
|
||||
|
||||
certReturn = gnutls_certificate_verification_status_print(verifyStatus, typeReturn, &printOut, 0);
|
||||
|
||||
if (certReturn < 0) {
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
qDebug() << "Gnutls certificate verification status:" << reinterpret_cast<char *>(printOut.data);
|
||||
|
||||
#ifdef WIN32
|
||||
free(printOut.data);
|
||||
#else
|
||||
gnutls_free(printOut.data);
|
||||
#endif
|
||||
|
||||
if (verifyStatus != 0) {
|
||||
qDebug() << "Server provided certificate for DTLS is not trusted. Can not complete handshake.";
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
} else {
|
||||
// certificate is valid, continue handshaking as normal
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DTLSClientSession::DTLSClientSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket) :
|
||||
DTLSSession(GNUTLS_CLIENT, dtlsSocket, destinationSocket)
|
||||
{
|
||||
gnutls_priority_set_direct(_gnutlsSession, "PERFORMANCE", NULL);
|
||||
gnutls_credentials_set(_gnutlsSession, GNUTLS_CRD_CERTIFICATE, _x509CACredentials);
|
||||
}
|
30
libraries/networking/src/DTLSClientSession.h
Normal file
30
libraries/networking/src/DTLSClientSession.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// DTLSClientSession.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DTLSClientSession_h
|
||||
#define hifi_DTLSClientSession_h
|
||||
|
||||
#include "DTLSSession.h"
|
||||
|
||||
class DTLSClientSession : public DTLSSession {
|
||||
public:
|
||||
DTLSClientSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket);
|
||||
|
||||
static void globalInit();
|
||||
static void globalDeinit();
|
||||
|
||||
static int verifyServerCertificate(gnutls_session_t session);
|
||||
|
||||
static gnutls_certificate_credentials_t _x509CACredentials;
|
||||
static bool _wasGloballyInitialized;
|
||||
};
|
||||
|
||||
#endif // hifi_DTLSClientSession_h
|
145
libraries/networking/src/DTLSSession.cpp
Normal file
145
libraries/networking/src/DTLSSession.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
//
|
||||
// DTLSSession.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
#include "NodeList.h"
|
||||
#include "DTLSSession.h"
|
||||
|
||||
int DTLSSession::socketPullTimeout(gnutls_transport_ptr_t ptr, unsigned int ms) {
|
||||
DTLSSession* session = static_cast<DTLSSession*>(ptr);
|
||||
QUdpSocket& dtlsSocket = session->_dtlsSocket;
|
||||
|
||||
if (dtlsSocket.hasPendingDatagrams()) {
|
||||
// peek the data on stack to see if it belongs to this session
|
||||
static sockaddr senderSockAddr;
|
||||
static socklen_t sockAddrSize = sizeof(senderSockAddr);
|
||||
|
||||
QByteArray peekDatagram(dtlsSocket.pendingDatagramSize(), 0);
|
||||
|
||||
recvfrom(dtlsSocket.socketDescriptor(), peekDatagram.data(), dtlsSocket.pendingDatagramSize(),
|
||||
MSG_PEEK, &senderSockAddr, &sockAddrSize);
|
||||
|
||||
if (HifiSockAddr(&senderSockAddr) == session->_destinationSocket) {
|
||||
// there is data for this session ready to be read
|
||||
return 1;
|
||||
} else {
|
||||
// the next data from the dtlsSocket is not for this session
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// no data available on the dtlsSocket
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t DTLSSession::socketPull(gnutls_transport_ptr_t ptr, void* buffer, size_t size) {
|
||||
DTLSSession* session = static_cast<DTLSSession*>(ptr);
|
||||
QUdpSocket& dtlsSocket = session->_dtlsSocket;
|
||||
|
||||
HifiSockAddr pulledSockAddr;
|
||||
qint64 bytesReceived = dtlsSocket.readDatagram(reinterpret_cast<char*>(buffer), size,
|
||||
pulledSockAddr.getAddressPointer(), pulledSockAddr.getPortPointer());
|
||||
if (bytesReceived == -1) {
|
||||
// no data to pull, return -1
|
||||
#if DTLS_VERBOSE_DEBUG
|
||||
qDebug() << "Received no data on call to readDatagram";
|
||||
#endif
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
if (pulledSockAddr == session->_destinationSocket) {
|
||||
// bytes received from the correct sender, return number of bytes received
|
||||
|
||||
#if DTLS_VERBOSE_DEBUG
|
||||
qDebug() << "Received" << bytesReceived << "on DTLS socket from" << pulledSockAddr;
|
||||
#endif
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
// we pulled a packet not matching this session, so output that
|
||||
qDebug() << "Denied connection from" << pulledSockAddr;
|
||||
|
||||
gnutls_transport_set_errno(session->_gnutlsSession, GNUTLS_E_AGAIN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_datum_t* DTLSSession::highFidelityCADatum() {
|
||||
static gnutls_datum_t hifiCADatum;
|
||||
static bool datumInitialized = false;
|
||||
|
||||
static unsigned char HIGHFIDELITY_ROOT_CA_CERT[] =
|
||||
"-----BEGIN CERTIFICATE-----"
|
||||
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
|
||||
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
|
||||
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
|
||||
"YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n"
|
||||
"E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n"
|
||||
"MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n"
|
||||
"BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n"
|
||||
"MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n"
|
||||
"IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n"
|
||||
"AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n"
|
||||
"2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n"
|
||||
"wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n"
|
||||
"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n"
|
||||
"HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n"
|
||||
"EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n"
|
||||
"bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n"
|
||||
"b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n"
|
||||
"c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n"
|
||||
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
|
||||
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
|
||||
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
|
||||
"-----END CERTIFICATE-----";
|
||||
|
||||
if (!datumInitialized) {
|
||||
hifiCADatum.data = HIGHFIDELITY_ROOT_CA_CERT;
|
||||
hifiCADatum.size = sizeof(HIGHFIDELITY_ROOT_CA_CERT);
|
||||
}
|
||||
|
||||
return &hifiCADatum;
|
||||
}
|
||||
|
||||
DTLSSession::DTLSSession(int end, QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket) :
|
||||
DummyDTLSSession(dtlsSocket, destinationSocket),
|
||||
_completedHandshake(false)
|
||||
{
|
||||
gnutls_init(&_gnutlsSession, end | GNUTLS_DATAGRAM | GNUTLS_NONBLOCK);
|
||||
|
||||
// see http://gnutls.org/manual/html_node/Datagram-TLS-API.html#gnutls_005fdtls_005fset_005fmtu
|
||||
const unsigned int DTLS_MAX_MTU = 1452;
|
||||
gnutls_dtls_set_mtu(_gnutlsSession, DTLS_MAX_MTU);
|
||||
|
||||
const unsigned int DTLS_HANDSHAKE_RETRANSMISSION_TIMEOUT = DOMAIN_SERVER_CHECK_IN_MSECS;
|
||||
const unsigned int DTLS_TOTAL_CONNECTION_TIMEOUT = 2 * NODE_SILENCE_THRESHOLD_MSECS;
|
||||
gnutls_dtls_set_timeouts(_gnutlsSession, DTLS_HANDSHAKE_RETRANSMISSION_TIMEOUT, DTLS_TOTAL_CONNECTION_TIMEOUT);
|
||||
|
||||
gnutls_transport_set_ptr(_gnutlsSession, this);
|
||||
gnutls_transport_set_push_function(_gnutlsSession, DummyDTLSSession::socketPush);
|
||||
gnutls_transport_set_pull_function(_gnutlsSession, socketPull);
|
||||
gnutls_transport_set_pull_timeout_function(_gnutlsSession, socketPullTimeout);
|
||||
}
|
||||
|
||||
DTLSSession::~DTLSSession() {
|
||||
gnutls_deinit(_gnutlsSession);
|
||||
}
|
||||
|
||||
void DTLSSession::setCompletedHandshake(bool completedHandshake) {
|
||||
_completedHandshake = completedHandshake;
|
||||
qDebug() << "Completed DTLS handshake with" << _destinationSocket;
|
||||
}
|
||||
|
||||
qint64 DTLSSession::writeDatagram(const QByteArray& datagram) {
|
||||
// we don't need to put a hash in this packet, so just send it off
|
||||
return gnutls_record_send(_gnutlsSession, datagram.data(), datagram.size());
|
||||
}
|
44
libraries/networking/src/DTLSSession.h
Normal file
44
libraries/networking/src/DTLSSession.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// DTLSSession.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-01.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DTLSSession_h
|
||||
#define hifi_DTLSSession_h
|
||||
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "DummyDTLSSession.h"
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
class DTLSSession : public DummyDTLSSession {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DTLSSession(int end, QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket);
|
||||
~DTLSSession();
|
||||
|
||||
static int socketPullTimeout(gnutls_transport_ptr_t ptr, unsigned int ms);
|
||||
static ssize_t socketPull(gnutls_transport_ptr_t ptr, void* buffer, size_t size);
|
||||
|
||||
static gnutls_datum_t* highFidelityCADatum();
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram);
|
||||
|
||||
gnutls_session_t* getGnuTLSSession() { return &_gnutlsSession; }
|
||||
|
||||
bool completedHandshake() const { return _completedHandshake; }
|
||||
void setCompletedHandshake(bool completedHandshake);
|
||||
protected:
|
||||
gnutls_session_t _gnutlsSession;
|
||||
bool _completedHandshake;
|
||||
};
|
||||
|
||||
#endif // hifi_DTLSSession_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// DataServerAccountInfo.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// DataServerAccountInfo.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/21/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
183
libraries/networking/src/DomainHandler.cpp
Normal file
183
libraries/networking/src/DomainHandler.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// DomainHandler.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
#include "NodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
#include "DomainHandler.h"
|
||||
|
||||
DomainHandler::DomainHandler(QObject* parent) :
|
||||
QObject(parent),
|
||||
_uuid(),
|
||||
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_assignmentUUID(),
|
||||
_isConnected(false),
|
||||
_dtlsSession(NULL),
|
||||
_handshakeTimer(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DomainHandler::~DomainHandler() {
|
||||
delete _dtlsSession;
|
||||
}
|
||||
|
||||
void DomainHandler::clearConnectionInfo() {
|
||||
_uuid = QUuid();
|
||||
_isConnected = false;
|
||||
|
||||
if (_handshakeTimer) {
|
||||
_handshakeTimer->stop();
|
||||
delete _handshakeTimer;
|
||||
_handshakeTimer = NULL;
|
||||
}
|
||||
|
||||
delete _dtlsSession;
|
||||
_dtlsSession = NULL;
|
||||
}
|
||||
|
||||
void DomainHandler::reset() {
|
||||
clearConnectionInfo();
|
||||
_hostname = QString();
|
||||
_sockAddr.setAddress(QHostAddress::Null);
|
||||
|
||||
delete _dtlsSession;
|
||||
_dtlsSession = NULL;
|
||||
}
|
||||
|
||||
const unsigned int DTLS_HANDSHAKE_INTERVAL_MSECS = 100;
|
||||
|
||||
void DomainHandler::initializeDTLSSession() {
|
||||
if (!_dtlsSession) {
|
||||
_dtlsSession = new DTLSClientSession(NodeList::getInstance()->getDTLSSocket(), _sockAddr);
|
||||
|
||||
gnutls_session_set_ptr(*_dtlsSession->getGnuTLSSession(), this);
|
||||
|
||||
// start a timer to complete the handshake process
|
||||
_handshakeTimer = new QTimer(this);
|
||||
connect(_handshakeTimer, &QTimer::timeout, this, &DomainHandler::completeDTLSHandshake);
|
||||
|
||||
// start the handshake right now
|
||||
completeDTLSHandshake();
|
||||
|
||||
// start the timer to finish off the handshake
|
||||
_handshakeTimer->start(DTLS_HANDSHAKE_INTERVAL_MSECS);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) {
|
||||
if (_sockAddr != sockAddr) {
|
||||
// we should reset on a sockAddr change
|
||||
reset();
|
||||
// change the sockAddr
|
||||
_sockAddr = sockAddr;
|
||||
}
|
||||
|
||||
// some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification
|
||||
_hostname = hostname;
|
||||
}
|
||||
|
||||
void DomainHandler::setHostname(const QString& hostname) {
|
||||
|
||||
if (hostname != _hostname) {
|
||||
// re-set the domain info so that auth information is reloaded
|
||||
reset();
|
||||
|
||||
int colonIndex = hostname.indexOf(':');
|
||||
|
||||
if (colonIndex > 0) {
|
||||
// the user has included a custom DS port with the hostname
|
||||
|
||||
// the new hostname is everything up to the colon
|
||||
_hostname = hostname.left(colonIndex);
|
||||
|
||||
// grab the port by reading the string after the colon
|
||||
_sockAddr.setPort(atoi(hostname.mid(colonIndex + 1, hostname.size()).toLocal8Bit().constData()));
|
||||
|
||||
qDebug() << "Updated hostname to" << _hostname << "and port to" << _sockAddr.getPort();
|
||||
|
||||
} else {
|
||||
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
||||
_hostname = hostname;
|
||||
_sockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT);
|
||||
}
|
||||
|
||||
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
||||
qDebug("Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||
|
||||
emit hostnameChanged(_hostname);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainHandler::completeDTLSHandshake() {
|
||||
int handshakeReturn = gnutls_handshake(*_dtlsSession->getGnuTLSSession());
|
||||
|
||||
if (handshakeReturn == 0) {
|
||||
// we've shaken hands, so we're good to go now
|
||||
_dtlsSession->setCompletedHandshake(true);
|
||||
|
||||
_handshakeTimer->stop();
|
||||
delete _handshakeTimer;
|
||||
_handshakeTimer = NULL;
|
||||
|
||||
// emit a signal so NodeList can handle incoming DTLS packets
|
||||
emit completedDTLSHandshake();
|
||||
|
||||
} else if (gnutls_error_is_fatal(handshakeReturn)) {
|
||||
// this was a fatal error handshaking, so remove this session
|
||||
qDebug() << "Fatal error -" << gnutls_strerror(handshakeReturn)
|
||||
<< "- during DTLS handshake with DS at"
|
||||
<< qPrintable((_hostname.isEmpty() ? _sockAddr.getAddress().toString() : _hostname));
|
||||
|
||||
clearConnectionInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
||||
for (int i = 0; i < hostInfo.addresses().size(); i++) {
|
||||
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_sockAddr.setAddress(hostInfo.addresses()[i]);
|
||||
qDebug("DS at %s is at %s", _hostname.toLocal8Bit().constData(),
|
||||
_sockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here then we failed to lookup the address
|
||||
qDebug("Failed domain server lookup");
|
||||
}
|
||||
|
||||
void DomainHandler::setIsConnected(bool isConnected) {
|
||||
if (_isConnected != isConnected) {
|
||||
_isConnected = isConnected;
|
||||
|
||||
if (_isConnected) {
|
||||
emit connectedToDomain(_hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) {
|
||||
// figure out the port that the DS wants us to use for us to talk to them with DTLS
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket);
|
||||
|
||||
unsigned short dtlsPort = 0;
|
||||
memcpy(&dtlsPort, dtlsRequirementPacket.data() + numBytesPacketHeader, sizeof(dtlsPort));
|
||||
|
||||
qDebug() << "domain-server DTLS port changed to" << dtlsPort << "- Enabling DTLS.";
|
||||
|
||||
_sockAddr.setPort(dtlsPort);
|
||||
|
||||
initializeDTLSSession();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// DomainInfo.h
|
||||
// libraries/shared/src
|
||||
// DomainHandler.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
|
@ -9,28 +9,30 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DomainInfo_h
|
||||
#define hifi_DomainInfo_h
|
||||
#ifndef hifi_DomainHandler_h
|
||||
#define hifi_DomainHandler_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "DTLSClientSession.h"
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io";
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
|
||||
|
||||
class DomainInfo : public QObject {
|
||||
class DomainHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DomainInfo();
|
||||
DomainHandler(QObject* parent = 0);
|
||||
~DomainHandler();
|
||||
|
||||
void clearConnectionInfo();
|
||||
|
||||
void parseAuthInformationFromJsonObject(const QJsonObject& jsonObject);
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
|
@ -41,43 +43,40 @@ public:
|
|||
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
||||
|
||||
const HifiSockAddr& getSockAddr() { return _sockAddr; }
|
||||
void setSockAddr(const HifiSockAddr& sockAddr);
|
||||
void setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname);
|
||||
|
||||
unsigned short getPort() const { return _sockAddr.getPort(); }
|
||||
|
||||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||
|
||||
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
||||
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
|
||||
|
||||
const QByteArray& getRegistrationToken() const { return _registrationToken; }
|
||||
|
||||
const QUrl& getRootAuthenticationURL() const { return _rootAuthenticationURL; }
|
||||
void setRootAuthenticationURL(const QUrl& rootAuthenticationURL) { _rootAuthenticationURL = rootAuthenticationURL; }
|
||||
|
||||
bool isConnected() const { return _isConnected; }
|
||||
void setIsConnected(bool isConnected);
|
||||
|
||||
private slots:
|
||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||
DTLSClientSession* getDTLSSession() { return _dtlsSession; }
|
||||
|
||||
void logout();
|
||||
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
|
||||
|
||||
private slots:
|
||||
void completeDTLSHandshake();
|
||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||
signals:
|
||||
void hostnameChanged(const QString& hostname);
|
||||
void connectedToDomain(const QString& hostname);
|
||||
void completedDTLSHandshake();
|
||||
void DTLSConnectionLost();
|
||||
|
||||
private:
|
||||
void reset();
|
||||
void initializeDTLSSession();
|
||||
|
||||
QUuid _uuid;
|
||||
QString _hostname;
|
||||
HifiSockAddr _sockAddr;
|
||||
QUuid _assignmentUUID;
|
||||
QUuid _connectionSecret;
|
||||
QByteArray _registrationToken;
|
||||
QUrl _rootAuthenticationURL;
|
||||
QString _publicKey;
|
||||
bool _isConnected;
|
||||
DTLSClientSession* _dtlsSession;
|
||||
QTimer* _handshakeTimer;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainInfo_h
|
||||
#endif // hifi_DomainHandler_h
|
31
libraries/networking/src/DummyDTLSSession.cpp
Normal file
31
libraries/networking/src/DummyDTLSSession.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// DummyDTLSSession.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-04.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DummyDTLSSession.h"
|
||||
|
||||
ssize_t DummyDTLSSession::socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size) {
|
||||
DummyDTLSSession* session = static_cast<DummyDTLSSession*>(ptr);
|
||||
QUdpSocket& dtlsSocket = session->_dtlsSocket;
|
||||
|
||||
#if DTLS_VERBOSE_DEBUG
|
||||
qDebug() << "Pushing a message of size" << size << "to" << session->_destinationSocket;
|
||||
#endif
|
||||
|
||||
return dtlsSocket.writeDatagram(reinterpret_cast<const char*>(buffer), size,
|
||||
session->_destinationSocket.getAddress(), session->_destinationSocket.getPort());
|
||||
}
|
||||
|
||||
DummyDTLSSession::DummyDTLSSession(QUdpSocket& dtlsSocket, const HifiSockAddr& destinationSocket) :
|
||||
_dtlsSocket(dtlsSocket),
|
||||
_destinationSocket(destinationSocket)
|
||||
{
|
||||
|
||||
}
|
34
libraries/networking/src/DummyDTLSSession.h
Normal file
34
libraries/networking/src/DummyDTLSSession.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// DummyDTLSSession.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-04.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DummyDTLSSession_h
|
||||
#define hifi_DummyDTLSSession_h
|
||||
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
#define DTLS_VERBOSE_DEBUG 0
|
||||
|
||||
class DummyDTLSSession : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DummyDTLSSession(QUdpSocket& dtlsSocket, const HifiSockAddr& destinationSocket);
|
||||
|
||||
static ssize_t socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size);
|
||||
protected:
|
||||
QUdpSocket& _dtlsSocket;
|
||||
HifiSockAddr _destinationSocket;
|
||||
};
|
||||
|
||||
#endif // hifi_DummyDTLSSession_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// HifiSockAddr.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 11/26/2013.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -9,12 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
static int hifiSockAddrMetaTypeId = qMetaTypeId<HifiSockAddr>();
|
||||
|
||||
HifiSockAddr::HifiSockAddr() :
|
||||
|
@ -47,6 +47,16 @@ HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) {
|
|||
}
|
||||
}
|
||||
|
||||
HifiSockAddr::HifiSockAddr(const sockaddr* sockaddr) {
|
||||
_address = QHostAddress(sockaddr);
|
||||
|
||||
if (sockaddr->sa_family == AF_INET) {
|
||||
_port = ntohs(reinterpret_cast<const sockaddr_in*>(sockaddr)->sin_port);
|
||||
} else {
|
||||
_port = ntohs(reinterpret_cast<const sockaddr_in6*>(sockaddr)->sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
HifiSockAddr& HifiSockAddr::operator=(const HifiSockAddr& rhsSockAddr) {
|
||||
_address = rhsSockAddr._address;
|
||||
_port = rhsSockAddr._port;
|
||||
|
@ -85,13 +95,13 @@ quint32 getHostOrderLocalAddress() {
|
|||
static int localAddress = 0;
|
||||
|
||||
if (localAddress == 0) {
|
||||
foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
|
||||
if (interface.flags() & QNetworkInterface::IsUp
|
||||
&& interface.flags() & QNetworkInterface::IsRunning
|
||||
&& interface.flags() & ~QNetworkInterface::IsLoopBack) {
|
||||
foreach(const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) {
|
||||
if (networkInterface.flags() & QNetworkInterface::IsUp
|
||||
&& networkInterface.flags() & QNetworkInterface::IsRunning
|
||||
&& networkInterface.flags() & ~QNetworkInterface::IsLoopBack) {
|
||||
// we've decided that this is the active NIC
|
||||
// enumerate it's addresses to grab the IPv4 address
|
||||
foreach(const QNetworkAddressEntry &entry, interface.addressEntries()) {
|
||||
foreach(const QNetworkAddressEntry &entry, networkInterface.addressEntries()) {
|
||||
// make sure it's an IPv4 address that isn't the loopback
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) {
|
||||
qDebug("Node's local address is %s", entry.ip().toString().toLocal8Bit().constData());
|
||||
|
@ -112,3 +122,8 @@ quint32 getHostOrderLocalAddress() {
|
|||
// return the looked up local address
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
uint qHash(const HifiSockAddr& key, uint seed) {
|
||||
// use the existing QHostAddress and quint16 hash functions to get our hash
|
||||
return qHash(key.getAddress(), seed) + qHash(key.getPort(), seed);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// HifiSockAddr.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 11/26/2013.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -12,6 +12,13 @@
|
|||
#ifndef hifi_HifiSockAddr_h
|
||||
#define hifi_HifiSockAddr_h
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <QtNetwork/QHostAddress>
|
||||
|
||||
class HifiSockAddr {
|
||||
|
@ -20,6 +27,7 @@ public:
|
|||
HifiSockAddr(const QHostAddress& address, quint16 port);
|
||||
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
||||
HifiSockAddr(const QString& hostname, quint16 hostOrderPort);
|
||||
HifiSockAddr(const sockaddr* sockaddr);
|
||||
|
||||
bool isNull() const { return _address.isNull() && _port == 0; }
|
||||
|
||||
|
@ -48,6 +56,8 @@ private:
|
|||
quint16 _port;
|
||||
};
|
||||
|
||||
uint qHash(const HifiSockAddr& key, uint seed);
|
||||
|
||||
quint32 getHostOrderLocalAddress();
|
||||
|
||||
Q_DECLARE_METATYPE(HifiSockAddr)
|
444
libraries/networking/src/LimitedNodeList.cpp
Normal file
444
libraries/networking/src/LimitedNodeList.cpp
Normal file
|
@ -0,0 +1,444 @@
|
|||
//
|
||||
// LimitedNodeList.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "Logging.h"
|
||||
#include "LimitedNodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UUID.h"
|
||||
|
||||
const char SOLO_NODE_TYPES[2] = {
|
||||
NodeType::AvatarMixer,
|
||||
NodeType::AudioMixer
|
||||
};
|
||||
|
||||
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data-web.highfidelity.io");
|
||||
|
||||
LimitedNodeList* LimitedNodeList::_sharedInstance = NULL;
|
||||
|
||||
LimitedNodeList* LimitedNodeList::createInstance(unsigned short socketListenPort, unsigned short dtlsPort) {
|
||||
if (!_sharedInstance) {
|
||||
NodeType::init();
|
||||
|
||||
_sharedInstance = new LimitedNodeList(socketListenPort, dtlsPort);
|
||||
|
||||
// register the SharedNodePointer meta-type for signals/slots
|
||||
qRegisterMetaType<SharedNodePointer>();
|
||||
} else {
|
||||
qDebug("LimitedNodeList createInstance called with existing instance.");
|
||||
}
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
LimitedNodeList* LimitedNodeList::getInstance() {
|
||||
if (!_sharedInstance) {
|
||||
qDebug("LimitedNodeList getInstance called before call to createInstance. Returning NULL pointer.");
|
||||
}
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
|
||||
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
||||
_sessionUUID(),
|
||||
_nodeHash(),
|
||||
_nodeHashMutex(QMutex::Recursive),
|
||||
_nodeSocket(this),
|
||||
_dtlsSocket(NULL),
|
||||
_numCollectedPackets(0),
|
||||
_numCollectedBytes(0),
|
||||
_packetStatTimer()
|
||||
{
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);
|
||||
qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||
|
||||
if (dtlsListenPort > 0) {
|
||||
// only create the DTLS socket during constructor if a custom port is passed
|
||||
_dtlsSocket = new QUdpSocket(this);
|
||||
|
||||
_dtlsSocket->bind(QHostAddress::AnyIPv4, dtlsListenPort);
|
||||
qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
|
||||
}
|
||||
|
||||
const int LARGER_SNDBUF_SIZE = 1048576;
|
||||
changeSendSocketBufferSize(LARGER_SNDBUF_SIZE);
|
||||
|
||||
_packetStatTimer.start();
|
||||
}
|
||||
|
||||
void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
|
||||
QUuid oldUUID = _sessionUUID;
|
||||
_sessionUUID = sessionUUID;
|
||||
|
||||
if (sessionUUID != oldUUID) {
|
||||
qDebug() << "NodeList UUID changed from" << uuidStringWithoutCurlyBraces(oldUUID)
|
||||
<< "to" << uuidStringWithoutCurlyBraces(_sessionUUID);
|
||||
emit uuidChanged(sessionUUID);
|
||||
}
|
||||
}
|
||||
|
||||
QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
||||
if (!_dtlsSocket) {
|
||||
// DTLS socket getter called but no DTLS socket exists, create it now
|
||||
_dtlsSocket = new QUdpSocket(this);
|
||||
|
||||
_dtlsSocket->bind(QHostAddress::AnyIPv4, 0, QAbstractSocket::DontShareAddress);
|
||||
qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
|
||||
}
|
||||
|
||||
return *_dtlsSocket;
|
||||
}
|
||||
|
||||
void LimitedNodeList::changeSendSocketBufferSize(int numSendBytes) {
|
||||
// change the socket send buffer size to be 1MB
|
||||
int oldBufferSize = 0;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
int sizeOfInt = sizeof(oldBufferSize);
|
||||
#else
|
||||
unsigned int sizeOfInt = sizeof(oldBufferSize);
|
||||
#endif
|
||||
|
||||
getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&oldBufferSize), &sizeOfInt);
|
||||
|
||||
setsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&numSendBytes),
|
||||
sizeof(numSendBytes));
|
||||
|
||||
int newBufferSize = 0;
|
||||
getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&newBufferSize), &sizeOfInt);
|
||||
|
||||
qDebug() << "Changed socket send buffer size from" << oldBufferSize << "to" << newBufferSize << "bytes";
|
||||
}
|
||||
|
||||
bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
||||
PacketType checkType = packetTypeForPacket(packet);
|
||||
if (packet[1] != versionForPacketType(checkType)
|
||||
&& checkType != PacketTypeStunResponse) {
|
||||
PacketType mismatchType = packetTypeForPacket(packet);
|
||||
int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());
|
||||
|
||||
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
|
||||
|
||||
QUuid senderUUID = uuidFromPacketHeader(packet);
|
||||
if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {
|
||||
qDebug() << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"
|
||||
<< uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"
|
||||
<< qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";
|
||||
|
||||
versionDebugSuppressMap.insert(senderUUID, checkType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NON_VERIFIED_PACKETS.contains(checkType)) {
|
||||
// figure out which node this is from
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
if (sendingNode) {
|
||||
// check if the md5 hash in the header matches the hash we would expect
|
||||
if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Packet hash mismatch on" << checkType << "- Sender"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Packet of type" << checkType << "received from unknown node with UUID"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
if (!connectionSecret.isNull()) {
|
||||
// setup the MD5 hash for source verification in the header
|
||||
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
|
||||
}
|
||||
|
||||
// stat collection for packets
|
||||
++_numCollectedPackets;
|
||||
_numCollectedBytes += datagram.size();
|
||||
|
||||
qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
if (bytesWritten < 0) {
|
||||
qDebug() << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString();
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
if (destinationNode) {
|
||||
// if we don't have an ovveriden address, assume they want to send to the node's active socket
|
||||
const HifiSockAddr* destinationSockAddr = &overridenSockAddr;
|
||||
if (overridenSockAddr.isNull()) {
|
||||
if (destinationNode->getActiveSocket()) {
|
||||
// use the node's active socket as the destination socket
|
||||
destinationSockAddr = destinationNode->getActiveSocket();
|
||||
} else {
|
||||
// we don't have a socket to send to, return 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
|
||||
}
|
||||
|
||||
// didn't have a destinationNode to send to, return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) {
|
||||
return writeDatagram(datagram, destinationSockAddr, QUuid());
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
return writeDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
|
||||
}
|
||||
|
||||
void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
// the node decided not to do anything with this packet
|
||||
// if it comes from a known source we should keep that node alive
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
if (matchingNode) {
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
}
|
||||
|
||||
int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray &packet) {
|
||||
QMutexLocker locker(&matchingNode->getMutex());
|
||||
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
matchingNode->recordBytesReceived(packet.size());
|
||||
|
||||
if (!matchingNode->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(matchingNode.data());
|
||||
}
|
||||
|
||||
QMutexLocker linkedDataLocker(&matchingNode->getLinkedData()->getMutex());
|
||||
|
||||
return matchingNode->getLinkedData()->parseData(packet);
|
||||
}
|
||||
|
||||
int LimitedNodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
|
||||
if (matchingNode) {
|
||||
updateNodeWithDataFromPacket(matchingNode, packet);
|
||||
}
|
||||
|
||||
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) {
|
||||
const int WAIT_TIME = 10; // wait up to 10ms in the try lock case
|
||||
SharedNodePointer node;
|
||||
// if caller wants us to block and guarantee the correct answer, then honor that request
|
||||
if (blockingLock) {
|
||||
// this will block till we can get access
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
node = _nodeHash.value(nodeUUID);
|
||||
} else if (_nodeHashMutex.tryLock(WAIT_TIME)) { // some callers are willing to get wrong answers but not block
|
||||
node = _nodeHash.value(nodeUUID);
|
||||
_nodeHashMutex.unlock();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::sendingNodeForPacket(const QByteArray& packet) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(packet);
|
||||
|
||||
// return the matching node, or NULL if there is no match
|
||||
return nodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
NodeHash LimitedNodeList::getNodeHash() {
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
return NodeHash(_nodeHash);
|
||||
}
|
||||
|
||||
void LimitedNodeList::eraseAllNodes() {
|
||||
qDebug() << "Clearing the NodeList. Deleting all nodes in list.";
|
||||
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
|
||||
NodeHash::iterator nodeItem = _nodeHash.begin();
|
||||
|
||||
// iterate the nodes in the list
|
||||
while (nodeItem != _nodeHash.end()) {
|
||||
nodeItem = killNodeAtHashIterator(nodeItem);
|
||||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::reset() {
|
||||
eraseAllNodes();
|
||||
}
|
||||
|
||||
void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
|
||||
NodeHash::iterator nodeItemToKill = _nodeHash.find(nodeUUID);
|
||||
if (nodeItemToKill != _nodeHash.end()) {
|
||||
killNodeAtHashIterator(nodeItemToKill);
|
||||
}
|
||||
}
|
||||
|
||||
NodeHash::iterator LimitedNodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill) {
|
||||
qDebug() << "Killed" << *nodeItemToKill.value();
|
||||
emit nodeKilled(nodeItemToKill.value());
|
||||
return _nodeHash.erase(nodeItemToKill);
|
||||
}
|
||||
|
||||
void LimitedNodeList::processKillNode(const QByteArray& dataByteArray) {
|
||||
// read the node id
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(dataByteArray), NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// kill the node with this UUID, if it exists
|
||||
killNodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
_nodeHashMutex.lock();
|
||||
|
||||
if (!_nodeHash.contains(uuid)) {
|
||||
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
|
||||
SharedNodePointer newNodeSharedPointer(newNode, &QObject::deleteLater);
|
||||
|
||||
_nodeHash.insert(newNode->getUUID(), newNodeSharedPointer);
|
||||
|
||||
_nodeHashMutex.unlock();
|
||||
|
||||
qDebug() << "Added" << *newNode;
|
||||
|
||||
emit nodeAdded(newNodeSharedPointer);
|
||||
|
||||
return newNodeSharedPointer;
|
||||
} else {
|
||||
_nodeHashMutex.unlock();
|
||||
|
||||
return updateSocketsForNode(uuid, publicSocket, localSocket);
|
||||
}
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::updateSocketsForNode(const QUuid& uuid,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
|
||||
SharedNodePointer matchingNode = nodeWithUUID(uuid);
|
||||
|
||||
if (matchingNode) {
|
||||
// perform appropriate updates to this node
|
||||
QMutexLocker locker(&matchingNode->getMutex());
|
||||
|
||||
// check if we need to change this node's public or local sockets
|
||||
if (publicSocket != matchingNode->getPublicSocket()) {
|
||||
matchingNode->setPublicSocket(publicSocket);
|
||||
qDebug() << "Public socket change for node" << *matchingNode;
|
||||
}
|
||||
|
||||
if (localSocket != matchingNode->getLocalSocket()) {
|
||||
matchingNode->setLocalSocket(localSocket);
|
||||
qDebug() << "Local socket change for node" << *matchingNode;
|
||||
}
|
||||
}
|
||||
|
||||
return matchingNode;
|
||||
}
|
||||
|
||||
unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
||||
unsigned n = 0;
|
||||
|
||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||
// only send to the NodeTypes we are asked to send to.
|
||||
if (destinationNodeTypes.contains(node->getType())) {
|
||||
writeDatagram(packet, node);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::soloNodeOfType(char nodeType) {
|
||||
|
||||
if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
|
||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||
if (node->getType() == nodeType) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
void LimitedNodeList::getPacketStats(float& packetsPerSecond, float& bytesPerSecond) {
|
||||
packetsPerSecond = (float) _numCollectedPackets / ((float) _packetStatTimer.elapsed() / 1000.0f);
|
||||
bytesPerSecond = (float) _numCollectedBytes / ((float) _packetStatTimer.elapsed() / 1000.0f);
|
||||
}
|
||||
|
||||
void LimitedNodeList::resetPacketStats() {
|
||||
_numCollectedPackets = 0;
|
||||
_numCollectedBytes = 0;
|
||||
_packetStatTimer.restart();
|
||||
}
|
||||
|
||||
void LimitedNodeList::removeSilentNodes() {
|
||||
|
||||
_nodeHashMutex.lock();
|
||||
|
||||
NodeHash::iterator nodeItem = _nodeHash.begin();
|
||||
|
||||
while (nodeItem != _nodeHash.end()) {
|
||||
SharedNodePointer node = nodeItem.value();
|
||||
|
||||
node->getMutex().lock();
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > (NODE_SILENCE_THRESHOLD_MSECS * 1000)) {
|
||||
// call our private method to kill this node (removes it and emits the right signal)
|
||||
nodeItem = killNodeAtHashIterator(nodeItem);
|
||||
} else {
|
||||
// we didn't kill this node, push the iterator forwards
|
||||
++nodeItem;
|
||||
}
|
||||
|
||||
node->getMutex().unlock();
|
||||
}
|
||||
|
||||
_nodeHashMutex.unlock();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NodeList.h
|
||||
// libraries/shared/src
|
||||
// LimitedNodeList.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -9,14 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_NodeList_h
|
||||
#define hifi_NodeList_h
|
||||
#ifndef hifi_LimitedNodeList_h
|
||||
#define hifi_LimitedNodeList_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#include <iterator>
|
||||
|
||||
|
@ -32,12 +27,14 @@
|
|||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include "DomainInfo.h"
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "DomainHandler.h"
|
||||
#include "Node.h"
|
||||
|
||||
const quint64 NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
|
||||
const quint64 DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
|
||||
const quint64 PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
|
||||
const int MAX_PACKET_SIZE = 1500;
|
||||
|
||||
const quint64 NODE_SILENCE_THRESHOLD_MSECS = 2 * 1000;
|
||||
|
||||
extern const char SOLO_NODE_TYPES[2];
|
||||
|
||||
|
@ -45,9 +42,6 @@ extern const QUrl DEFAULT_NODE_AUTH_URL;
|
|||
|
||||
const char DEFAULT_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost";
|
||||
|
||||
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
|
||||
|
||||
class Assignment;
|
||||
class HifiSockAddr;
|
||||
|
||||
typedef QSet<NodeType_t> NodeSet;
|
||||
|
@ -56,57 +50,31 @@ typedef QSharedPointer<Node> SharedNodePointer;
|
|||
typedef QHash<QUuid, SharedNodePointer> NodeHash;
|
||||
Q_DECLARE_METATYPE(SharedNodePointer)
|
||||
|
||||
typedef quint8 PingType_t;
|
||||
namespace PingType {
|
||||
const PingType_t Agnostic = 0;
|
||||
const PingType_t Local = 1;
|
||||
const PingType_t Public = 2;
|
||||
const PingType_t Symmetric = 3;
|
||||
}
|
||||
|
||||
class NodeList : public QObject {
|
||||
class LimitedNodeList : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0);
|
||||
static NodeList* getInstance();
|
||||
NodeType_t getOwnerType() const { return _ownerType; }
|
||||
void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
|
||||
static LimitedNodeList* createInstance(unsigned short socketListenPort = 0, unsigned short dtlsPort = 0);
|
||||
static LimitedNodeList* getInstance();
|
||||
|
||||
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
|
||||
|
||||
QUdpSocket& getNodeSocket() { return _nodeSocket; }
|
||||
QUdpSocket& getDTLSSocket();
|
||||
|
||||
bool packetVersionAndHashMatch(const QByteArray& packet);
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
qint64 writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr);
|
||||
qint64 writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
qint64 sendStatsToDomainServer(const QJsonObject& statsObject);
|
||||
|
||||
void(*linkedDataCreateCallback)(Node *);
|
||||
|
||||
NodeHash getNodeHash();
|
||||
int size() const { return _nodeHash.size(); }
|
||||
|
||||
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
|
||||
DomainInfo& getDomainInfo() { return _domainInfo; }
|
||||
|
||||
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
|
||||
void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
|
||||
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
|
||||
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
|
||||
|
||||
int processDomainServerList(const QByteArray& packet);
|
||||
|
||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
||||
QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
|
||||
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
||||
|
||||
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true);
|
||||
SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
|
||||
|
||||
|
@ -126,15 +94,10 @@ public:
|
|||
|
||||
void getPacketStats(float &packetsPerSecond, float &bytesPerSecond);
|
||||
void resetPacketStats();
|
||||
|
||||
void loadData(QSettings* settings);
|
||||
void saveData(QSettings* settings);
|
||||
public slots:
|
||||
void reset();
|
||||
void eraseAllNodes();
|
||||
|
||||
void sendDomainServerCheckIn();
|
||||
void pingInactiveNodes();
|
||||
void removeSilentNodes();
|
||||
|
||||
void killNodeWithUUID(const QUuid& nodeUUID);
|
||||
|
@ -142,45 +105,29 @@ signals:
|
|||
void uuidChanged(const QUuid& ownerUUID);
|
||||
void nodeAdded(SharedNodePointer);
|
||||
void nodeKilled(SharedNodePointer);
|
||||
void limitOfSilentDomainCheckInsReached();
|
||||
private slots:
|
||||
void domainServerAuthReply(const QJsonObject& jsonObject);
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
protected:
|
||||
static LimitedNodeList* _sharedInstance;
|
||||
|
||||
NodeList(char ownerType, unsigned short int socketListenPort);
|
||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void sendSTUNRequest();
|
||||
void processSTUNResponse(const QByteArray& packet);
|
||||
LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort);
|
||||
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret);
|
||||
|
||||
NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill);
|
||||
|
||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||
void requestAuthForDomainServer();
|
||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
|
||||
void changeSendSocketBufferSize(int numSendBytes);
|
||||
|
||||
QUuid _sessionUUID;
|
||||
NodeHash _nodeHash;
|
||||
QMutex _nodeHashMutex;
|
||||
QUdpSocket _nodeSocket;
|
||||
NodeType_t _ownerType;
|
||||
NodeSet _nodeTypesOfInterest;
|
||||
DomainInfo _domainInfo;
|
||||
QUuid _sessionUUID;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
HifiSockAddr _publicSockAddr;
|
||||
bool _hasCompletedInitialSTUNFailure;
|
||||
unsigned int _stunRequestsSinceSuccess;
|
||||
QUdpSocket* _dtlsSocket;
|
||||
int _numCollectedPackets;
|
||||
int _numCollectedBytes;
|
||||
QElapsedTimer _packetStatTimer;
|
||||
};
|
||||
|
||||
#endif // hifi_NodeList_h
|
||||
#endif // hifi_LimitedNodeList_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Logging.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 6/11/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Logging.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 6/11/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -12,12 +12,6 @@
|
|||
#ifndef hifi_Logging_h
|
||||
#define hifi_Logging_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
const int LOGSTASH_UDP_PORT = 9500;
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NetworkPacket.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/9/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NetworkPacket.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/9/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Node.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -12,12 +12,6 @@
|
|||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <arpa/inet.h> // not available on windows, apparently not needed on mac
|
||||
#endif
|
||||
|
||||
#include "Node.h"
|
||||
#include "SharedUtil.h"
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Node.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -15,12 +15,6 @@
|
|||
#include <ostream>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QUuid>
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NodeData.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/19/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// NodeData.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/19/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
590
libraries/networking/src/NodeList.cpp
Normal file
590
libraries/networking/src/NodeList.cpp
Normal file
|
@ -0,0 +1,590 @@
|
|||
//
|
||||
// NodeList.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include <gnutls/dtls.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "Logging.h"
|
||||
#include "NodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UUID.h"
|
||||
|
||||
NodeList* NodeList::_sharedInstance = NULL;
|
||||
|
||||
NodeList* NodeList::createInstance(char ownerType, unsigned short socketListenPort, unsigned short dtlsPort) {
|
||||
if (!_sharedInstance) {
|
||||
NodeType::init();
|
||||
|
||||
_sharedInstance = new NodeList(ownerType, socketListenPort, dtlsPort);
|
||||
LimitedNodeList::_sharedInstance = _sharedInstance;
|
||||
|
||||
// register the SharedNodePointer meta-type for signals/slots
|
||||
qRegisterMetaType<SharedNodePointer>();
|
||||
} else {
|
||||
qDebug("NodeList createInstance called with existing instance.");
|
||||
}
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
NodeList* NodeList::getInstance() {
|
||||
if (!_sharedInstance) {
|
||||
qDebug("NodeList getInstance called before call to createInstance. Returning NULL pointer.");
|
||||
}
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
||||
LimitedNodeList(socketListenPort, dtlsListenPort),
|
||||
_ownerType(newOwnerType),
|
||||
_nodeTypesOfInterest(),
|
||||
_domainHandler(this),
|
||||
_numNoReplyDomainCheckIns(0),
|
||||
_assignmentServerSocket(),
|
||||
_publicSockAddr(),
|
||||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0)
|
||||
{
|
||||
// clear our NodeList when the domain changes
|
||||
connect(&_domainHandler, &DomainHandler::hostnameChanged, this, &NodeList::reset);
|
||||
|
||||
// clear our NodeList when logout is requested
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||
|
||||
// perform a function when DTLS handshake is completed
|
||||
connect(&_domainHandler, &DomainHandler::completedDTLSHandshake, this, &NodeList::completedDTLSHandshake);
|
||||
}
|
||||
|
||||
qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
|
||||
QByteArray statsPacket = byteArrayWithPopulatedHeader(PacketTypeNodeJsonStats);
|
||||
QDataStream statsPacketStream(&statsPacket, QIODevice::Append);
|
||||
|
||||
statsPacketStream << statsObject.toVariantMap();
|
||||
|
||||
return writeDatagram(statsPacket, _domainHandler.getSockAddr(), QUuid());
|
||||
}
|
||||
|
||||
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
quint8 pingType;
|
||||
quint64 ourOriginalTime, othersReplyTime;
|
||||
|
||||
packetStream >> pingType >> ourOriginalTime >> othersReplyTime;
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
int pingTime = now - ourOriginalTime;
|
||||
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
||||
|
||||
// The other node's expected time should be our original time plus the one way flight time
|
||||
// anything other than that is clock skew
|
||||
quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
|
||||
int clockSkew = othersReplyTime - othersExprectedReply;
|
||||
|
||||
sendingNode->setPingMs(pingTime / 1000);
|
||||
sendingNode->setClockSkewUsec(clockSkew);
|
||||
|
||||
const bool wantDebug = false;
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "PING_REPLY from node " << *sendingNode << "\n" <<
|
||||
" now: " << now << "\n" <<
|
||||
" ourTime: " << ourOriginalTime << "\n" <<
|
||||
" pingTime: " << pingTime << "\n" <<
|
||||
" oneWayFlightTime: " << oneWayFlightTime << "\n" <<
|
||||
" othersReplyTime: " << othersReplyTime << "\n" <<
|
||||
" othersExprectedReply: " << othersExprectedReply << "\n" <<
|
||||
" clockSkew: " << clockSkew;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::completedDTLSHandshake() {
|
||||
// at this point, we've got a DTLS socket
|
||||
// make this NodeList the handler of DTLS packets
|
||||
connect(_dtlsSocket, &QUdpSocket::readyRead, this, &NodeList::processAvailableDTLSDatagrams);
|
||||
}
|
||||
|
||||
void NodeList::processAvailableDTLSDatagrams() {
|
||||
while (_dtlsSocket->hasPendingDatagrams()) {
|
||||
QByteArray dtlsPacket(_dtlsSocket->pendingDatagramSize(), 0);
|
||||
|
||||
// pull the data from this user off the stack and process it
|
||||
int receivedBytes = gnutls_record_recv(*_domainHandler.getDTLSSession()->getGnuTLSSession(),
|
||||
dtlsPacket.data(), dtlsPacket.size());
|
||||
if (receivedBytes > 0) {
|
||||
// successful data receive, hand this off to processNodeData
|
||||
processNodeData(_domainHandler.getSockAddr(), dtlsPacket.left(receivedBytes));
|
||||
} else if (gnutls_error_is_fatal(receivedBytes)) {
|
||||
qDebug() << "Fatal error -" << gnutls_strerror(receivedBytes) << "- receiving DTLS packet from domain-server.";
|
||||
} else {
|
||||
qDebug() << "non fatal receive" << receivedBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
switch (packetTypeForPacket(packet)) {
|
||||
case PacketTypeDomainList: {
|
||||
processDomainServerList(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypeDomainServerRequireDTLS: {
|
||||
_domainHandler.parseDTLSRequirementPacket(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypePing: {
|
||||
// send back a reply
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
if (matchingNode) {
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
QByteArray replyPacket = constructPingReplyPacket(packet);
|
||||
writeDatagram(replyPacket, matchingNode, senderSockAddr);
|
||||
|
||||
// If we don't have a symmetric socket for this node and this socket doesn't match
|
||||
// what we have for public and local then set it as the symmetric.
|
||||
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
|
||||
if (matchingNode->getSymmetricSocket().isNull()) {
|
||||
if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) {
|
||||
matchingNode->setSymmetricSocket(senderSockAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypePingReply: {
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
|
||||
if (sendingNode) {
|
||||
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
// activate the appropriate socket for this node, if not yet updated
|
||||
activateSocketFromNodeCommunication(packet, sendingNode);
|
||||
|
||||
// set the ping time for this node for stat collection
|
||||
timePingReply(packet, sendingNode);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypeStunResponse: {
|
||||
// a STUN packet begins with 00, we've checked the second zero with packetVersionMatch
|
||||
// pass it along so it can be processed into our public address and port
|
||||
processSTUNResponse(packet);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LimitedNodeList::processNodeData(senderSockAddr, packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::reset() {
|
||||
LimitedNodeList::reset();
|
||||
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
// clear the domain connection information
|
||||
_domainHandler.clearConnectionInfo();
|
||||
|
||||
// also disconnect from the DTLS socket readyRead() so it can handle handshaking
|
||||
disconnect(_dtlsSocket, 0, this, 0);
|
||||
}
|
||||
|
||||
void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) {
|
||||
_nodeTypesOfInterest << nodeTypeToAdd;
|
||||
}
|
||||
|
||||
void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) {
|
||||
_nodeTypesOfInterest.unite(setOfNodeTypes);
|
||||
}
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442;
|
||||
const int NUM_BYTES_STUN_HEADER = 20;
|
||||
const unsigned int NUM_STUN_REQUESTS_BEFORE_FALLBACK = 5;
|
||||
|
||||
void NodeList::sendSTUNRequest() {
|
||||
const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io";
|
||||
const unsigned short STUN_SERVER_PORT = 3478;
|
||||
|
||||
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;
|
||||
QUuid randomUUID = QUuid::createUuid();
|
||||
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
|
||||
|
||||
// lookup the IP for the STUN server
|
||||
static HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qDebug("Sending intial stun request to %s", stunSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||
stunSockAddr.getAddress(), stunSockAddr.getPort());
|
||||
|
||||
_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.",
|
||||
STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
}
|
||||
|
||||
// reset the public address and port
|
||||
// use 0 so the DS knows to act as out STUN server
|
||||
_publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort());
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processSTUNResponse(const QByteArray& packet) {
|
||||
// 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
|
||||
const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4;
|
||||
const uint16_t XOR_MAPPED_ADDRESS_TYPE = htons(0x0020);
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||
|
||||
int attributeStartIndex = NUM_BYTES_STUN_HEADER;
|
||||
|
||||
if (memcmp(packet.data() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
|
||||
&RFC_5389_MAGIC_COOKIE_NETWORK_ORDER,
|
||||
sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) {
|
||||
|
||||
// enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE
|
||||
while (attributeStartIndex < packet.size()) {
|
||||
if (memcmp(packet.data() + 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_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;
|
||||
memcpy(&addressFamily, packet.data(), sizeof(addressFamily));
|
||||
|
||||
byteIndex += sizeof(addressFamily);
|
||||
|
||||
if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) {
|
||||
// grab the X-Port
|
||||
uint16_t xorMappedPort = 0;
|
||||
memcpy(&xorMappedPort, packet.data() + byteIndex, sizeof(xorMappedPort));
|
||||
|
||||
uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
|
||||
|
||||
byteIndex += sizeof(xorMappedPort);
|
||||
|
||||
// grab the X-Address
|
||||
uint32_t xorMappedAddress = 0;
|
||||
memcpy(&xorMappedAddress, packet.data() + byteIndex, sizeof(xorMappedAddress));
|
||||
|
||||
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
|
||||
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||
|
||||
if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) {
|
||||
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
|
||||
|
||||
qDebug("New public socket received from STUN server is %s:%hu",
|
||||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
}
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// push forward attributeStartIndex by the length of this attribute
|
||||
const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
|
||||
|
||||
uint16_t attributeLength = 0;
|
||||
memcpy(&attributeLength, packet.data() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
|
||||
sizeof(attributeLength));
|
||||
attributeLength = ntohs(attributeLength);
|
||||
|
||||
attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::sendDomainServerCheckIn() {
|
||||
if (_publicSockAddr.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 if (!_domainHandler.getIP().isNull()) {
|
||||
|
||||
DTLSClientSession* dtlsSession = _domainHandler.getDTLSSession();
|
||||
bool isUsingDTLS = false;
|
||||
|
||||
if (dtlsSession) {
|
||||
if (!dtlsSession->completedHandshake()) {
|
||||
// if the handshake process is not complete then we can't check in, so return
|
||||
return;
|
||||
} else {
|
||||
isUsingDTLS = true;
|
||||
}
|
||||
}
|
||||
|
||||
// construct the DS check in packet
|
||||
QUuid packetUUID = (!_sessionUUID.isNull() ? _sessionUUID : _domainHandler.getAssignmentUUID());
|
||||
|
||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(PacketTypeDomainListRequest, packetUUID);
|
||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||
|
||||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr
|
||||
<< HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
|
||||
<< (quint8) _nodeTypesOfInterest.size();
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
|
||||
packetStream << nodeTypeOfInterest;
|
||||
}
|
||||
|
||||
if (!isUsingDTLS) {
|
||||
writeDatagram(domainServerPacket, _domainHandler.getSockAddr(), QUuid());
|
||||
} else {
|
||||
dtlsSession->writeDatagram(domainServerPacket);
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||
// so emit our signal that indicates that
|
||||
emit limitOfSilentDomainCheckInsReached();
|
||||
}
|
||||
|
||||
// increment the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns++;
|
||||
}
|
||||
}
|
||||
|
||||
int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||
// this is a packet from the domain server, reset the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
// if this was the first domain-server list from this domain, we've now connected
|
||||
_domainHandler.setIsConnected(true);
|
||||
|
||||
int readNodes = 0;
|
||||
|
||||
// setup variables to read into from QDataStream
|
||||
qint8 nodeType;
|
||||
|
||||
QUuid nodeUUID, connectionUUID;
|
||||
|
||||
HifiSockAddr nodePublicSocket;
|
||||
HifiSockAddr nodeLocalSocket;
|
||||
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
// pull our owner UUID from the packet, it's always the first thing
|
||||
QUuid newUUID;
|
||||
packetStream >> newUUID;
|
||||
setSessionUUID(newUUID);
|
||||
|
||||
// pull each node in the packet
|
||||
while(packetStream.device()->pos() < packet.size()) {
|
||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket;
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
if (nodePublicSocket.getAddress().isNull()) {
|
||||
nodePublicSocket.setAddress(_domainHandler.getIP());
|
||||
}
|
||||
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket);
|
||||
|
||||
packetStream >> connectionUUID;
|
||||
node->setConnectionSecret(connectionUUID);
|
||||
}
|
||||
|
||||
// ping inactive nodes in conjunction with receipt of list from domain-server
|
||||
// this makes it happen every second and also pings any newly added nodes
|
||||
pingInactiveNodes();
|
||||
|
||||
return readNodes;
|
||||
}
|
||||
|
||||
void NodeList::sendAssignment(Assignment& assignment) {
|
||||
|
||||
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
|
||||
? PacketTypeCreateAssignment
|
||||
: PacketTypeRequestAssignment;
|
||||
|
||||
QByteArray packet = byteArrayWithPopulatedHeader(assignmentPacketType);
|
||||
QDataStream packetStream(&packet, QIODevice::Append);
|
||||
|
||||
packetStream << assignment;
|
||||
|
||||
static HifiSockAddr DEFAULT_ASSIGNMENT_SOCKET(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, DEFAULT_DOMAIN_SERVER_PORT);
|
||||
|
||||
const HifiSockAddr* assignmentServerSocket = _assignmentServerSocket.isNull()
|
||||
? &DEFAULT_ASSIGNMENT_SOCKET
|
||||
: &_assignmentServerSocket;
|
||||
|
||||
_nodeSocket.writeDatagram(packet, assignmentServerSocket->getAddress(), assignmentServerSocket->getPort());
|
||||
}
|
||||
|
||||
QByteArray NodeList::constructPingPacket(PingType_t pingType) {
|
||||
QByteArray pingPacket = byteArrayWithPopulatedHeader(PacketTypePing);
|
||||
|
||||
QDataStream packetStream(&pingPacket, QIODevice::Append);
|
||||
|
||||
packetStream << pingType;
|
||||
packetStream << usecTimestampNow();
|
||||
|
||||
return pingPacket;
|
||||
}
|
||||
|
||||
QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
||||
QDataStream pingPacketStream(pingPacket);
|
||||
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||
|
||||
PingType_t typeFromOriginalPing;
|
||||
pingPacketStream >> typeFromOriginalPing;
|
||||
|
||||
quint64 timeFromOriginalPing;
|
||||
pingPacketStream >> timeFromOriginalPing;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithPopulatedHeader(PacketTypePingReply);
|
||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||
|
||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||
|
||||
return replyPacket;
|
||||
}
|
||||
|
||||
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = constructPingPacket(PingType::Local);
|
||||
writeDatagram(localPingPacket, node, node->getLocalSocket());
|
||||
|
||||
QByteArray publicPingPacket = constructPingPacket(PingType::Public);
|
||||
writeDatagram(publicPingPacket, node, node->getPublicSocket());
|
||||
|
||||
if (!node->getSymmetricSocket().isNull()) {
|
||||
QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric);
|
||||
writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket());
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::pingInactiveNodes() {
|
||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active link to this node, ping it to set that up
|
||||
pingPunchForInactiveNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
// deconstruct this ping packet to see if it is a public or local reply
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
quint8 pingType;
|
||||
packetStream >> pingType;
|
||||
|
||||
// if this is a local or public ping then we can activate a socket
|
||||
// we do nothing with agnostic pings, those are simply for timing
|
||||
if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) {
|
||||
sendingNode->activateLocalSocket();
|
||||
} else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) {
|
||||
sendingNode->activatePublicSocket();
|
||||
} else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) {
|
||||
sendingNode->activateSymmetricSocket();
|
||||
}
|
||||
}
|
||||
|
||||
const QString QSETTINGS_GROUP_NAME = "NodeList";
|
||||
const QString DOMAIN_SERVER_SETTING_KEY = "domainServerHostname";
|
||||
|
||||
void NodeList::loadData(QSettings *settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
QString domainServerHostname = settings->value(DOMAIN_SERVER_SETTING_KEY).toString();
|
||||
|
||||
if (domainServerHostname.size() > 0) {
|
||||
_domainHandler.setHostname(domainServerHostname);
|
||||
} else {
|
||||
_domainHandler.setHostname(DEFAULT_DOMAIN_HOSTNAME);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void NodeList::saveData(QSettings* settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
if (_domainHandler.getHostname() != DEFAULT_DOMAIN_HOSTNAME) {
|
||||
// the user is using a different hostname, store it
|
||||
settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainHandler.getHostname()));
|
||||
} else {
|
||||
// the user has switched back to default, remove the current setting
|
||||
settings->remove(DOMAIN_SERVER_SETTING_KEY);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
114
libraries/networking/src/NodeList.h
Normal file
114
libraries/networking/src/NodeList.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// NodeList.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_NodeList_h
|
||||
#define hifi_NodeList_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <iterator>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h> // not on windows, not needed for mac or windows
|
||||
#endif
|
||||
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "DomainHandler.h"
|
||||
#include "LimitedNodeList.h"
|
||||
#include "Node.h"
|
||||
|
||||
const quint64 DOMAIN_SERVER_CHECK_IN_MSECS = 1 * 1000;
|
||||
|
||||
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
|
||||
|
||||
class Assignment;
|
||||
|
||||
typedef quint8 PingType_t;
|
||||
namespace PingType {
|
||||
const PingType_t Agnostic = 0;
|
||||
const PingType_t Local = 1;
|
||||
const PingType_t Public = 2;
|
||||
const PingType_t Symmetric = 3;
|
||||
}
|
||||
|
||||
class NodeList : public LimitedNodeList {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static NodeList* createInstance(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsPort = 0);
|
||||
static NodeList* getInstance();
|
||||
NodeType_t getOwnerType() const { return _ownerType; }
|
||||
void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
|
||||
|
||||
qint64 sendStatsToDomainServer(const QJsonObject& statsObject);
|
||||
|
||||
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
|
||||
DomainHandler& getDomainHandler() { return _domainHandler; }
|
||||
|
||||
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
|
||||
void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
|
||||
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
|
||||
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
|
||||
|
||||
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
|
||||
|
||||
int processDomainServerList(const QByteArray& packet);
|
||||
|
||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
||||
QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
|
||||
|
||||
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
||||
|
||||
void loadData(QSettings* settings);
|
||||
void saveData(QSettings* settings);
|
||||
public slots:
|
||||
void reset();
|
||||
void sendDomainServerCheckIn();
|
||||
void pingInactiveNodes();
|
||||
void completedDTLSHandshake();
|
||||
void processAvailableDTLSDatagrams();
|
||||
signals:
|
||||
void limitOfSilentDomainCheckInsReached();
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
|
||||
NodeList(char ownerType, unsigned short socketListenPort, unsigned short dtlsListenPort);
|
||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void sendSTUNRequest();
|
||||
void processSTUNResponse(const QByteArray& packet);
|
||||
|
||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||
void requestAuthForDomainServer();
|
||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
|
||||
NodeType_t _ownerType;
|
||||
NodeSet _nodeTypesOfInterest;
|
||||
DomainHandler _domainHandler;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
HifiSockAddr _publicSockAddr;
|
||||
bool _hasCompletedInitialSTUNFailure;
|
||||
unsigned int _stunRequestsSinceSuccess;
|
||||
};
|
||||
|
||||
#endif // hifi_NodeList_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// OAuthAccessToken.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// OAuthAccessToken.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// PacketHeaders.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 6/28/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -55,15 +55,10 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
return 1;
|
||||
case PacketTypeDomainList:
|
||||
case PacketTypeDomainListRequest:
|
||||
return 1;
|
||||
return 2;
|
||||
case PacketTypeCreateAssignment:
|
||||
case PacketTypeRequestAssignment:
|
||||
return 1;
|
||||
case PacketTypeDataServerGet:
|
||||
case PacketTypeDataServerPut:
|
||||
case PacketTypeDataServerConfirm:
|
||||
case PacketTypeDataServerSend:
|
||||
return 1;
|
||||
return 2;
|
||||
case PacketTypeVoxelSet:
|
||||
case PacketTypeVoxelSetDestructive:
|
||||
return 1;
|
||||
|
@ -92,15 +87,17 @@ int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionU
|
|||
|
||||
char* position = packet + numTypeBytes + sizeof(PacketVersion);
|
||||
|
||||
QUuid packUUID = connectionUUID.isNull() ? NodeList::getInstance()->getSessionUUID() : connectionUUID;
|
||||
QUuid packUUID = connectionUUID.isNull() ? LimitedNodeList::getInstance()->getSessionUUID() : connectionUUID;
|
||||
|
||||
QByteArray rfcUUID = packUUID.toRfc4122();
|
||||
memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID);
|
||||
position += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
// pack 16 bytes of zeros where the md5 hash will be placed one data is packed
|
||||
memset(position, 0, NUM_BYTES_MD5_HASH);
|
||||
position += NUM_BYTES_MD5_HASH;
|
||||
if (!NON_VERIFIED_PACKETS.contains(type)) {
|
||||
// pack 16 bytes of zeros where the md5 hash will be placed one data is packed
|
||||
memset(position, 0, NUM_BYTES_MD5_HASH);
|
||||
position += NUM_BYTES_MD5_HASH;
|
||||
}
|
||||
|
||||
// return the number of bytes written for pointer pushing
|
||||
return position - packet;
|
||||
|
@ -108,16 +105,26 @@ int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionU
|
|||
|
||||
int numBytesForPacketHeader(const QByteArray& packet) {
|
||||
// returns the number of bytes used for the type, version, and UUID
|
||||
return numBytesArithmeticCodingFromBuffer(packet.data()) + NUM_STATIC_HEADER_BYTES;
|
||||
return numBytesArithmeticCodingFromBuffer(packet.data())
|
||||
+ numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet))
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
}
|
||||
|
||||
int numBytesForPacketHeader(const char* packet) {
|
||||
// returns the number of bytes used for the type, version, and UUID
|
||||
return numBytesArithmeticCodingFromBuffer(packet) + NUM_STATIC_HEADER_BYTES;
|
||||
return numBytesArithmeticCodingFromBuffer(packet)
|
||||
+ numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet))
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
}
|
||||
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType type) {
|
||||
return (int) ceilf((float)type / 255) + NUM_STATIC_HEADER_BYTES;
|
||||
return (int) ceilf((float)type / 255)
|
||||
+ numHashBytesInPacketHeaderGivenPacketType(type)
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
}
|
||||
|
||||
int numHashBytesInPacketHeaderGivenPacketType(PacketType type) {
|
||||
return (NON_VERIFIED_PACKETS.contains(type) ? 0 : NUM_BYTES_MD5_HASH);
|
||||
}
|
||||
|
||||
QUuid uuidFromPacketHeader(const QByteArray& packet) {
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// PacketHeaders.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Stephen Birarda on 4/8/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -13,6 +13,7 @@
|
|||
#define hifi_PacketHeaders_h
|
||||
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "UUID.h"
|
||||
|
@ -37,9 +38,9 @@ enum PacketType {
|
|||
PacketTypeDomainListRequest,
|
||||
PacketTypeRequestAssignment,
|
||||
PacketTypeCreateAssignment,
|
||||
PacketTypeDataServerPut,
|
||||
PacketTypeDataServerGet,
|
||||
PacketTypeDataServerSend,
|
||||
PacketTypeDataServerPut, // reusable
|
||||
PacketTypeDataServerGet, // reusable
|
||||
PacketTypeDataServerSend, // reusable
|
||||
PacketTypeDataServerConfirm,
|
||||
PacketTypeVoxelQuery,
|
||||
PacketTypeVoxelData,
|
||||
|
@ -57,16 +58,21 @@ enum PacketType {
|
|||
PacketTypeMetavoxelData,
|
||||
PacketTypeAvatarIdentity,
|
||||
PacketTypeAvatarBillboard,
|
||||
PacketTypeDomainConnectRequest,
|
||||
PacketTypeDomainServerAuthRequest,
|
||||
PacketTypeNodeJsonStats
|
||||
PacketTypeDomainConnectRequest, // reusable
|
||||
PacketTypeDomainServerRequireDTLS,
|
||||
PacketTypeNodeJsonStats,
|
||||
};
|
||||
|
||||
typedef char PacketVersion;
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainList << PacketTypeDomainListRequest
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats;
|
||||
|
||||
const int NUM_BYTES_MD5_HASH = 16;
|
||||
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID + NUM_BYTES_MD5_HASH;
|
||||
const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + NUM_STATIC_HEADER_BYTES;
|
||||
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
||||
const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + NUM_BYTES_MD5_HASH + NUM_STATIC_HEADER_BYTES;
|
||||
|
||||
PacketVersion versionForPacketType(PacketType type);
|
||||
|
||||
|
@ -76,6 +82,8 @@ QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connection
|
|||
int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID = nullUUID);
|
||||
int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID = nullUUID);
|
||||
|
||||
int numHashBytesInPacketHeaderGivenPacketType(PacketType type);
|
||||
|
||||
int numBytesForPacketHeader(const QByteArray& packet);
|
||||
int numBytesForPacketHeader(const char* packet);
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType type);
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// PacketSender.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// PacketSender.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ReceivedPacketProcessor.cpp
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ReceivedPacketProcessor.h
|
||||
// libraries/shared/src
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
|
@ -44,11 +44,11 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy
|
|||
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
QTimer* silentNodeRemovalTimer = new QTimer(this);
|
||||
connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_MSECS);
|
||||
|
||||
if (shouldSendStats) {
|
||||
// send a stats packet every 1 second
|
|
@ -22,9 +22,17 @@ include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
|||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link ZLIB
|
||||
# link ZLIB and GnuTLS
|
||||
find_package(ZLIB)
|
||||
include_directories("${ZLIB_INCLUDE_DIRS}")
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets "${GNUTLS_LIBRARY}")
|
|
@ -23,9 +23,16 @@ include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
|||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link ZLIB
|
||||
# link ZLIB and GnuTLS
|
||||
find_package(ZLIB)
|
||||
include_directories("${ZLIB_INCLUDE_DIRS}")
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets "${GNUTLS_LIBRARY}")
|
|
@ -28,6 +28,12 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
|
|||
|
||||
# link ZLIB
|
||||
find_package(ZLIB)
|
||||
include_directories("${ZLIB_INCLUDE_DIRS}")
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" "${GNUTLS_LIBRARY}" Qt5::Widgets)
|
|
@ -32,4 +32,4 @@ if (UNIX AND NOT APPLE)
|
|||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif (UNIX AND NOT APPLE)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
|
|
@ -1,129 +0,0 @@
|
|||
//
|
||||
// DomainInfo.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
||||
#include "DomainInfo.h"
|
||||
|
||||
DomainInfo::DomainInfo() :
|
||||
_uuid(),
|
||||
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_assignmentUUID(),
|
||||
_connectionSecret(),
|
||||
_registrationToken(),
|
||||
_rootAuthenticationURL(),
|
||||
_publicKey(),
|
||||
_isConnected(false)
|
||||
{
|
||||
// clear appropriate variables after a domain-server logout
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete, this, &DomainInfo::logout);
|
||||
}
|
||||
|
||||
void DomainInfo::clearConnectionInfo() {
|
||||
_uuid = QUuid();
|
||||
_connectionSecret = QUuid();
|
||||
_registrationToken = QByteArray();
|
||||
_rootAuthenticationURL = QUrl();
|
||||
_publicKey = QString();
|
||||
_isConnected = false;
|
||||
}
|
||||
|
||||
void DomainInfo::reset() {
|
||||
clearConnectionInfo();
|
||||
_hostname = QString();
|
||||
_sockAddr.setAddress(QHostAddress::Null);
|
||||
}
|
||||
|
||||
void DomainInfo::parseAuthInformationFromJsonObject(const QJsonObject& jsonObject) {
|
||||
QJsonObject dataObject = jsonObject["data"].toObject();
|
||||
_connectionSecret = QUuid(dataObject["connection_secret"].toString());
|
||||
_registrationToken = QByteArray::fromHex(dataObject["registration_token"].toString().toUtf8());
|
||||
_publicKey = dataObject["public_key"].toString();
|
||||
}
|
||||
|
||||
void DomainInfo::setSockAddr(const HifiSockAddr& sockAddr) {
|
||||
if (_sockAddr != sockAddr) {
|
||||
// we should reset on a sockAddr change
|
||||
reset();
|
||||
// change the sockAddr
|
||||
_sockAddr = sockAddr;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainInfo::setHostname(const QString& hostname) {
|
||||
|
||||
if (hostname != _hostname) {
|
||||
// re-set the domain info so that auth information is reloaded
|
||||
reset();
|
||||
|
||||
int colonIndex = hostname.indexOf(':');
|
||||
|
||||
if (colonIndex > 0) {
|
||||
// the user has included a custom DS port with the hostname
|
||||
|
||||
// the new hostname is everything up to the colon
|
||||
_hostname = hostname.left(colonIndex);
|
||||
|
||||
// grab the port by reading the string after the colon
|
||||
_sockAddr.setPort(atoi(hostname.mid(colonIndex + 1, hostname.size()).toLocal8Bit().constData()));
|
||||
|
||||
qDebug() << "Updated hostname to" << _hostname << "and port to" << _sockAddr.getPort();
|
||||
|
||||
} else {
|
||||
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
||||
_hostname = hostname;
|
||||
_sockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT);
|
||||
}
|
||||
|
||||
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
||||
qDebug("Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||
|
||||
emit hostnameChanged(_hostname);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainInfo::completedHostnameLookup(const QHostInfo& hostInfo) {
|
||||
for (int i = 0; i < hostInfo.addresses().size(); i++) {
|
||||
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_sockAddr.setAddress(hostInfo.addresses()[i]);
|
||||
qDebug("DS at %s is at %s", _hostname.toLocal8Bit().constData(),
|
||||
_sockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here then we failed to lookup the address
|
||||
qDebug("Failed domain server lookup");
|
||||
}
|
||||
|
||||
void DomainInfo::setIsConnected(bool isConnected) {
|
||||
if (_isConnected != isConnected) {
|
||||
_isConnected = isConnected;
|
||||
|
||||
if (_isConnected) {
|
||||
emit connectedToDomain(_hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainInfo::logout() {
|
||||
// clear any information related to auth for this domain, assuming it had requested auth
|
||||
if (!_rootAuthenticationURL.isEmpty()) {
|
||||
_rootAuthenticationURL = QUrl();
|
||||
_connectionSecret = QUuid();
|
||||
_registrationToken = QByteArray();
|
||||
_publicKey = QString();
|
||||
_isConnected = false;
|
||||
}
|
||||
}
|
98
libraries/shared/src/HifiConfigVariantMap.cpp
Normal file
98
libraries/shared/src/HifiConfigVariantMap.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// HifiConfigVariantMap.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-08.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
#include "HifiConfigVariantMap.h"
|
||||
|
||||
QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) {
|
||||
|
||||
QVariantMap mergedMap;
|
||||
|
||||
// Add anything in the CL parameter list to the variant map.
|
||||
// Take anything with a dash in it as a key, and the values after it as the value.
|
||||
|
||||
const QString DASHED_KEY_REGEX_STRING = "(^-{1,2})([\\w-]+)";
|
||||
QRegExp dashedKeyRegex(DASHED_KEY_REGEX_STRING);
|
||||
|
||||
int keyIndex = argumentList.indexOf(dashedKeyRegex);
|
||||
int nextKeyIndex = 0;
|
||||
|
||||
// check if there is a config file to read where we can pull config info not passed on command line
|
||||
const QString CONFIG_FILE_OPTION = "--config";
|
||||
|
||||
while (keyIndex != -1) {
|
||||
if (argumentList[keyIndex] != CONFIG_FILE_OPTION) {
|
||||
// we have a key - look forward to see how many values associate to it
|
||||
QString key = dashedKeyRegex.cap(2);
|
||||
|
||||
nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1);
|
||||
|
||||
if (nextKeyIndex == keyIndex + 1) {
|
||||
// there's no value associated with this option, it's a boolean
|
||||
// so add it to the variant map with NULL as value
|
||||
mergedMap.insertMulti(key, QVariant());
|
||||
} else {
|
||||
int maxIndex = (nextKeyIndex == -1) ? argumentList.size() : nextKeyIndex;
|
||||
|
||||
// there's at least one value associated with the option
|
||||
// pull the first value to start
|
||||
QString value = argumentList[keyIndex + 1];
|
||||
|
||||
// for any extra values, append them, with a space, to the value string
|
||||
for (int i = keyIndex + 2; i < maxIndex; i++) {
|
||||
value += " " + argumentList[i];
|
||||
}
|
||||
|
||||
// add the finalized value to the merged map
|
||||
mergedMap.insert(key, value);
|
||||
}
|
||||
|
||||
keyIndex = nextKeyIndex;
|
||||
} else {
|
||||
keyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
|
||||
|
||||
if (configIndex != -1) {
|
||||
// we have a config file - try and read it
|
||||
QString configFilePath = argumentList[configIndex + 1];
|
||||
QFile configFile(configFilePath);
|
||||
|
||||
if (configFile.exists()) {
|
||||
qDebug() << "Reading JSON config file at" << configFilePath;
|
||||
configFile.open(QIODevice::ReadOnly);
|
||||
|
||||
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
||||
QJsonObject rootObject = configDocument.object();
|
||||
|
||||
// enumerate the keys of the configDocument object
|
||||
foreach(const QString& key, rootObject.keys()) {
|
||||
|
||||
if (!mergedMap.contains(key)) {
|
||||
// no match in existing list, add it
|
||||
mergedMap.insert(key, QVariant(rootObject[key]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Could not find JSON config file at" << configFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
return mergedMap;
|
||||
}
|
22
libraries/shared/src/HifiConfigVariantMap.h
Normal file
22
libraries/shared/src/HifiConfigVariantMap.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// HifiConfigVariantMap.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-04-08.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_HifiConfigVariantMap_h
|
||||
#define hifi_HifiConfigVariantMap_h
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
class HifiConfigVariantMap {
|
||||
public:
|
||||
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
||||
};
|
||||
|
||||
#endif // hifi_HifiConfigVariantMap_h
|
|
@ -1,981 +0,0 @@
|
|||
//
|
||||
// NodeList.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "Logging.h"
|
||||
#include "NodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UUID.h"
|
||||
|
||||
const char SOLO_NODE_TYPES[2] = {
|
||||
NodeType::AvatarMixer,
|
||||
NodeType::AudioMixer
|
||||
};
|
||||
|
||||
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data-web.highfidelity.io");
|
||||
|
||||
NodeList* NodeList::_sharedInstance = NULL;
|
||||
|
||||
NodeList* NodeList::createInstance(char ownerType, unsigned short int socketListenPort) {
|
||||
if (!_sharedInstance) {
|
||||
NodeType::init();
|
||||
|
||||
_sharedInstance = new NodeList(ownerType, socketListenPort);
|
||||
|
||||
// register the SharedNodePointer meta-type for signals/slots
|
||||
qRegisterMetaType<SharedNodePointer>();
|
||||
} else {
|
||||
qDebug("NodeList createInstance called with existing instance.");
|
||||
}
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
NodeList* NodeList::getInstance() {
|
||||
if (!_sharedInstance) {
|
||||
qDebug("NodeList getInstance called before call to createInstance. Returning NULL pointer.");
|
||||
}
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
|
||||
NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
||||
_nodeHash(),
|
||||
_nodeHashMutex(QMutex::Recursive),
|
||||
_nodeSocket(this),
|
||||
_ownerType(newOwnerType),
|
||||
_nodeTypesOfInterest(),
|
||||
_sessionUUID(),
|
||||
_numNoReplyDomainCheckIns(0),
|
||||
_assignmentServerSocket(),
|
||||
_publicSockAddr(),
|
||||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0),
|
||||
_numCollectedPackets(0),
|
||||
_numCollectedBytes(0),
|
||||
_packetStatTimer()
|
||||
{
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, newSocketListenPort);
|
||||
qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||
|
||||
// clear our NodeList when the domain changes
|
||||
connect(&_domainInfo, &DomainInfo::hostnameChanged, this, &NodeList::reset);
|
||||
|
||||
// clear our NodeList when logout is requested
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||
|
||||
const int LARGER_SNDBUF_SIZE = 1048576;
|
||||
changeSendSocketBufferSize(LARGER_SNDBUF_SIZE);
|
||||
|
||||
_packetStatTimer.start();
|
||||
}
|
||||
|
||||
void NodeList::changeSendSocketBufferSize(int numSendBytes) {
|
||||
// change the socket send buffer size to be 1MB
|
||||
int oldBufferSize = 0;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
int sizeOfInt = sizeof(oldBufferSize);
|
||||
#else
|
||||
unsigned int sizeOfInt = sizeof(oldBufferSize);
|
||||
#endif
|
||||
|
||||
getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&oldBufferSize), &sizeOfInt);
|
||||
|
||||
setsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&numSendBytes),
|
||||
sizeof(numSendBytes));
|
||||
|
||||
int newBufferSize = 0;
|
||||
getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&newBufferSize), &sizeOfInt);
|
||||
|
||||
qDebug() << "Changed socket send buffer size from" << oldBufferSize << "to" << newBufferSize << "bytes";
|
||||
}
|
||||
|
||||
bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
||||
PacketType checkType = packetTypeForPacket(packet);
|
||||
if (packet[1] != versionForPacketType(checkType)
|
||||
&& checkType != PacketTypeStunResponse) {
|
||||
PacketType mismatchType = packetTypeForPacket(packet);
|
||||
int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());
|
||||
|
||||
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
|
||||
|
||||
QUuid senderUUID = uuidFromPacketHeader(packet);
|
||||
if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {
|
||||
qDebug() << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"
|
||||
<< uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"
|
||||
<< qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";
|
||||
|
||||
versionDebugSuppressMap.insert(senderUUID, checkType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerAuthRequest << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeStunResponse << PacketTypeDataServerConfirm
|
||||
<< PacketTypeDataServerGet << PacketTypeDataServerPut << PacketTypeDataServerSend
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment;
|
||||
|
||||
if (!NON_VERIFIED_PACKETS.contains(checkType)) {
|
||||
// figure out which node this is from
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
if (sendingNode) {
|
||||
// check if the md5 hash in the header matches the hash we would expect
|
||||
if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Packet hash mismatch on" << checkType << "- Sender"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
}
|
||||
} else {
|
||||
if (checkType == PacketTypeDomainList) {
|
||||
|
||||
if (_domainInfo.getRootAuthenticationURL().isEmpty() && _domainInfo.getUUID().isNull()) {
|
||||
// if this is a domain-server that doesn't require auth,
|
||||
// pull the UUID from this packet and set it as our domain-server UUID
|
||||
_domainInfo.setUUID(uuidFromPacketHeader(packet));
|
||||
|
||||
// we also know this domain-server requires no authentication
|
||||
// so set the account manager root URL to the default one
|
||||
AccountManager::getInstance().setAuthURL(DEFAULT_NODE_AUTH_URL);
|
||||
}
|
||||
|
||||
if (_domainInfo.getUUID() == uuidFromPacketHeader(packet)) {
|
||||
if (hashForPacketAndConnectionUUID(packet, _domainInfo.getConnectionSecret()) == hashFromPacketHeader(packet)) {
|
||||
// this is a packet from the domain-server (PacketTypeDomainServerListRequest)
|
||||
// and the sender UUID matches the UUID we expect for the domain
|
||||
return true;
|
||||
} else {
|
||||
// this is a packet from the domain-server but there is a hash mismatch
|
||||
qDebug() << "Packet hash mismatch on" << checkType << "from domain-server at" << _domainInfo.getHostname();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Packet of type" << checkType << "received from unknown node with UUID"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 NodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
// setup the MD5 hash for source verification in the header
|
||||
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
|
||||
|
||||
// stat collection for packets
|
||||
++_numCollectedPackets;
|
||||
_numCollectedBytes += datagram.size();
|
||||
|
||||
qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
if (bytesWritten < 0) {
|
||||
qDebug() << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString();
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
qint64 NodeList::writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
if (destinationNode) {
|
||||
// if we don't have an ovveriden address, assume they want to send to the node's active socket
|
||||
const HifiSockAddr* destinationSockAddr = &overridenSockAddr;
|
||||
if (overridenSockAddr.isNull()) {
|
||||
if (destinationNode->getActiveSocket()) {
|
||||
// use the node's active socket as the destination socket
|
||||
destinationSockAddr = destinationNode->getActiveSocket();
|
||||
} else {
|
||||
// we don't have a socket to send to, return 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
|
||||
}
|
||||
|
||||
// didn't have a destinationNode to send to, return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 NodeList::writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
return writeDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
|
||||
}
|
||||
|
||||
qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
|
||||
QByteArray statsPacket = byteArrayWithPopulatedHeader(PacketTypeNodeJsonStats);
|
||||
QDataStream statsPacketStream(&statsPacket, QIODevice::Append);
|
||||
|
||||
statsPacketStream << statsObject.toVariantMap();
|
||||
|
||||
return writeDatagram(statsPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret());
|
||||
}
|
||||
|
||||
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
quint8 pingType;
|
||||
quint64 ourOriginalTime, othersReplyTime;
|
||||
|
||||
packetStream >> pingType >> ourOriginalTime >> othersReplyTime;
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
int pingTime = now - ourOriginalTime;
|
||||
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
||||
|
||||
// The other node's expected time should be our original time plus the one way flight time
|
||||
// anything other than that is clock skew
|
||||
quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
|
||||
int clockSkew = othersReplyTime - othersExprectedReply;
|
||||
|
||||
sendingNode->setPingMs(pingTime / 1000);
|
||||
sendingNode->setClockSkewUsec(clockSkew);
|
||||
|
||||
const bool wantDebug = false;
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "PING_REPLY from node " << *sendingNode << "\n" <<
|
||||
" now: " << now << "\n" <<
|
||||
" ourTime: " << ourOriginalTime << "\n" <<
|
||||
" pingTime: " << pingTime << "\n" <<
|
||||
" oneWayFlightTime: " << oneWayFlightTime << "\n" <<
|
||||
" othersReplyTime: " << othersReplyTime << "\n" <<
|
||||
" othersExprectedReply: " << othersExprectedReply << "\n" <<
|
||||
" clockSkew: " << clockSkew;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
switch (packetTypeForPacket(packet)) {
|
||||
case PacketTypeDomainList: {
|
||||
processDomainServerList(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypeDomainServerAuthRequest: {
|
||||
// the domain-server has asked us to auth via a data-server
|
||||
processDomainServerAuthRequest(packet);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypePing: {
|
||||
// send back a reply
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
if (matchingNode) {
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
QByteArray replyPacket = constructPingReplyPacket(packet);
|
||||
writeDatagram(replyPacket, matchingNode, senderSockAddr);
|
||||
|
||||
// If we don't have a symmetric socket for this node and this socket doesn't match
|
||||
// what we have for public and local then set it as the symmetric.
|
||||
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
|
||||
if (matchingNode->getSymmetricSocket().isNull()) {
|
||||
if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) {
|
||||
matchingNode->setSymmetricSocket(senderSockAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypePingReply: {
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
|
||||
if (sendingNode) {
|
||||
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
// activate the appropriate socket for this node, if not yet updated
|
||||
activateSocketFromNodeCommunication(packet, sendingNode);
|
||||
|
||||
// set the ping time for this node for stat collection
|
||||
timePingReply(packet, sendingNode);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypeStunResponse: {
|
||||
// a STUN packet begins with 00, we've checked the second zero with packetVersionMatch
|
||||
// pass it along so it can be processed into our public address and port
|
||||
processSTUNResponse(packet);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// the node decided not to do anything with this packet
|
||||
// if it comes from a known source we should keep that node alive
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
if (matchingNode) {
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int NodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray &packet) {
|
||||
QMutexLocker locker(&matchingNode->getMutex());
|
||||
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
matchingNode->recordBytesReceived(packet.size());
|
||||
|
||||
if (!matchingNode->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(matchingNode.data());
|
||||
}
|
||||
|
||||
QMutexLocker linkedDataLocker(&matchingNode->getLinkedData()->getMutex());
|
||||
|
||||
return matchingNode->getLinkedData()->parseData(packet);
|
||||
}
|
||||
|
||||
int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
|
||||
if (matchingNode) {
|
||||
updateNodeWithDataFromPacket(matchingNode, packet);
|
||||
}
|
||||
|
||||
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) {
|
||||
const int WAIT_TIME = 10; // wait up to 10ms in the try lock case
|
||||
SharedNodePointer node;
|
||||
// if caller wants us to block and guarantee the correct answer, then honor that request
|
||||
if (blockingLock) {
|
||||
// this will block till we can get access
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
node = _nodeHash.value(nodeUUID);
|
||||
} else if (_nodeHashMutex.tryLock(WAIT_TIME)) { // some callers are willing to get wrong answers but not block
|
||||
node = _nodeHash.value(nodeUUID);
|
||||
_nodeHashMutex.unlock();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(packet);
|
||||
|
||||
// return the matching node, or NULL if there is no match
|
||||
return nodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
NodeHash NodeList::getNodeHash() {
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
return NodeHash(_nodeHash);
|
||||
}
|
||||
|
||||
void NodeList::eraseAllNodes() {
|
||||
qDebug() << "Clearing the NodeList. Deleting all nodes in list.";
|
||||
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
|
||||
NodeHash::iterator nodeItem = _nodeHash.begin();
|
||||
|
||||
// iterate the nodes in the list
|
||||
while (nodeItem != _nodeHash.end()) {
|
||||
nodeItem = killNodeAtHashIterator(nodeItem);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::reset() {
|
||||
eraseAllNodes();
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
// clear the domain connection information
|
||||
_domainInfo.clearConnectionInfo();
|
||||
}
|
||||
|
||||
void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) {
|
||||
_nodeTypesOfInterest << nodeTypeToAdd;
|
||||
}
|
||||
|
||||
void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) {
|
||||
_nodeTypesOfInterest.unite(setOfNodeTypes);
|
||||
}
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442;
|
||||
const int NUM_BYTES_STUN_HEADER = 20;
|
||||
const unsigned int NUM_STUN_REQUESTS_BEFORE_FALLBACK = 5;
|
||||
|
||||
void NodeList::sendSTUNRequest() {
|
||||
const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io";
|
||||
const unsigned short STUN_SERVER_PORT = 3478;
|
||||
|
||||
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;
|
||||
QUuid randomUUID = QUuid::createUuid();
|
||||
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
|
||||
|
||||
// lookup the IP for the STUN server
|
||||
static HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qDebug("Sending intial stun request to %s", stunSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||
stunSockAddr.getAddress(), stunSockAddr.getPort());
|
||||
|
||||
_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.",
|
||||
STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
}
|
||||
|
||||
// reset the public address and port
|
||||
// use 0 so the DS knows to act as out STUN server
|
||||
_publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort());
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processSTUNResponse(const QByteArray& packet) {
|
||||
// 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
|
||||
const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4;
|
||||
const uint16_t XOR_MAPPED_ADDRESS_TYPE = htons(0x0020);
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||
|
||||
int attributeStartIndex = NUM_BYTES_STUN_HEADER;
|
||||
|
||||
if (memcmp(packet.data() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
|
||||
&RFC_5389_MAGIC_COOKIE_NETWORK_ORDER,
|
||||
sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) {
|
||||
|
||||
// enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE
|
||||
while (attributeStartIndex < packet.size()) {
|
||||
if (memcmp(packet.data() + 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_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;
|
||||
memcpy(&addressFamily, packet.data(), sizeof(addressFamily));
|
||||
|
||||
byteIndex += sizeof(addressFamily);
|
||||
|
||||
if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) {
|
||||
// grab the X-Port
|
||||
uint16_t xorMappedPort = 0;
|
||||
memcpy(&xorMappedPort, packet.data() + byteIndex, sizeof(xorMappedPort));
|
||||
|
||||
uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
|
||||
|
||||
byteIndex += sizeof(xorMappedPort);
|
||||
|
||||
// grab the X-Address
|
||||
uint32_t xorMappedAddress = 0;
|
||||
memcpy(&xorMappedAddress, packet.data() + byteIndex, sizeof(xorMappedAddress));
|
||||
|
||||
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
|
||||
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||
|
||||
if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) {
|
||||
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
|
||||
|
||||
qDebug("New public socket received from STUN server is %s:%hu",
|
||||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
}
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// push forward attributeStartIndex by the length of this attribute
|
||||
const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
|
||||
|
||||
uint16_t attributeLength = 0;
|
||||
memcpy(&attributeLength, packet.data() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
|
||||
sizeof(attributeLength));
|
||||
attributeLength = ntohs(attributeLength);
|
||||
|
||||
attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
|
||||
NodeHash::iterator nodeItemToKill = _nodeHash.find(nodeUUID);
|
||||
if (nodeItemToKill != _nodeHash.end()) {
|
||||
killNodeAtHashIterator(nodeItemToKill);
|
||||
}
|
||||
}
|
||||
|
||||
NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill) {
|
||||
qDebug() << "Killed" << *nodeItemToKill.value();
|
||||
emit nodeKilled(nodeItemToKill.value());
|
||||
return _nodeHash.erase(nodeItemToKill);
|
||||
}
|
||||
|
||||
void NodeList::processKillNode(const QByteArray& dataByteArray) {
|
||||
// read the node id
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(dataByteArray), NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// kill the node with this UUID, if it exists
|
||||
killNodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
void NodeList::sendDomainServerCheckIn() {
|
||||
if (_publicSockAddr.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 if (!_domainInfo.getIP().isNull()) {
|
||||
if (_domainInfo.getRootAuthenticationURL().isEmpty()
|
||||
|| !_sessionUUID.isNull()
|
||||
|| !_domainInfo.getRegistrationToken().isEmpty() ) {
|
||||
// construct the DS check in packet
|
||||
|
||||
PacketType domainPacketType = _sessionUUID.isNull() ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
||||
|
||||
QUuid packetUUID = (domainPacketType == PacketTypeDomainListRequest)
|
||||
? _sessionUUID : _domainInfo.getAssignmentUUID();
|
||||
|
||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID);
|
||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||
|
||||
if (domainPacketType == PacketTypeDomainConnectRequest) {
|
||||
// we may need a registration token to present to the domain-server
|
||||
packetStream << (quint8) !_domainInfo.getRegistrationToken().isEmpty();
|
||||
|
||||
if (!_domainInfo.getRegistrationToken().isEmpty()) {
|
||||
// if we have a registration token send that along in the request
|
||||
packetStream << _domainInfo.getRegistrationToken();
|
||||
}
|
||||
}
|
||||
|
||||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr
|
||||
<< HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
|
||||
<< (quint8) _nodeTypesOfInterest.size();
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
|
||||
packetStream << nodeTypeOfInterest;
|
||||
}
|
||||
|
||||
writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret());
|
||||
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();
|
||||
}
|
||||
|
||||
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||
// so emit our signal that indicates that
|
||||
emit limitOfSilentDomainCheckInsReached();
|
||||
}
|
||||
|
||||
// increment the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns++;
|
||||
} else if (AccountManager::getInstance().hasValidAccessToken()) {
|
||||
// we have an access token we can use for the authentication server the domain-server requested
|
||||
// so ask that server to provide us with information to connect to the domain-server
|
||||
requestAuthForDomainServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::setSessionUUID(const QUuid& sessionUUID) {
|
||||
QUuid oldUUID = _sessionUUID;
|
||||
_sessionUUID = sessionUUID;
|
||||
|
||||
if (sessionUUID != oldUUID) {
|
||||
qDebug() << "NodeList UUID changed from" << uuidStringWithoutCurlyBraces(oldUUID)
|
||||
<< "to" << uuidStringWithoutCurlyBraces(_sessionUUID);
|
||||
emit uuidChanged(sessionUUID);
|
||||
}
|
||||
}
|
||||
|
||||
int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||
// this is a packet from the domain server, reset the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
// if this was the first domain-server list from this domain, we've now connected
|
||||
_domainInfo.setIsConnected(true);
|
||||
|
||||
int readNodes = 0;
|
||||
|
||||
// setup variables to read into from QDataStream
|
||||
qint8 nodeType;
|
||||
|
||||
QUuid nodeUUID, connectionUUID;
|
||||
|
||||
HifiSockAddr nodePublicSocket;
|
||||
HifiSockAddr nodeLocalSocket;
|
||||
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
// pull our owner UUID from the packet, it's always the first thing
|
||||
QUuid newUUID;
|
||||
packetStream >> newUUID;
|
||||
setSessionUUID(newUUID);
|
||||
|
||||
// pull each node in the packet
|
||||
while(packetStream.device()->pos() < packet.size()) {
|
||||
packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket;
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
if (nodePublicSocket.getAddress().isNull()) {
|
||||
nodePublicSocket.setAddress(_domainInfo.getIP());
|
||||
}
|
||||
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket);
|
||||
|
||||
packetStream >> connectionUUID;
|
||||
node->setConnectionSecret(connectionUUID);
|
||||
}
|
||||
|
||||
// ping inactive nodes in conjunction with receipt of list from domain-server
|
||||
// this makes it happen every second and also pings any newly added nodes
|
||||
pingInactiveNodes();
|
||||
|
||||
return readNodes;
|
||||
}
|
||||
|
||||
void NodeList::domainServerAuthReply(const QJsonObject& jsonObject) {
|
||||
_domainInfo.parseAuthInformationFromJsonObject(jsonObject);
|
||||
}
|
||||
|
||||
void NodeList::requestAuthForDomainServer() {
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "domainServerAuthReply";
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest("/api/v1/domains/"
|
||||
+ uuidStringWithoutCurlyBraces(_domainInfo.getUUID()) + "/auth.json",
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void NodeList::processDomainServerAuthRequest(const QByteArray& packet) {
|
||||
QDataStream authPacketStream(packet);
|
||||
authPacketStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
_domainInfo.setUUID(uuidFromPacketHeader(packet));
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
// grab the hostname this domain-server wants us to authenticate with
|
||||
QUrl authenticationRootURL;
|
||||
authPacketStream >> authenticationRootURL;
|
||||
|
||||
accountManager.setAuthURL(authenticationRootURL);
|
||||
_domainInfo.setRootAuthenticationURL(authenticationRootURL);
|
||||
|
||||
if (AccountManager::getInstance().checkAndSignalForAccessToken()) {
|
||||
// request a domain-server auth
|
||||
requestAuthForDomainServer();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::sendAssignment(Assignment& assignment) {
|
||||
|
||||
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
|
||||
? PacketTypeCreateAssignment
|
||||
: PacketTypeRequestAssignment;
|
||||
|
||||
QByteArray packet = byteArrayWithPopulatedHeader(assignmentPacketType);
|
||||
QDataStream packetStream(&packet, QIODevice::Append);
|
||||
|
||||
packetStream << assignment;
|
||||
|
||||
static HifiSockAddr DEFAULT_ASSIGNMENT_SOCKET(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, DEFAULT_DOMAIN_SERVER_PORT);
|
||||
|
||||
const HifiSockAddr* assignmentServerSocket = _assignmentServerSocket.isNull()
|
||||
? &DEFAULT_ASSIGNMENT_SOCKET
|
||||
: &_assignmentServerSocket;
|
||||
|
||||
_nodeSocket.writeDatagram(packet, assignmentServerSocket->getAddress(), assignmentServerSocket->getPort());
|
||||
}
|
||||
|
||||
QByteArray NodeList::constructPingPacket(PingType_t pingType) {
|
||||
QByteArray pingPacket = byteArrayWithPopulatedHeader(PacketTypePing);
|
||||
|
||||
QDataStream packetStream(&pingPacket, QIODevice::Append);
|
||||
|
||||
packetStream << pingType;
|
||||
packetStream << usecTimestampNow();
|
||||
|
||||
return pingPacket;
|
||||
}
|
||||
|
||||
QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
||||
QDataStream pingPacketStream(pingPacket);
|
||||
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||
|
||||
PingType_t typeFromOriginalPing;
|
||||
pingPacketStream >> typeFromOriginalPing;
|
||||
|
||||
quint64 timeFromOriginalPing;
|
||||
pingPacketStream >> timeFromOriginalPing;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithPopulatedHeader(PacketTypePingReply);
|
||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||
|
||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||
|
||||
return replyPacket;
|
||||
}
|
||||
|
||||
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = constructPingPacket(PingType::Local);
|
||||
writeDatagram(localPingPacket, node, node->getLocalSocket());
|
||||
|
||||
QByteArray publicPingPacket = constructPingPacket(PingType::Public);
|
||||
writeDatagram(publicPingPacket, node, node->getPublicSocket());
|
||||
|
||||
if (!node->getSymmetricSocket().isNull()) {
|
||||
QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric);
|
||||
writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket());
|
||||
}
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
_nodeHashMutex.lock();
|
||||
|
||||
if (!_nodeHash.contains(uuid)) {
|
||||
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
|
||||
SharedNodePointer newNodeSharedPointer(newNode, &QObject::deleteLater);
|
||||
|
||||
_nodeHash.insert(newNode->getUUID(), newNodeSharedPointer);
|
||||
|
||||
_nodeHashMutex.unlock();
|
||||
|
||||
qDebug() << "Added" << *newNode;
|
||||
|
||||
emit nodeAdded(newNodeSharedPointer);
|
||||
|
||||
return newNodeSharedPointer;
|
||||
} else {
|
||||
_nodeHashMutex.unlock();
|
||||
|
||||
return updateSocketsForNode(uuid, publicSocket, localSocket);
|
||||
}
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::updateSocketsForNode(const QUuid& uuid,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
|
||||
SharedNodePointer matchingNode = nodeWithUUID(uuid);
|
||||
|
||||
if (matchingNode) {
|
||||
// perform appropriate updates to this node
|
||||
QMutexLocker locker(&matchingNode->getMutex());
|
||||
|
||||
// check if we need to change this node's public or local sockets
|
||||
if (publicSocket != matchingNode->getPublicSocket()) {
|
||||
matchingNode->setPublicSocket(publicSocket);
|
||||
qDebug() << "Public socket change for node" << *matchingNode;
|
||||
}
|
||||
|
||||
if (localSocket != matchingNode->getLocalSocket()) {
|
||||
matchingNode->setLocalSocket(localSocket);
|
||||
qDebug() << "Local socket change for node" << *matchingNode;
|
||||
}
|
||||
}
|
||||
|
||||
return matchingNode;
|
||||
}
|
||||
|
||||
unsigned NodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
||||
unsigned n = 0;
|
||||
|
||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||
// only send to the NodeTypes we are asked to send to.
|
||||
if (destinationNodeTypes.contains(node->getType())) {
|
||||
writeDatagram(packet, node);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void NodeList::pingInactiveNodes() {
|
||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active link to this node, ping it to set that up
|
||||
pingPunchForInactiveNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
// deconstruct this ping packet to see if it is a public or local reply
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
quint8 pingType;
|
||||
packetStream >> pingType;
|
||||
|
||||
// if this is a local or public ping then we can activate a socket
|
||||
// we do nothing with agnostic pings, those are simply for timing
|
||||
if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) {
|
||||
sendingNode->activateLocalSocket();
|
||||
} else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) {
|
||||
sendingNode->activatePublicSocket();
|
||||
} else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) {
|
||||
sendingNode->activateSymmetricSocket();
|
||||
}
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::soloNodeOfType(char nodeType) {
|
||||
|
||||
if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
|
||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||
if (node->getType() == nodeType) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
void NodeList::getPacketStats(float& packetsPerSecond, float& bytesPerSecond) {
|
||||
packetsPerSecond = (float) _numCollectedPackets / ((float) _packetStatTimer.elapsed() / 1000.0f);
|
||||
bytesPerSecond = (float) _numCollectedBytes / ((float) _packetStatTimer.elapsed() / 1000.0f);
|
||||
}
|
||||
|
||||
void NodeList::resetPacketStats() {
|
||||
_numCollectedPackets = 0;
|
||||
_numCollectedBytes = 0;
|
||||
_packetStatTimer.restart();
|
||||
}
|
||||
|
||||
void NodeList::removeSilentNodes() {
|
||||
|
||||
_nodeHashMutex.lock();
|
||||
|
||||
NodeHash::iterator nodeItem = _nodeHash.begin();
|
||||
|
||||
while (nodeItem != _nodeHash.end()) {
|
||||
SharedNodePointer node = nodeItem.value();
|
||||
|
||||
node->getMutex().lock();
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||
// call our private method to kill this node (removes it and emits the right signal)
|
||||
nodeItem = killNodeAtHashIterator(nodeItem);
|
||||
} else {
|
||||
// we didn't kill this node, push the iterator forwards
|
||||
++nodeItem;
|
||||
}
|
||||
|
||||
node->getMutex().unlock();
|
||||
}
|
||||
|
||||
_nodeHashMutex.unlock();
|
||||
}
|
||||
|
||||
const QString QSETTINGS_GROUP_NAME = "NodeList";
|
||||
const QString DOMAIN_SERVER_SETTING_KEY = "domainServerHostname";
|
||||
|
||||
void NodeList::loadData(QSettings *settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
QString domainServerHostname = settings->value(DOMAIN_SERVER_SETTING_KEY).toString();
|
||||
|
||||
if (domainServerHostname.size() > 0) {
|
||||
_domainInfo.setHostname(domainServerHostname);
|
||||
} else {
|
||||
_domainInfo.setHostname(DEFAULT_DOMAIN_HOSTNAME);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void NodeList::saveData(QSettings* settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
if (_domainInfo.getHostname() != DEFAULT_DOMAIN_HOSTNAME) {
|
||||
// the user is using a different hostname, store it
|
||||
settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainInfo.getHostname()));
|
||||
} else {
|
||||
// the user has switched back to default, remove the current setting
|
||||
settings->remove(DOMAIN_SERVER_SETTING_KEY);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
|
@ -15,10 +15,6 @@
|
|||
#include <cctype>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
@ -26,7 +22,6 @@
|
|||
#include <QtCore/QDebug>
|
||||
|
||||
#include "OctalCode.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
quint64 usecTimestamp(const timeval *time) {
|
||||
|
@ -44,7 +39,7 @@ quint64 usecTimestampNow() {
|
|||
return (now.tv_sec * 1000000 + now.tv_usec) + ::usecTimestampNowAdjust;
|
||||
}
|
||||
|
||||
float randFloat () {
|
||||
float randFloat() {
|
||||
return (rand() % 10000)/10000.f;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
const int BYTES_PER_FLAGS = 1;
|
||||
typedef unsigned char rgbColor[BYTES_PER_COLOR];
|
||||
|
@ -45,7 +43,6 @@ struct xColor {
|
|||
unsigned char blue;
|
||||
};
|
||||
|
||||
|
||||
static const float ZERO = 0.0f;
|
||||
static const float ONE = 1.0f;
|
||||
static const float ONE_HALF = 0.5f;
|
||||
|
@ -69,8 +66,6 @@ static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
|||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
|
||||
const int MAX_PACKET_SIZE = 1500;
|
||||
|
||||
quint64 usecTimestamp(const timeval *time);
|
||||
quint64 usecTimestampNow();
|
||||
void usecTimestampNowForceClockSkew(int clockSkew);
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef __Syssocket__
|
||||
#define __Syssocket__
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WINSOCK_API_LINKAGE
|
||||
#include <winsock2.h>
|
||||
#ifndef _timeval_
|
||||
#define _timeval_
|
||||
#endif
|
||||
typedef SSIZE_T ssize_t;
|
||||
typedef ULONG32 in_addr_t;
|
||||
typedef USHORT in_port_t;
|
||||
typedef USHORT uint16_t;
|
||||
typedef ULONG32 socklen_t;
|
||||
|
||||
#endif _Win32
|
||||
|
||||
#endif __Syssocket__
|
|
@ -1,14 +1,61 @@
|
|||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define _timeval_
|
||||
//
|
||||
// Systime.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "Systime.h"
|
||||
|
||||
int gettimeofday( timeval* p_tv, timezone* p_tz )
|
||||
{
|
||||
int tt = timeGetTime();
|
||||
/**
|
||||
* gettimeofday
|
||||
* Implementation according to:
|
||||
* The Open Group Base Specifications Issue 6
|
||||
* IEEE Std 1003.1, 2004 Edition
|
||||
*/
|
||||
|
||||
/**
|
||||
* THIS SOFTWARE IS NOT COPYRIGHTED
|
||||
*
|
||||
* This source code is offered for use in the public domain. You may
|
||||
* use, modify or distribute it freely.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful but
|
||||
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
|
||||
* DISCLAIMED. This includes but is not limited to warranties of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* Contributed by:
|
||||
* Danny Smith <dannysmith@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
p_tv->tv_sec = tt / 1000;
|
||||
p_tv->tv_usec = tt % 1000 * 1000;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
/** Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
|
||||
#define _W32_FT_OFFSET (116444736000000000ULL)
|
||||
|
||||
int gettimeofday(timeval* p_tv, timezone* p_tz) {
|
||||
|
||||
union {
|
||||
unsigned long long ns100; /**time since 1 Jan 1601 in 100ns units */
|
||||
FILETIME ft;
|
||||
} _now;
|
||||
|
||||
if (p_tv) {
|
||||
GetSystemTimeAsFileTime (&_now.ft);
|
||||
p_tv->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL );
|
||||
p_tv->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL);
|
||||
}
|
||||
|
||||
/** Always return 0 as per Open Group Base Specifications Issue 6.
|
||||
Do not set errno on error. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,39 +1,27 @@
|
|||
#ifndef __Systime__
|
||||
#define __Systime__
|
||||
//
|
||||
// Systime.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Systime_h
|
||||
#define hifi_Systime_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef WIN32
|
||||
|
||||
#ifdef _WINSOCK2API_
|
||||
#define _timeval_
|
||||
#endif
|
||||
|
||||
#ifndef _timeval_
|
||||
#define _timeval_
|
||||
/*
|
||||
* Structure returned by gettimeofday(2) system call,
|
||||
* and used in other calls.
|
||||
*/
|
||||
|
||||
// this is a bit of a hack for now, but sometimes on windows
|
||||
// we need timeval defined here, sometimes we get it
|
||||
// from winsock.h
|
||||
#ifdef WANT_TIMEVAL
|
||||
struct timeval {
|
||||
long tv_sec; /* seconds */
|
||||
long tv_usec; /* and microseconds */
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif _timeval_
|
||||
#include <winsock2.h>
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest; /* minutes west of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
|
||||
int gettimeofday( struct timeval* p_tv, struct timezone* p_tz );
|
||||
int gettimeofday(struct timeval* p_tv, struct timezone* p_tz);
|
||||
|
||||
#endif _Win32
|
||||
#endif
|
||||
|
||||
#endif __Systime__
|
||||
#endif // hifi_Systime_h
|
|
@ -24,9 +24,17 @@ include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
|||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link ZLIB
|
||||
# link ZLIB and GnuTLS
|
||||
find_package(ZLIB)
|
||||
include_directories("${ZLIB_INCLUDE_DIRS}")
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets Qt5::Script)
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" "${GNUTLS_LIBRARY}" Qt5::Widgets Qt5::Script)
|
|
@ -31,8 +31,19 @@ link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
|||
# link in the hifi voxels library
|
||||
link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link in the hifi networking library
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link GnuTLS
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
IF (WIN32)
|
||||
target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
add_definitions(-Dssize_t=long)
|
||||
ENDIF(WIN32)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Script)
|
||||
include_directories(SYSTEM "${GNUTLS_INCLUDE_DIR}")
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Script "${GNUTLS_LIBRARY}")
|
Loading…
Reference in a new issue