// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "P2pConnectionProxy.h" #include "LevinProtocol.h" #include "P2pContext.h" #include "P2pNode.h" #include 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(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)); } } }