/// <summary> /// Processes a Hello Ixian protocol message and updates the `PresenceList` as appropriate. /// </summary> /// <remarks> /// This function should normally be called from `NetworkProtocol.parseProtocolMessage()` /// </remarks> /// <param name="endpoint">Remote endpoint from which the message was received.</param> /// <param name="reader">Reader object placed at the beginning of the hello message data.</param> /// <returns>True if the message was formatted properly and accepted.</returns> public static bool processHelloMessage(RemoteEndpoint endpoint, BinaryReader reader) { // Node already has a presence if (endpoint.presence != null) { // Ignore the hello message in this case return(false); } // Another layer to catch any incompatible node exceptions for the hello message try { int protocol_version = reader.ReadInt32(); Logging.info(string.Format("Received Hello: Node version {0}", protocol_version)); // Check for incompatible nodes if (protocol_version < CoreConfig.protocolVersion) { Logging.warn(String.Format("Hello: Connected node version ({0}) is too old! Upgrade the node.", protocol_version)); sendBye(endpoint, ProtocolByeCode.deprecated, string.Format("Your node version is too old. Should be at least {0} is {1}", CoreConfig.protocolVersion, protocol_version), CoreConfig.protocolVersion.ToString(), true); return(false); } int addrLen = reader.ReadInt32(); byte[] addr = reader.ReadBytes(addrLen); bool test_net = reader.ReadBoolean(); char node_type = reader.ReadChar(); string node_version = reader.ReadString(); string device_id = reader.ReadString(); int pkLen = reader.ReadInt32(); byte[] pubkey = null; if (pkLen > 0) { pubkey = reader.ReadBytes(pkLen); } endpoint.serverPubKey = pubkey; int port = reader.ReadInt32(); long timestamp = reader.ReadInt64(); int sigLen = reader.ReadInt32(); byte[] signature = reader.ReadBytes(sigLen); // Check the testnet designator and disconnect on mismatch if (test_net != CoreConfig.isTestNet) { Logging.warn(string.Format("Rejected node {0} due to incorrect testnet designator: {1}", endpoint.fullAddress, test_net)); sendBye(endpoint, ProtocolByeCode.incorrectNetworkType, string.Format("Incorrect testnet designator: {0}. Should be {1}", test_net, CoreConfig.isTestNet), test_net.ToString(), true); return(false); } // Check the address and pubkey and disconnect on mismatch if (!addr.SequenceEqual((new Address(pubkey)).address)) { Logging.warn(string.Format("Pubkey and address do not match.")); sendBye(endpoint, ProtocolByeCode.authFailed, "Pubkey and address do not match.", "", true); return(false); } endpoint.incomingPort = port; // Verify the signature if (node_type == 'C') { // TODO: verify if the client is connectable, then if connectable, check if signature verifies /*if (CryptoManager.lib.verifySignature(Encoding.UTF8.GetBytes(ConsensusConfig.ixianChecksumLockString + "-" + device_id + "-" + timestamp + "-" + endpoint.getFullAddress(true)), pubkey, signature) == false) * { * CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.incorrectIp, "Verify signature failed in hello message, likely an incorrect IP was specified. Detected IP:", endpoint.address); * Logging.warn(string.Format("Connected node used an incorrect signature in hello message, likely an incorrect IP was specified. Detected IP: {0}", endpoint.address)); * return false; * }*/ // TODO store the full address if connectable // Store the presence address for this remote endpoint endpoint.presenceAddress = new PresenceAddress(device_id, "", node_type, node_version, Clock.getNetworkTimestamp() - CoreConfig.clientKeepAliveInterval, null); } else { if (!CryptoManager.lib.verifySignature(Encoding.UTF8.GetBytes(ConsensusConfig.ixianChecksumLockString + "-" + device_id + "-" + timestamp + "-" + endpoint.getFullAddress(true)), pubkey, signature)) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.incorrectIp, "Verify signature failed in hello message, likely an incorrect IP was specified. Detected IP:", endpoint.address); Logging.warn(string.Format("Connected node used an incorrect signature in hello message, likely an incorrect IP was specified. Detected IP: {0}", endpoint.address)); return(false); } // Store the presence address for this remote endpoint endpoint.presenceAddress = new PresenceAddress(device_id, endpoint.getFullAddress(true), node_type, node_version, Clock.getNetworkTimestamp() - CoreConfig.serverKeepAliveInterval, null); } // if we're a client update the network time difference if (endpoint.GetType() == typeof(NetworkClient)) { long timeDiff = endpoint.calculateTimeDifference(); // amortize +- 2 seconds if (timeDiff >= -2 && timeDiff <= 2) { timeDiff = 0; } ((NetworkClient)endpoint).timeDifference = timeDiff; // Check the address and local address and disconnect on mismatch if (endpoint.serverWalletAddress != null && !addr.SequenceEqual(endpoint.serverWalletAddress)) { Logging.warn(string.Format("Local address mismatch, possible Man-in-the-middle attack.")); sendBye(endpoint, ProtocolByeCode.addressMismatch, "Local address mismatch.", "", true); return(false); } } // Create a temporary presence with the client's address and device id Presence presence = new Presence(addr, pubkey, null, endpoint.presenceAddress); if (endpoint.GetType() != typeof(NetworkClient)) { // we're the server if (node_type == 'M' || node_type == 'H' || node_type == 'R') { if (node_type != 'R') { // Check the wallet balance for the minimum amount of coins IxiNumber balance = IxianHandler.getWalletBalance(addr); if (balance < ConsensusConfig.minimumMasterNodeFunds) { Logging.warn(string.Format("Rejected master node {0} due to insufficient funds: {1}", endpoint.getFullAddress(), balance.ToString())); sendBye(endpoint, ProtocolByeCode.insufficientFunds, string.Format("Insufficient funds. Minimum is {0}", ConsensusConfig.minimumMasterNodeFunds), balance.ToString(), true); return(false); } } // Limit to one IP per masternode // TODO TODO TODO - think about this and do it properly /*string[] hostname_split = hostname.Split(':'); * if (PresenceList.containsIP(hostname_split[0], 'M')) * { * using (MemoryStream m2 = new MemoryStream()) * { * using (BinaryWriter writer = new BinaryWriter(m2)) * { * writer.Write(string.Format("This IP address ( {0} ) already has a masternode connected.", hostname_split[0])); * Logging.info(string.Format("Rejected master node {0} due to duplicate IP address", hostname)); * socket.Send(prepareProtocolMessage(ProtocolMessageCode.bye, m2.ToArray()), SocketFlags.None); * socket.Disconnect(true); * return; * } * } * }*/ if (!checkNodeConnectivity(endpoint)) { return(false); } } } // Retrieve the final presence entry from the list (or create a fresh one) endpoint.presence = PresenceList.updateEntry(presence); } catch (Exception e) { // Disconnect the node in case of any reading errors Logging.warn(string.Format("Exception occured in Hello Message {0}", e.ToString())); sendBye(endpoint, ProtocolByeCode.deprecated, "Something went wrong during hello, make sure you're running the latest version of Ixian DLT.", ""); return(false); } if (NetworkClientManager.getConnectedClients().Count() == 1) { PresenceList.forceSendKeepAlive = true; } return(true); }
/// <summary> /// Reads a protocol message from the specified byte-field and calls appropriate methods to process this message. /// </summary> /// <remarks> /// This function checks all applicable checksums and validates that the message is complete before calling one of the specialized /// methods to handle actual decoding and processing. /// </remarks> /// <param name="recv_buffer">Byte-field with an Ixian protocol message.</param> /// <param name="endpoint">Remote endpoint from where the message was received.</param> public static void readProtocolMessage(byte[] recv_buffer, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. readProtocolMessage"); return; } ProtocolMessageCode code = ProtocolMessageCode.hello; byte[] data = null; using (MemoryStream m = new MemoryStream(recv_buffer)) { using (BinaryReader reader = new BinaryReader(m)) { // Check for multi-message packets. One packet can contain multiple network messages. while (reader.BaseStream.Position < reader.BaseStream.Length) { byte[] data_checksum; try { byte startByte = reader.ReadByte(); int message_code = reader.ReadInt32(); code = (ProtocolMessageCode)message_code; int data_length = reader.ReadInt32(); // If this is a connected client, filter messages if (endpoint.GetType() == typeof(RemoteEndpoint)) { if (endpoint.presence == null) { // Check for presence and only accept hello and syncPL messages if there is no presence. if (code == ProtocolMessageCode.hello || code == ProtocolMessageCode.getPresenceList || code == ProtocolMessageCode.getBalance || code == ProtocolMessageCode.newTransaction) { } else { // Ignore anything else return; } } } data_checksum = reader.ReadBytes(32); // sha512qu, 32 bytes byte header_checksum = reader.ReadByte(); byte endByte = reader.ReadByte(); data = reader.ReadBytes(data_length); } catch (Exception e) { Logging.error(String.Format("NET: dropped packet. {0}", e)); return; } // Compute checksum of received data byte[] local_checksum = Crypto.sha512sqTrunc(data, 0, 0, 32); // Verify the checksum before proceeding if (local_checksum.SequenceEqual(data_checksum) == false) { Logging.error("Dropped message (invalid checksum)"); continue; } // Can proceed to parse the data parameter based on the protocol message code. // Data can contain multiple elements. //parseProtocolMessage(code, data, socket, endpoint); NetworkQueue.receiveProtocolMessage(code, data, data_checksum, endpoint); } } } }