request and store domain public key in ice-server memory

This commit is contained in:
Stephen Birarda 2016-02-22 16:14:11 -08:00
parent 9f9ef8764d
commit ddf4f029d9
2 changed files with 116 additions and 23 deletions

View file

@ -9,14 +9,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QTimer> #include "IceServer.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QTimer>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <LimitedNodeList.h> #include <LimitedNodeList.h>
#include <NetworkingConstants.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "IceServer.h"
const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000;
const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000;
@ -70,9 +74,10 @@ void IceServer::processPacket(std::unique_ptr<udt::Packet> packet) {
if (nlPacket->getType() == PacketType::ICEServerHeartbeat) { if (nlPacket->getType() == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket); SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket);
if (peer) {
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr()); peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr());
}
} else if (nlPacket->getType() == PacketType::ICEServerQuery) { } else if (nlPacket->getType() == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(nlPacket.get()); QDataStream heartbeatStream(nlPacket.get());
@ -114,31 +119,107 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) {
// pull the UUID, public and private sock addrs for this peer // pull the UUID, public and private sock addrs for this peer
QUuid senderUUID; QUuid senderUUID;
HifiSockAddr publicSocket, localSocket; HifiSockAddr publicSocket, localSocket;
QByteArray signature;
QDataStream heartbeatStream(&packet); QDataStream heartbeatStream(&packet);
heartbeatStream >> senderUUID >> publicSocket >> localSocket;
heartbeatStream >> senderUUID;
heartbeatStream >> publicSocket >> localSocket;
// make sure we have this sender in our peer hash auto signedPlaintext = QByteArray::fromRawData(packet.getPayload(), heartbeatStream.device()->pos());
SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); heartbeatStream >> signature;
if (!matchingPeer) { // make sure this is a verified heartbeat before performing any more processing
// if we don't have this sender we need to create them now if (isVerifiedHeartbeat(senderUUID, signedPlaintext, signature)) {
matchingPeer = QSharedPointer<NetworkPeer>::create(senderUUID, publicSocket, localSocket); // make sure we have this sender in our peer hash
_activePeers.insert(senderUUID, matchingPeer); SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
qDebug() << "Added a new network peer" << *matchingPeer; if (!matchingPeer) {
// if we don't have this sender we need to create them now
matchingPeer = QSharedPointer<NetworkPeer>::create(senderUUID, publicSocket, localSocket);
_activePeers.insert(senderUUID, matchingPeer);
qDebug() << "Added a new network peer" << *matchingPeer;
} else {
// we already had the peer so just potentially update their sockets
matchingPeer->setPublicSocket(publicSocket);
matchingPeer->setLocalSocket(localSocket);
}
// update our last heard microstamp for this network peer to now
matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
return matchingPeer;
} else { } else {
// we already had the peer so just potentially update their sockets // not verified, return the empty peer object
matchingPeer->setPublicSocket(publicSocket); return SharedNetworkPeer();
matchingPeer->setLocalSocket(localSocket);
} }
}
// update our last heard microstamp for this network peer to now bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature) {
matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); // check if we have a private key for this domain ID - if we do not then fire off the request for it
auto it = _domainPublicKeys.find(domainID);
if (it != _domainPublicKeys.end()) {
return matchingPeer; return true;
} else {
// we don't have a public key for this domain so we can't verify this heartbeat
// ask the metaverse API for the right public key and return false to indicate that this is not verified
requestDomainPublicKey(domainID);
return false;
}
}
void IceServer::requestDomainPublicKey(const QUuid& domainID) {
// send a request to the metaverse API for the public key for this domain
QNetworkAccessManager* manager = new QNetworkAccessManager { this };
connect(manager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
publicKeyURL.setPath(publicKeyPath);
QNetworkRequest publicKeyRequest { publicKeyURL };
publicKeyRequest.setAttribute(QNetworkRequest::User, domainID);
qDebug() << "Requesting public key for domain with ID" << domainID;
manager->get(publicKeyRequest);
}
void IceServer::publicKeyReplyFinished(QNetworkReply* reply) {
// get the domain ID from the QNetworkReply attribute
QUuid domainID = reply->request().attribute(QNetworkRequest::User).toUuid();
if (reply->error() == QNetworkReply::NoError) {
// pull out the public key and store it for this domain
// the response should be JSON
QJsonDocument responseDocument = QJsonDocument::fromJson(reply->readAll());
static const QString DATA_KEY = "data";
static const QString PUBLIC_KEY_KEY = "public_key";
static const QString STATUS_KEY = "status";
static const QString SUCCESS_VALUE = "success";
auto responseObject = responseDocument.object();
if (responseObject[STATUS_KEY].toString() == SUCCESS_VALUE) {
auto dataObject = responseObject[DATA_KEY].toObject();
if (dataObject.contains(PUBLIC_KEY_KEY)) {
_domainPublicKeys.emplace(domainID,
QByteArray::fromBase64(dataObject[PUBLIC_KEY_KEY].toString().toUtf8()));
} else {
qWarning() << "There was no public key present in response for domain with ID" << domainID;
}
} else {
qWarning() << "The metaverse API did not return success for public key request for domain with ID" << domainID;
}
} else {
// there was a problem getting the public key for the domain
// log it since it will be re-requested on the next heartbeat
qWarning() << "Error retreiving public key for domain with ID" << domainID << "-" << reply->errorString();
}
} }
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) { void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {

View file

@ -16,13 +16,15 @@
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
#include <QUdpSocket> #include <QUdpSocket>
#include <UUIDHasher.h>
#include <NetworkPeer.h> #include <NetworkPeer.h>
#include <HTTPConnection.h> #include <HTTPConnection.h>
#include <HTTPManager.h> #include <HTTPManager.h>
#include <NLPacket.h> #include <NLPacket.h>
#include <udt/Socket.h> #include <udt/Socket.h>
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash; class QNetworkReply;
class IceServer : public QCoreApplication, public HTTPRequestHandler { class IceServer : public QCoreApplication, public HTTPRequestHandler {
Q_OBJECT Q_OBJECT
@ -31,6 +33,7 @@ public:
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
private slots: private slots:
void clearInactivePeers(); void clearInactivePeers();
void publicKeyReplyFinished(QNetworkReply* reply);
private: private:
bool packetVersionMatch(const udt::Packet& packet); bool packetVersionMatch(const udt::Packet& packet);
void processPacket(std::unique_ptr<udt::Packet> packet); void processPacket(std::unique_ptr<udt::Packet> packet);
@ -38,10 +41,19 @@ private:
SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket); SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket);
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr); void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
bool isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature);
void requestDomainPublicKey(const QUuid& domainID);
QUuid _id; QUuid _id;
udt::Socket _serverSocket; udt::Socket _serverSocket;
using NetworkPeerHash = QHash<QUuid, SharedNetworkPeer>;
NetworkPeerHash _activePeers; NetworkPeerHash _activePeers;
HTTPManager _httpManager; HTTPManager _httpManager;
using DomainPublicKeyHash = std::unordered_map<QUuid, QByteArray>;
DomainPublicKeyHash _domainPublicKeys;
}; };
#endif // hifi_IceServer_h #endif // hifi_IceServer_h