From caf2473df82741db635097b8f03a2ec1e4f44976 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 1 Apr 2014 13:08:00 -0700 Subject: [PATCH] add a DTLSSession object to handle GnuTLS DTLS sessions --- assignment-client/src/AssignmentClient.cpp | 4 + domain-server/src/DomainServer.cpp | 168 +++++++++++---------- domain-server/src/main.cpp | 2 - libraries/shared/src/DTLSSession.cpp | 43 ++++++ libraries/shared/src/DTLSSession.h | 32 ++++ libraries/shared/src/DomainInfo.cpp | 21 ++- libraries/shared/src/DomainInfo.h | 7 +- libraries/shared/src/NodeList.cpp | 23 +-- libraries/shared/src/NodeList.h | 4 +- 9 files changed, 206 insertions(+), 98 deletions(-) create mode 100644 libraries/shared/src/DTLSSession.cpp create mode 100644 libraries/shared/src/DTLSSession.h diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index aa20f2ff29..7da87e4e5d 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include #include @@ -31,6 +33,8 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), _currentAssignment() { + gnutls_global_init(); + setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("assignment-client"); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index a9bb6c4cfc..d6fc91c8e7 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -39,7 +39,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : _x509Credentials(NULL), _dhParams(NULL), _priorityCache(NULL) -{ +{ + gnutls_global_init(); + setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); @@ -80,28 +82,30 @@ DomainServer::DomainServer(int argc, char* argv[]) : bool DomainServer::optionallySetupDTLS() { if (readX509KeyAndCertificate()) { - 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); - - _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 (_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); + + _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."; + } return true; } else { @@ -180,7 +184,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { domainServerPort = _argumentList.value(argumentIndex + 1).toUShort(); } - unsigned short domainServerDTLSPort = -1; + unsigned short domainServerDTLSPort = 0; if (_isUsingDTLS) { domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT; @@ -532,69 +536,17 @@ void DomainServer::readAvailableDatagrams() { HifiSockAddr senderSockAddr; QByteArray receivedPacket; + + + static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); + static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); while (nodeList->getNodeSocket().hasPendingDatagrams()) { receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); - - 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(&dtlsPort)); - } + if (packetTypeForPacket(receivedPacket) && nodeList->packetVersionAndHashMatch(receivedPacket)) { - nodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr); - } - } -} - -void DomainServer::readAvailableDTLSDatagrams() { - -} - -void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { - NodeList* nodeList = NodeList::getInstance(); - - static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); - static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); - - 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; - - int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, - 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 { - // new node - add this node to our NodeList - // and send back session UUID right away - addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr); - } - - } else if (requestType == PacketTypeRequestAssignment) { - // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); @@ -626,7 +578,7 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS } else { if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() - << "from" << senderSockAddr; + << "from" << senderSockAddr; noisyMessage = true; } } @@ -634,6 +586,58 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS 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(&dtlsPort)); + } + + nodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr); + } + } +} + +void DomainServer::readAvailableDTLSDatagrams() { + +} + +void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { + NodeList* nodeList = NodeList::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; + + int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, + 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 { + // 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) { diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 4bfa35fac5..096431e0a4 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -24,8 +24,6 @@ int main(int argc, char* argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); #endif - gnutls_global_init(); - qInstallMessageHandler(Logging::verboseMessageHandler); DomainServer domainServer(argc, argv); diff --git a/libraries/shared/src/DTLSSession.cpp b/libraries/shared/src/DTLSSession.cpp new file mode 100644 index 0000000000..ce14d91f27 --- /dev/null +++ b/libraries/shared/src/DTLSSession.cpp @@ -0,0 +1,43 @@ +// +// DTLSSession.cpp +// hifi +// +// Created by Stephen Birarda on 2014-04-01. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "DTLSSession.h" + +static int socketPullTimeout(gnutls_transport_ptr_t ptr, unsigned int ms) { + return 1; +} + +static ssize_t socketPull(gnutls_transport_ptr_t ptr, void* buffer, size_t size) { + DTLSSession* session = static_cast(ptr); + QUdpSocket& dtlsSocket = session->_dtlsSocket; + + if (dtlsSocket.hasPendingDatagrams()) { + return dtlsSocket.read(reinterpret_cast(buffer), size); + } else { + gnutls_transport_set_errno(session->_gnutlsSession, GNUTLS_E_AGAIN); + return 0; + } +} + +static ssize_t socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size) { + DTLSSession* session = static_cast(ptr); + QUdpSocket& dtlsSocket = session->_dtlsSocket; + + if (dtlsSocket.state() != QAbstractSocket::ConnectedState) { + gnutls_transport_set_errno(session->_gnutlsSession, GNUTLS_E_AGAIN); + return -1; + } + + return dtlsSocket.write(reinterpret_cast(buffer), size); +} + +DTLSSession::DTLSSession(QUdpSocket& dtlsSocket) : + _dtlsSocket(dtlsSocket) +{ + +} \ No newline at end of file diff --git a/libraries/shared/src/DTLSSession.h b/libraries/shared/src/DTLSSession.h new file mode 100644 index 0000000000..190373c155 --- /dev/null +++ b/libraries/shared/src/DTLSSession.h @@ -0,0 +1,32 @@ +// +// DTLSSession.h +// hifi +// +// Created by Stephen Birarda on 2014-04-01. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__DTLSSession__ +#define __hifi__DTLSSession__ + +#include + +#include + +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 ssize_t socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size); + +class DTLSSession { +public: + DTLSSession(QUdpSocket& dtlsSocket); + + friend int socketPullTimeout(gnutls_transport_ptr_t ptr, unsigned int ms); + friend ssize_t socketPull(gnutls_transport_ptr_t ptr, void* buffer, size_t size); + friend ssize_t socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size); +private: + QUdpSocket& _dtlsSocket; + gnutls_session_t _gnutlsSession; +}; + +#endif /* defined(__hifi__DTLSSession__) */ diff --git a/libraries/shared/src/DomainInfo.cpp b/libraries/shared/src/DomainInfo.cpp index 648f658619..1a360d1150 100644 --- a/libraries/shared/src/DomainInfo.cpp +++ b/libraries/shared/src/DomainInfo.cpp @@ -6,8 +6,9 @@ // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // -#include +#include +#include "NodeList.h" #include "PacketHeaders.h" #include "DomainInfo.h" @@ -16,12 +17,17 @@ DomainInfo::DomainInfo() : _uuid(), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _assignmentUUID(), + _isConnected(false), _requiresDTLS(false), - _isConnected(false) + _dtlsSession(NULL) { } +DomainInfo::~DomainInfo() { + delete _dtlsSession; +} + void DomainInfo::clearConnectionInfo() { _uuid = QUuid(); _isConnected = false; @@ -34,6 +40,12 @@ void DomainInfo::reset() { _requiresDTLS = false; } +void DomainInfo::initializeDTLSSession() { + if (!_dtlsSession) { + _dtlsSession = new DTLSSession(NodeList::getInstance()->getDTLSSocket()); + } +} + void DomainInfo::setSockAddr(const HifiSockAddr& sockAddr) { if (_sockAddr != sockAddr) { // we should reset on a sockAddr change @@ -107,8 +119,11 @@ void DomainInfo::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPac unsigned short dtlsPort = 0; memcpy(&dtlsPort, dtlsRequirementPacket.data() + numBytesPacketHeader, sizeof(dtlsPort)); + + qDebug() << "domain-server DTLS port changed to" << dtlsPort << "- Enabling DTLS."; + _sockAddr.setPort(dtlsPort); _requiresDTLS = true; - qDebug() << "domain-server DTLS port changed to" << dtlsPort << "- DTLS enabled."; + initializeDTLSSession(); } \ No newline at end of file diff --git a/libraries/shared/src/DomainInfo.h b/libraries/shared/src/DomainInfo.h index 63c7dcbf6d..71a0caabe0 100644 --- a/libraries/shared/src/DomainInfo.h +++ b/libraries/shared/src/DomainInfo.h @@ -14,6 +14,7 @@ #include #include +#include "DTLSSession.h" #include "HifiSockAddr.h" const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io"; @@ -24,6 +25,7 @@ class DomainInfo : public QObject { Q_OBJECT public: DomainInfo(); + ~DomainInfo(); void clearConnectionInfo(); @@ -54,15 +56,18 @@ private slots: signals: void hostnameChanged(const QString& hostname); void connectedToDomain(const QString& hostname); + void initializedDTLSSession(); private: void reset(); + void initializeDTLSSession(); QUuid _uuid; QString _hostname; HifiSockAddr _sockAddr; QUuid _assignmentUUID; - bool _requiresDTLS; bool _isConnected; + bool _requiresDTLS; + DTLSSession* _dtlsSession; }; #endif /* defined(__hifi__DomainInfo__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 167354542c..56986b4cc8 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -62,6 +62,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned _nodeHash(), _nodeHashMutex(QMutex::Recursive), _nodeSocket(this), + _dtlsSocket(NULL), _ownerType(newOwnerType), _nodeTypesOfInterest(), _sessionUUID(), @@ -78,9 +79,11 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort(); if (dtlsListenPort > 0) { - // we have a specfic DTLS port, bind that socket now - _dtlsSocket.bind(QHostAddress::AnyIPv4, dtlsListenPort); - qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket.localPort(); + // 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(); } // clear our NodeList when the domain changes @@ -96,12 +99,15 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned } QUdpSocket& NodeList::getDTLSSocket() { - if (_dtlsSocket.state() == QAbstractSocket::UnconnectedState) { - _dtlsSocket.bind(QHostAddress::AnyIPv4); - qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket.localPort(); + 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; + return *_dtlsSocket; } void NodeList::changeSendSocketBufferSize(int numSendBytes) { @@ -569,7 +575,7 @@ void NodeList::sendDomainServerCheckIn() { // 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()) { + } else if (!_domainInfo.getIP().isNull()) { // construct the DS check in packet PacketType domainPacketType = _sessionUUID.isNull() ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest; @@ -580,7 +586,6 @@ void NodeList::sendDomainServerCheckIn() { QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID); QDataStream packetStream(&domainServerPacket, QIODevice::Append); - // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort()) diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 613b431929..b12174296b 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -29,6 +29,8 @@ #include #include +#include + #include "DomainInfo.h" #include "Node.h" @@ -165,7 +167,7 @@ private: NodeHash _nodeHash; QMutex _nodeHashMutex; QUdpSocket _nodeSocket; - QUdpSocket _dtlsSocket; + QUdpSocket* _dtlsSocket; NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; DomainInfo _domainInfo;