// Update a presence entry. If the wallet address is not found, it creates a new entry in the Presence List. // If the wallet address is found in the presence list, it adds any new addresses from the specified presence. public static Presence updateEntry(Presence presence, bool return_presence_only_if_updated = false) { if (presence == null) { return(null); } bool updated = false; long currentTime = Clock.getNetworkTimestamp(); lock (presences) { Presence pr = presences.Find(x => x.wallet.SequenceEqual(presence.wallet)); if (pr != null) { lock (pr) { // Go through all addresses and add any missing ones foreach (PresenceAddress local_addr in presence.addresses) { long lTimestamp = local_addr.lastSeenTime; int expiration_time = CoreConfig.serverPresenceExpiration; if (local_addr.type == 'C') { expiration_time = CoreConfig.clientPresenceExpiration; } // Check for tampering. Includes a +300, -30 second synchronization zone if ((currentTime - lTimestamp) > expiration_time) { Logging.warn(string.Format("[PL] Received expired presence for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(pr.wallet), local_addr.address, currentTime, lTimestamp)); continue; } if ((currentTime - lTimestamp) < -30) { Logging.warn(string.Format("[PL] Potential presence tampering for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(pr.wallet), local_addr.address, currentTime, lTimestamp)); continue; } PresenceAddress addr = pr.addresses.Find(x => x.device == local_addr.device); if (addr != null) { if (addr.lastSeenTime < local_addr.lastSeenTime) { if (local_addr.signature != null) { addr.version = local_addr.version; addr.address = local_addr.address; addr.lastSeenTime = local_addr.lastSeenTime; addr.signature = local_addr.signature; updated = true; } if (addr.type == 'M' || addr.type == 'H') { PeerStorage.addPeerToPeerList(addr.address, presence.wallet); } //Console.WriteLine("[PL] Last time updated for {0}", addr.device); } } else { // Add the address if it's not found //Logging.info("[PL] Adding new address for {0}", presence.wallet); pr.addresses.Add(local_addr); if (local_addr.type == 'M' || local_addr.type == 'H') { PeerStorage.addPeerToPeerList(local_addr.address, presence.wallet); } lock (presenceCount) { if (!presenceCount.ContainsKey(local_addr.type)) { presenceCount.Add(local_addr.type, 0); } presenceCount[local_addr.type]++; } updated = true; } } if (!updated && return_presence_only_if_updated) { return(null); } else { return(pr); } } } else { // Insert a new entry //Console.WriteLine("[PL] Adding new entry for {0}", presence.wallet); foreach (PresenceAddress pa in presence.addresses) { if (pa.type == 'M' || pa.type == 'H') { PeerStorage.addPeerToPeerList(pa.address, presence.wallet); } lock (presenceCount) { if (!presenceCount.ContainsKey(pa.type)) { presenceCount.Add(pa.type, 0); } presenceCount[pa.type]++; } updated = true; } if (updated) { presences.Add(presence); } if (!updated && return_presence_only_if_updated) { return(null); } else { return(presence); } } } }
/// <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 processHelloMessageV6(RemoteEndpoint endpoint, BinaryReader reader, bool set_hello_received = true) { // 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 = (int)reader.ReadIxiVarUInt(); endpoint.version = protocol_version; Logging.info("Received Hello: Node version {0}", protocol_version); // Check for incompatible nodes if (protocol_version < 6) { Logging.warn("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 = (int)reader.ReadIxiVarUInt(); byte[] addr = reader.ReadBytes(addrLen); if (addrLen > 70) { Logging.error("Hello: Invalid address from {0} - {1}.", endpoint.getFullAddress(true), Base58Check.Base58CheckEncoding.EncodePlain(addr)); sendBye(endpoint, ProtocolByeCode.rejected, "Invalid address", "", true); return(false); } bool test_net = reader.ReadBoolean(); char node_type = reader.ReadChar(); string node_version = reader.ReadString(); if (node_version.Length > 20) { Logging.error("Hello: Invalid node version from {0} - {1}.", endpoint.getFullAddress(true), node_version); sendBye(endpoint, ProtocolByeCode.rejected, "Invalid Node Version", "", true); return(false); } int device_id_len = (int)reader.ReadIxiVarUInt(); byte[] device_id = reader.ReadBytes(device_id_len); if (device_id_len > 32) { Logging.error("Hello: Invalid device id from {0} - {1}.", endpoint.getFullAddress(true), Crypto.hashToString(device_id)); sendBye(endpoint, ProtocolByeCode.rejected, "Invalid Device ID", "", true); return(false); } int pkLen = (int)reader.ReadIxiVarUInt(); byte[] pubkey = null; if (pkLen > 0) { pubkey = reader.ReadBytes(pkLen); } endpoint.serverPubKey = pubkey; int port = (int)reader.ReadIxiVarInt(); long timestamp = reader.ReadIxiVarInt(); int sigLen = (int)reader.ReadIxiVarUInt(); byte[] signature = reader.ReadBytes(sigLen); int challenge = 0; bool in_hello = false; if (endpoint.GetType() != typeof(NetworkClient)) { challenge = (int)reader.ReadIxiVarUInt(); in_hello = true; } // Check the testnet designator and disconnect on mismatch if (test_net != IxianHandler.isTestNet) { Logging.warn("Hello: 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, IxianHandler.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("Hello: Pubkey and address do not match."); sendBye(endpoint, ProtocolByeCode.authFailed, "Pubkey and address do not match.", "", true); return(false); } endpoint.incomingPort = port; if (PeerStorage.isBlacklisted(addr) || PeerStorage.isBlacklisted(endpoint.getFullAddress(true))) { Logging.warn("Hello: Connected node is blacklisted ({0} - {1}).", endpoint.getFullAddress(true), Base58Check.Base58CheckEncoding.EncodePlain(addr)); sendBye(endpoint, ProtocolByeCode.rejected, "Blacklisted", "", true); return(false); } // 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 { using (MemoryStream mSig = new MemoryStream(1024)) { using (BinaryWriter sigWriter = new BinaryWriter(mSig)) { sigWriter.Write(ConsensusConfig.ixianChecksumLock); sigWriter.Write(device_id); sigWriter.Write(timestamp); sigWriter.Write(endpoint.getFullAddress(true)); if (in_hello) { sigWriter.Write(challenge); } else { sigWriter.Write(endpoint.challenge); } } if (!CryptoManager.lib.verifySignature(mSig.ToArray(), pubkey, signature)) { sendBye(endpoint, ProtocolByeCode.incorrectIp, "Verify signature failed in hello message, likely an incorrect IP was specified. Detected IP:", endpoint.address); Logging.warn("Hello: 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("Hello: Local address mismatch, possible Man-in-the-middle attack."); sendBye(endpoint, ProtocolByeCode.addressMismatch, "Local address mismatch.", "", true); return(false); } PeerStorage.updateLastConnected(endpoint.getFullAddress(true)); } // 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("Hello: 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); } } sendHelloMessageV6(endpoint, true, challenge); if (set_hello_received) { endpoint.helloReceived = true; } } // 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("Hello: 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); }