2016-01-18 15:33:29 +00:00
|
|
|
// Copyright (c) 2011-2016 The Cryptonote developers
|
2015-09-18 11:55:31 +00:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2015-07-30 15:22:07 +00:00
|
|
|
|
|
|
|
#include "P2pConnectionProxy.h"
|
|
|
|
|
|
|
|
#include "LevinProtocol.h"
|
|
|
|
#include "P2pContext.h"
|
|
|
|
#include "P2pNode.h"
|
|
|
|
|
|
|
|
#include <System/InterruptedException.h>
|
|
|
|
|
|
|
|
using namespace System;
|
|
|
|
|
|
|
|
namespace CryptoNote {
|
|
|
|
|
|
|
|
P2pConnectionProxy::P2pConnectionProxy(P2pContextOwner&& ctx, IP2pNodeInternal& node)
|
|
|
|
: m_contextOwner(std::move(ctx)), m_context(m_contextOwner.get()), m_node(node) {}
|
|
|
|
|
|
|
|
P2pConnectionProxy::~P2pConnectionProxy() {
|
|
|
|
m_context.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool P2pConnectionProxy::processIncomingHandshake() {
|
|
|
|
LevinProtocol::Command cmd;
|
|
|
|
if (!m_context.readCommand(cmd)) {
|
|
|
|
throw std::runtime_error("Connection unexpectedly closed");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd.command == COMMAND_HANDSHAKE::ID) {
|
|
|
|
handleHandshakeRequest(cmd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd.command == COMMAND_PING::ID) {
|
|
|
|
COMMAND_PING::response resp{ PING_OK_RESPONSE_STATUS_TEXT, m_node.getPeerId() };
|
|
|
|
m_context.writeMessage(makeReply(COMMAND_PING::ID, LevinProtocol::encode(resp), LEVIN_PROTOCOL_RETCODE_SUCCESS));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw std::runtime_error("Unexpected command: " + std::to_string(cmd.command));
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::read(P2pMessage& message) {
|
|
|
|
if (!m_readQueue.empty()) {
|
|
|
|
message = std::move(m_readQueue.front());
|
|
|
|
m_readQueue.pop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
LevinProtocol::Command cmd;
|
|
|
|
if (!m_context.readCommand(cmd)) {
|
|
|
|
throw InterruptedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
message.type = cmd.command;
|
|
|
|
|
|
|
|
if (cmd.command == COMMAND_HANDSHAKE::ID) {
|
|
|
|
handleHandshakeResponse(cmd, message);
|
|
|
|
break;
|
|
|
|
} else if (cmd.command == COMMAND_TIMED_SYNC::ID) {
|
|
|
|
handleTimedSync(cmd);
|
|
|
|
} else {
|
|
|
|
message.data = std::move(cmd.buf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::write(const P2pMessage &message) {
|
|
|
|
if (message.type == COMMAND_HANDSHAKE::ID) {
|
|
|
|
writeHandshake(message);
|
|
|
|
} else {
|
|
|
|
m_context.writeMessage(P2pContext::Message(P2pMessage(message), P2pContext::Message::NOTIFY));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::ban() {
|
|
|
|
// not implemented
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::stop() {
|
|
|
|
m_context.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::writeHandshake(const P2pMessage &message) {
|
|
|
|
CORE_SYNC_DATA coreSync;
|
|
|
|
LevinProtocol::decode(message.data, coreSync);
|
|
|
|
|
|
|
|
if (m_context.isIncoming()) {
|
|
|
|
// response
|
|
|
|
COMMAND_HANDSHAKE::response res;
|
|
|
|
res.node_data = m_node.getNodeData();
|
|
|
|
res.payload_data = coreSync;
|
|
|
|
res.local_peerlist = m_node.getLocalPeerList();
|
|
|
|
m_context.writeMessage(makeReply(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(res), LEVIN_PROTOCOL_RETCODE_SUCCESS));
|
|
|
|
m_node.tryPing(m_context);
|
|
|
|
} else {
|
|
|
|
// request
|
|
|
|
COMMAND_HANDSHAKE::request req;
|
|
|
|
req.node_data = m_node.getNodeData();
|
|
|
|
req.payload_data = coreSync;
|
|
|
|
m_context.writeMessage(makeRequest(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(req)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::handleHandshakeRequest(const LevinProtocol::Command& cmd) {
|
|
|
|
COMMAND_HANDSHAKE::request req;
|
|
|
|
if (!LevinProtocol::decode<COMMAND_HANDSHAKE::request>(cmd.buf, req)) {
|
|
|
|
throw std::runtime_error("Failed to decode COMMAND_HANDSHAKE request");
|
|
|
|
}
|
|
|
|
|
|
|
|
m_node.handleNodeData(req.node_data, m_context);
|
|
|
|
m_readQueue.push(P2pMessage{
|
|
|
|
cmd.command, LevinProtocol::encode(req.payload_data) }); // enqueue payload info
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::handleHandshakeResponse(const LevinProtocol::Command& cmd, P2pMessage& message) {
|
|
|
|
if (m_context.isIncoming()) {
|
|
|
|
// handshake should be already received by P2pNode
|
|
|
|
throw std::runtime_error("Unexpected COMMAND_HANDSHAKE from incoming connection");
|
|
|
|
}
|
|
|
|
|
|
|
|
COMMAND_HANDSHAKE::response res;
|
|
|
|
if (!LevinProtocol::decode(cmd.buf, res)) {
|
|
|
|
throw std::runtime_error("Invalid handshake message format");
|
|
|
|
}
|
|
|
|
|
|
|
|
m_node.handleNodeData(res.node_data, m_context);
|
|
|
|
m_node.handleRemotePeerList(res.local_peerlist, res.node_data.local_time);
|
|
|
|
|
|
|
|
message.data = LevinProtocol::encode(res.payload_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void P2pConnectionProxy::handleTimedSync(const LevinProtocol::Command& cmd) {
|
|
|
|
if (cmd.isResponse) {
|
|
|
|
COMMAND_TIMED_SYNC::response res;
|
|
|
|
LevinProtocol::decode(cmd.buf, res);
|
|
|
|
m_node.handleRemotePeerList(res.local_peerlist, res.local_time);
|
|
|
|
} else {
|
|
|
|
// we ignore information from the request
|
|
|
|
// COMMAND_TIMED_SYNC::request req;
|
|
|
|
// LevinProtocol::decode(cmd.buf, req);
|
|
|
|
COMMAND_TIMED_SYNC::response res;
|
|
|
|
|
|
|
|
res.local_time = time(nullptr);
|
|
|
|
res.local_peerlist = m_node.getLocalPeerList();
|
|
|
|
res.payload_data = m_node.getGenesisPayload();
|
|
|
|
|
|
|
|
m_context.writeMessage(makeReply(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(res), LEVIN_PROTOCOL_RETCODE_SUCCESS));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|