private static void acceptConnection(Socket clientSocket) { IPEndPoint clientEndpoint = (IPEndPoint)clientSocket.RemoteEndPoint; // Add timeouts and set socket options //clientSocket.ReceiveTimeout = 5000; //clientSocket.SendTimeout = 5000; clientSocket.LingerState = new LingerOption(true, 3); clientSocket.NoDelay = true; clientSocket.Blocking = true; if (!IxianHandler.isAcceptingConnections()) { Thread.Sleep(100); // wait a bit for check connectivity purposes clientSocket.Send(CoreProtocolMessage.prepareProtocolMessage(ProtocolMessageCode.bye, new byte[1])); clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Disconnect(true); return; } lastIncomingConnectionTime = DateTime.UtcNow; connectable = true; // Setup the remote endpoint RemoteEndpoint remoteEndpoint = new RemoteEndpoint(); lock (connectedClients) { if (connectedClients.Count + 1 > CoreConfig.maximumServerMasterNodes) { Logging.warn(string.Format("Maximum number of connected clients reached. Disconnecting client: {0}:{1}", clientEndpoint.Address.ToString(), clientEndpoint.Port)); clientSocket.Send(CoreProtocolMessage.prepareProtocolMessage(ProtocolMessageCode.bye, new byte[1])); clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Disconnect(true); return; } var existing_clients = connectedClients.Where(re => re.remoteIP.Address == clientEndpoint.Address); if (existing_clients.Count() > 0) { Logging.warn(String.Format("Client {0}:{1} already connected as {2}.", clientEndpoint.Address.ToString(), clientEndpoint.Port, existing_clients.First().ToString())); clientSocket.Send(CoreProtocolMessage.prepareProtocolMessage(ProtocolMessageCode.bye, new byte[1])); clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Disconnect(true); return; } connectedClients.Add(remoteEndpoint); Logging.info(String.Format("Client connection accepted: {0} | #{1}/{2}", clientEndpoint.ToString(), connectedClients.Count + 1, CoreConfig.maximumServerMasterNodes)); remoteEndpoint.start(clientSocket); } }
/// <summary> /// Attempts to connect to the given host name or IP address and transmit some data. /// Note: This function has a possible delay of about 2 seconds. /// </summary> /// <param name="full_hostname">Hostname or IP address of the remote endpoint.</param> /// <returns>True, if the IP address is reachable.</returns> public static bool PingAddressReachable(String full_hostname) { // TODO TODO TODO TODO move this to another thread if (String.IsNullOrWhiteSpace(full_hostname)) { return(false); } String[] hn_port = full_hostname.Split(':'); if (hn_port.Length != 2) { return(false); } String hostname = hn_port[0]; if (!IXICore.Utils.IxiUtils.validateIPv4(hostname)) { return(false); } int port; if (int.TryParse(hn_port[1], out port) == false) { return(false); } if (port <= 0) { return(false); } TcpClient temp = new TcpClient(); bool connected = false; try { Logging.info(String.Format("Testing client connectivity for {0}.", full_hostname)); if (!temp.ConnectAsync(hostname, port).Wait(1000)) { return(false); } temp.Client.SendTimeout = 500; temp.Client.ReceiveTimeout = 500; temp.Client.Blocking = false; temp.Client.Send(new byte[1], 0, 0); connected = temp.Client.Connected; temp.Client.Send(CoreProtocolMessage.prepareProtocolMessage(ProtocolMessageCode.bye, new byte[1])); temp.Client.Shutdown(SocketShutdown.Both); temp.Close(); } catch (SocketException) { connected = false; } return(connected); }
// Sends data over the network public static void sendData(ProtocolMessageCode code, byte[] data) { byte[] ba = CoreProtocolMessage.prepareProtocolMessage(code, data); NetDump.Instance.appendSent(tcpClient.Client, ba, ba.Length); try { tcpClient.Client.Send(ba, SocketFlags.None); if (tcpClient.Client.Connected == false) { Logging.error("Failed senddata to client. Reconnecting."); } } catch (Exception e) { Logging.error(String.Format("CLN: Socket exception, attempting to reconnect {0}", e)); } //Console.WriteLine("sendData done"); }
// Internal function that sends data through the socket protected void sendDataInternal(ProtocolMessageCode code, byte[] data, byte[] checksum) { byte[] ba = CoreProtocolMessage.prepareProtocolMessage(code, data, checksum); NetDump.Instance.appendSent(clientSocket, ba, ba.Length); try { for (int sentBytes = 0; sentBytes < ba.Length && running;) { int bytesToSendCount = ba.Length - sentBytes; if (bytesToSendCount > 8000) { bytesToSendCount = 8000; } int curSentBytes = clientSocket.Send(ba, sentBytes, bytesToSendCount, SocketFlags.None); lastDataSentTime = Clock.getTimestamp(); // Sleep a bit to allow other threads to do their thing Thread.Yield(); sentBytes += curSentBytes; // TODO TODO TODO timeout } if (clientSocket.Connected == false) { if (running) { Logging.warn(String.Format("sendRE: Failed senddata to remote endpoint {0}, Closing.", getFullAddress())); } state = RemoteEndpointState.Closed; } } catch (Exception e) { if (running) { Logging.warn(String.Format("sendRE: Socket exception for {0}, closing. {1}", getFullAddress(), e)); } state = RemoteEndpointState.Closed; } }
// Unified protocol message parsing public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. parseProtocolMessage"); return; } try { switch (code) { case ProtocolMessageCode.hello: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessage(endpoint, reader)) { byte[] challenge_response = null; try { // TODO TODO TODO TODO TODO try/catch wrapper will be removed when everybody upgrades int challenge_len = reader.ReadInt32(); byte[] challenge = reader.ReadBytes(challenge_len); challenge_response = CryptoManager.lib.getSignature(challenge, Node.walletStorage.getPrimaryPrivateKey()); } catch (Exception e) { } CoreProtocolMessage.sendHelloMessage(endpoint, true, challenge_response); endpoint.helloReceived = true; return; } } } break; case ProtocolMessageCode.helloData: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessage(endpoint, reader)) { char node_type = endpoint.presenceAddress.type; if (node_type != 'M' && node_type != 'H') { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.expectingMaster, string.Format("Expecting master node."), "", true); return; } ulong last_block_num = reader.ReadUInt64(); int bcLen = reader.ReadInt32(); byte[] block_checksum = reader.ReadBytes(bcLen); int wsLen = reader.ReadInt32(); byte[] walletstate_checksum = reader.ReadBytes(wsLen); int consensus = reader.ReadInt32(); endpoint.blockHeight = last_block_num; int block_version = reader.ReadInt32(); Node.setLastBlock(last_block_num, block_checksum, walletstate_checksum, block_version); Node.setRequiredConsensus(consensus); // Check for legacy level ulong legacy_level = reader.ReadUInt64(); // Check for legacy node if (Legacy.isLegacy(legacy_level)) { // TODO TODO TODO TODO check this out //endpoint.setLegacy(true); } int challenge_response_len = reader.ReadInt32(); byte[] challenge_response = reader.ReadBytes(challenge_response_len); if (!CryptoManager.lib.verifySignature(endpoint.challenge, endpoint.serverPubKey, challenge_response)) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.authFailed, string.Format("Invalid challenge response."), "", true); return; } // Process the hello data endpoint.helloReceived = true; NetworkClientManager.recalculateLocalTimeDifference(); } } } break; case ProtocolMessageCode.s2data: { StreamProcessor.receiveData(data, endpoint); } break; case ProtocolMessageCode.s2failed: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { Logging.error("Failed to send s2 data"); } } } break; case ProtocolMessageCode.s2signature: { StreamProcessor.receivedTransactionSignature(data, endpoint); } break; case ProtocolMessageCode.newTransaction: { // Forward the new transaction message to the DLT network CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.newTransaction, data, null); } break; case ProtocolMessageCode.syncPresenceList: { byte[] pdata = PresenceList.getBytes(); byte[] ba = CoreProtocolMessage.prepareProtocolMessage(ProtocolMessageCode.presenceList, pdata); endpoint.sendData(ProtocolMessageCode.presenceList, pdata); } break; case ProtocolMessageCode.presenceList: { Logging.info("Receiving complete presence list"); PresenceList.syncFromBytes(data); } break; case ProtocolMessageCode.updatePresence: { // Parse the data and update entries in the presence list PresenceList.updateFromBytes(data); } break; case ProtocolMessageCode.keepAlivePresence: { byte[] address = null; bool updated = PresenceList.receiveKeepAlive(data, out address); // If a presence entry was updated, broadcast this message again if (updated) { CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'R', 'H', 'W' }, ProtocolMessageCode.keepAlivePresence, data, address, endpoint); // Send this keepalive message to all connected clients CoreProtocolMessage.broadcastEventDataMessage(NetworkEvents.Type.keepAlive, address, ProtocolMessageCode.keepAlivePresence, data, address, endpoint); } } break; case ProtocolMessageCode.getPresence: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int walletLen = reader.ReadInt32(); byte[] wallet = reader.ReadBytes(walletLen); lock (PresenceList.presences) { // TODO re-verify this Presence p = PresenceList.presences.Find(x => x.wallet.SequenceEqual(wallet)); if (p != null) { byte[][] presence_chunks = p.getByteChunks(); int i = 0; foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk); i++; } } else { // TODO blacklisting point Logging.warn(string.Format("Node has requested presence information about {0} that is not in our PL.", Base58Check.Base58CheckEncoding.EncodePlain(wallet))); } } } } } break; case ProtocolMessageCode.balance: { // TODO: make sure this is received from a DLT node only. using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = reader.ReadInt32(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = reader.ReadString(); if (address.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { Node.balance = balance; } // Retrieve the blockheight for the balance ulong blockheight = reader.ReadUInt64(); Node.blockHeight = blockheight; } } } break; case ProtocolMessageCode.bye: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { endpoint.stop(); bool byeV1 = false; try { ProtocolByeCode byeCode = (ProtocolByeCode)reader.ReadInt32(); string byeMessage = reader.ReadString(); string byeData = reader.ReadString(); byeV1 = true; switch (byeCode) { case ProtocolByeCode.bye: // all good break; case ProtocolByeCode.forked: // forked node disconnected Logging.info(string.Format("Disconnected with message: {0} {1}", byeMessage, byeData)); break; case ProtocolByeCode.deprecated: // deprecated node disconnected Logging.info(string.Format("Disconnected with message: {0} {1}", byeMessage, byeData)); break; case ProtocolByeCode.incorrectIp: // incorrect IP if (IxiUtils.validateIPv4(byeData)) { if (NetworkClientManager.getConnectedClients().Length < 2) { Config.publicServerIP = byeData; Logging.info("Changed internal IP Address to " + byeData + ", reconnecting"); } } break; case ProtocolByeCode.notConnectable: // not connectable from the internet Logging.error("This node must be connectable from the internet, to connect to the network."); Logging.error("Please setup uPNP and/or port forwarding on your router for port " + Config.serverPort + "."); NetworkServer.connectable = false; break; case ProtocolByeCode.insufficientFunds: break; default: Logging.warn(string.Format("Disconnected with message: {0} {1}", byeMessage, byeData)); break; } } catch (Exception) { } if (byeV1) { return; } reader.BaseStream.Seek(0, SeekOrigin.Begin); // Retrieve the message string message = reader.ReadString(); if (message.Length > 0) { Logging.info(string.Format("Disconnected with message: {0}", message)); } else { Logging.info("Disconnected"); } } } } break; case ProtocolMessageCode.extend: { if (Config.isTestClient) { TestClientNode.handleExtendProtocol(data); } } break; default: break; } } catch (Exception e) { Logging.error(string.Format("Error parsing network message. Details: {0}", e.ToString())); } }