Пример #1
0
 /// <summary>
 ///     Makes a copy of this encoder.
 /// </summary>
 /// <returns>Copy of encoder.</returns>
 public StreamEncryptor Clone()
 {
     StreamEncryptor e = new StreamEncryptor(m_key);
     e.m_encryptKeyOffset = m_encryptKeyOffset;
     e.m_decryptKeyOffset = m_decryptKeyOffset;
     return e;
 }
Пример #2
0
        /// <summary>
        ///     Mangles the given buffer of data.
        /// </summary>
        /// <param name="data">data to mangle.</param>       
        /// <param name="cryptor">The encryption class to encrypt the buffer with.</param>
        /// <param name="compressor">Compressor used to compress the packet.</param>
        /// <param name="encoder">Encoder used for delta-encoding of packets.</param>
        private byte[] MangleData(byte[] data,  StreamDeltaEncoder encoder, StreamCompressor compressor, StreamEncryptor cryptor)
        {
            // Delta encoder data.
            if (encoder != null)
            {
                encoder.EncodeInPlace(data, GetType().FullName.GetHashCode());
            }

            // Compress payload.
            if (compressor != null)
            {
                data = compressor.Compress(data);
            }

            // Encrypt output.
            if (cryptor != null)
            {
                cryptor.EncryptInPlace(data);
            }

            return data;
        }
Пример #3
0
        /*
        static double _noneTime = 0.0f;
        static int _noneSize = 0;

        static int _encodeSize = 0;
        static double _encodeTime = 0.0f;
        static int  _encodeCompressSize = 0;
        static double _encodeCompressTime = 0.0f;
        static int  _encodeCompressEncryptSize = 0;
        static double _encodeCompressEncryptTime = 0.0f;
          static int   _compressSize = 0;
        static double _compressTime = 0.0f;
           static int  _compressEncryptSize = 0;
        static double _compressEncryptTime = 0.0f;
          static int   _encryptSize = 0;
        static double _encryptTime = 0.0f;
        static int _encryptEncodeSize = 0;
        static double _encryptEncodeTime = 0.0f;

        static int _packetCounter = 0;
        */
        /// <summary>
        ///     Serializes this packet to a byte buffer than can be recreated by calling FromBuffer.
        /// </summary>
        /// <param name="inReplyTo">If not null this packet is marked as a reply to another packet.</param>
        /// <param name="cryptor">The encryption class to encrypt the buffer with.</param>
        /// <param name="compressor">Compressor used to compress the packet.</param>
        /// <param name="encoder">Encoder used for delta-encoding of packets.</param>
        /// <returns>Byte buffer conversion of packet.</returns>
        public byte[] ToBuffer(Packet inReplyTo, StreamEncryptor cryptor, StreamCompressor compressor, StreamDeltaEncoder encoder)
        {
            byte[] data = CreatePayload();

            // Data cannot be larger than a ushort.
            if (data.Length >= ushort.MaxValue)
            {
                throw new InvalidDataException("Packets cannot be larger than a ushort's max value.");
            }

            // Work out checksum of unmodified payload.
            PearsonHash hash = new PearsonHash();
            hash.AddBuffer(data, 0, data.Length);
            byte dataChecksum = hash.Calculate();

            // Mangle our data based ready for transport.
            data = MangleData(data, encoder, compressor, cryptor);

            // DEBUG TIMING
            /*
            HighPerformanceTimer timer = new HighPerformanceTimer();

            StreamEncryptor tmp_cryptor = cryptor == null ? null : cryptor.Clone();
            StreamCompressor tmp_compressor = new StreamCompressor();
            StreamDeltaEncoder tmp_encoder = encoder == null ? null : encoder.Clone();

            byte[] tmp = CreatePayload();
            timer.Start();
            _noneSize += MangleData(tmp, null, null, null).Length;
            _noneTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _encodeSize += MangleData(tmp, tmp_encoder, null, null).Length;
            _encodeTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _encodeCompressSize += MangleData(tmp, tmp_encoder, tmp_compressor, null).Length;
            _encodeCompressTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _encodeCompressEncryptSize += MangleData(tmp, tmp_encoder, tmp_compressor, tmp_cryptor).Length;
            _encodeCompressEncryptTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _compressSize += MangleData(tmp, null, tmp_compressor, null).Length;
            _compressTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _compressEncryptSize += MangleData(tmp, null, tmp_compressor, tmp_cryptor).Length;
            _compressEncryptTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _encryptSize += MangleData(tmp, null, null, tmp_cryptor).Length;
            _encryptTime += timer.Stop();

            tmp = CreatePayload();
            timer.Start();
            _encryptEncodeSize += MangleData(tmp, tmp_encoder, null, tmp_cryptor).Length;
            _encryptEncodeTime += timer.Stop();

            _packetCounter++;
            if (_packetCounter >= 100000)
            {
                System.Console.WriteLine("DONE!");
            }
             * */
            // DEBUG TIMING

            // Write in data checksum.
            byte[] buffer = new byte[HEADER_SIZE + data.Length];
            buffer[0] = dataChecksum;

            // Write in payload size.
            byte[] bytes = BitConverter.GetBytes((ushort)data.Length);
            buffer[1] = bytes[0];
            buffer[2] = bytes[1];

            // Write in class hash.
            bytes = BitConverter.GetBytes(this.GetType().FullName.GetHashCode());
            buffer[3] = bytes[0];
            buffer[4] = bytes[1];
            buffer[5] = bytes[2];
            buffer[6] = bytes[3];

            // Write in a reply-to-packet-id.
            bytes = BitConverter.GetBytes(inReplyTo == null ? 0 : inReplyTo.PacketID);
            buffer[7] = bytes[0];
            buffer[8] = bytes[1];

            // Write in payload.
            for (int i = 0; i < data.Length; i++)
            {
                buffer[HEADER_SIZE + i] = data[i];
            }

            return buffer;
        }
Пример #4
0
        /// <summary>
        ///     Finishes the job that FromHeader starts, decrypts and parses the packet payload.
        ///     
        ///     Be aware that for the sake of speed, decryption of the data will be done
        ///     in-place - the value of buffer will be in its decrypted form when this
        ///     function returns!
        /// </summary>
        /// <returns>True if successful, or false if payload is invalid (and thus packet is too).</returns>
        /// <param name="cryptor">The encryption class to decrypt the buffer with.</param>
        /// <param name="compressor">Compressor used to decompress the packet.</param>
        /// <param name="encoder">Encoder used for delta-deencoding of packets.</param>
        /// <param name="buffer">Buffer to construct packet from.</param>
        public bool RecievePayload(byte[] buffer, StreamEncryptor cryptor, StreamCompressor compressor, StreamDeltaEncoder encoder)
        {
            try
            {
                // Decrypt the payload.
                cryptor.DecryptInPlace(buffer);

                // Decompress payload.
                buffer = compressor.Decompress(buffer);

                // Delta un-encode.
                encoder.DecodeInPlace(buffer, GetType().FullName.GetHashCode());
            }
            catch (Exception)
            {
                Logger.Error("Recieved packet with invalid data. Dropping packet.", LoggerVerboseLevel.Normal);
                return false;
            }

            // Check that the checksum is correct.
            PearsonHash hash = new PearsonHash();
            hash.AddBuffer(buffer, 0, buffer.Length);
            int bufferChecksum = hash.Calculate();

            if (bufferChecksum != m_checksum)
            {
                Logger.Error("Recieved packet with invalid checksum, got {0}, expected {1}. Dropping packet.", LoggerVerboseLevel.Normal, bufferChecksum, m_checksum);
                return false;
            }

            // Parse the payload!
            return ParsePayload(buffer);
        }
Пример #5
0
        /// <summary>
        ///     Constructs a packet from the given packet header.
        ///     RecievePayload should be called on the packet instance after data is recieved.
        ///     
        ///     Be aware that for the sake of speed, decryption of the header will be done
        ///     in-place - the value of buffer will be in its decrypted form when this
        ///     function returns!
        /// </summary>
        /// <returns>Skeleton packet instance holding header information. Or null if header is invalid.</returns>
        /// <param name="cryptor">The encryption class to decrypt the buffer with.</param>
        /// <param name="compressor">Compressor used to decompress the packet.</param>
        /// <param name="encoder">Encoder used for delta-deencoding of packets.</param>
        /// <param name="buffer">Buffer to construct packet from.</param>
        public static Packet FromHeader(byte[] buffer, StreamEncryptor cryptor, StreamCompressor compressor, StreamDeltaEncoder encoder)
        {
            // Make sure we have been passed a header, and not some other kind of buffer.
            if (buffer.Length != HEADER_SIZE)
            {
                Logger.Error("Recieved packet with invalid header size, got {0} bytes, expected {1}. Dropping packet.", LoggerVerboseLevel.Normal, buffer.Length, HEADER_SIZE);
                return null;
            }

            // Read header.
            byte checksum       = buffer[0];
            int payloadSize     = (int)BitConverter.ToUInt16(buffer, 1);
            int classHash       = (int)BitConverter.ToInt32(buffer, 3);
            int replyToPacketID = (int)BitConverter.ToUInt16(buffer, 7);

            // Verify class hash is correct.
            Type classType = null;
            lock (m_hashToTypeTable_lock)
            {
                if (m_hashToTypeTable.ContainsKey(classHash) == true)
                {
                    classType = m_hashToTypeTable[classHash];
                }
                else
                {
                    var subclasses = typeof(Packet).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Packet)));
                    foreach (Type type in subclasses)
                    {
                        int code = type.FullName.GetHashCode();
                        if (code == classHash)
                        {
                            m_hashToTypeTable.Add(code, type);
                            classType = type;
                            break;
                        }
                    }
                }
            }

            // No class available for hash?
            if (classType == null)
            {
                Logger.Error("Recieved packet with invalid class hash '{0}'. Dropping packet.", LoggerVerboseLevel.Normal, classHash);
                return null;
            }

            // Load the packet.
            Packet packet = null;
            try
            {
                packet = (Packet)Activator.CreateInstance(classType);
                packet.m_payloadSize        = payloadSize;
                packet.m_checksum           = checksum;
                packet.m_replyToPacketID    = replyToPacketID;
            }
            catch (Exception)
            {
                Logger.Error("Failed to create packet of type '{0}'. Dropping packet.", LoggerVerboseLevel.Normal, classType.Name);
                return null;
            }

            return packet;
        }
Пример #6
0
        /// <summary>
        ///     Disposes of the socket if it exists.
        /// </summary>
        /// <param name="byError">If this disconnect was caused by an error, then we will try and reconnect.</param>
        /// <param name="timeout">Amount of time in milliseconds to wait until socket is closed. 0 closes socket instantly.</param>
        /// <param name="reconnectDelay">Used to determine at what point we reconnect.</param>
        private void DisposeSocket(bool byError, int timeout = 1000)
        {
            // Disconnect from the socket.
            lock (m_socket_lock)
            {
                if (m_socket != null)
                {
                    try
                    {
                        if (m_socket.Connected == true)
                        {
                            m_socket.Disconnect(false);
                        }
                        m_socket.Close(timeout);
                        m_socket = null;
                    }
                    catch (SocketException)
                    {
                        Logger.Warning("Recieved socket exception whilst disposing of old socket.", LoggerVerboseLevel.Highest);
                    }
                    catch (ObjectDisposedException)
                    {
                        Logger.Warning("Recieved socket exception whilst disposing of old socket.", LoggerVerboseLevel.Highest);
                    }
                    catch (NullReferenceException)
                    {
                        Logger.Warning("Recieved socket exception whilst disposing of old socket.", LoggerVerboseLevel.Highest);
                    }
                }
            }

            // Disconnect all peers.
            lock (m_peers_lock)
            {
                try
                {
                    foreach (Connection connection in m_peers)
                    {
                        connection.DisposeSocket(false, timeout); // BUG: Lock loop, this calls DisposeSocket, which in turn calls listen_socket (this connection) .PeerDisconnect, which in turn calls this function!
                        m_disconnected_peers.Add(connection);
                    }
                    m_peers.Clear();
                }
                catch (InvalidOperationException ex)
                {
                    // Hack: This needs fixing, its just to prevent a bug with the lock loop that causes 
                    //       a "collection modified" error.
                }
            }

            // Reset state.
            lock (m_socket_lock)
            {
                m_connected = false;
                m_connecting = false;
                m_listening = false;
                m_readingMessagePayload = false;

                m_hardwareFingerprint = new byte[0];
                m_computerName = "";
                m_computerUserName = "";
                m_connectionEstablished = false;

                m_sentBytes = 0;
                m_recievedBytes = 0;
                m_queuedSendBytes = 0;
                m_sendPacketIDCounter = 0;
                m_recvPacketIDCounter = 0;

                m_timeSinceLastPing = 0;
                m_timeSinceLastPong = 0;
                m_waitingForPong = false;
                m_ping = 0;

                m_encryptor = null;
                m_deltaEncoder = null;
                m_compressor = null;
            }

            lock (m_messageQueue_lock)
            {
                m_messageQueue.Clear();
            }

            // If we have a listen socket, remove us from their peer list.
            if (m_listenConnection != null)
            {
                m_listenConnection.PeerDisconnected(this);
            }

            // Clear out the packet wait list.
            lock (m_replyWaitContexts_lock)
            {
                foreach (ConnectionReplyWaitContext context in m_replyWaitContexts)
                {
                    context.Event.Set();
                }
                m_replyWaitContexts.Clear();
            }

            // Reconnect to the server as it appears
            // that we have been disconnected!
            //if (byError == true)
            //{
            //    PerformReconnect();
            //}
        }
Пример #7
0
        /// <summary>
        ///     Sets up this connection as a peer of a listen connection.
        /// </summary>
        private void SetupAsPeer(Connection listenConnection, Socket socket)
        {
            lock (m_socket_lock)
            {
                m_listenConnection = listenConnection;
                m_socket = socket;
                m_connected = true;
                m_endPoint = (System.Net.IPEndPoint)socket.RemoteEndPoint;

                m_encryptor = new StreamEncryptor("");
                m_compressor = new StreamCompressor();
                m_deltaEncoder = new StreamDeltaEncoder();
            }

           // Begin reading from the server.
           StartRead();

           // Start polling connection.
           StartPoll();
        }
Пример #8
0
        /// <summary>
        ///     Processes a packet if its an internal packet.
        /// </summary>
        /// <param name="packet">Packet to process.</param>
        /// <returns>True if the packet was processes, otherwise false.</returns>
        private bool ProcessInternalPacket(Packet packet)
        {
            // Responds to a ping request from a client.
            if (packet is PingPacket)
            {
                PongPacket pong = new PongPacket();
                SendPacket(pong, packet);

                return true;
            }

            // Registers the response from a ping packet we have sent.
            else if (packet is PongPacket)
            {
                m_ping              = Environment.TickCount - m_timeSinceLastPing;
                m_timeSinceLastPong = Environment.TickCount;
                m_waitingForPong    = false;

                return true;
            }

            // Provides connections details regarding their connection.
            else if (packet is ConnectPacket)
            {
                if (m_listenConnection == null)
                {
                    throw new InvalidOperationException("Remote host sent a connect packet to a non-listening connection.");
                }

                ConnectPacket p = (ConnectPacket)packet;

                m_hardwareFingerprint = p.HardwareFingerprint;
                m_guid                = p.ConnectionGUID;
                m_computerUserName    = p.ComputerUserName;
                m_computerName        = p.ComputerName;

                Logger.Info("Connection from {0} established connection.", LoggerVerboseLevel.High, m_endPoint.Serialize().ToString());
                Logger.Info("\tHardware Fingerprint: {0}", LoggerVerboseLevel.High, StringHelper.ByteArrayToHexString(m_hardwareFingerprint));
                Logger.Info("\tGUID: {0}", LoggerVerboseLevel.High, StringHelper.ByteArrayToHexString(m_guid));
                Logger.Info("\tComputer Name: {0}", LoggerVerboseLevel.High, m_computerName);
                Logger.Info("\tComputer User Name: {0}", LoggerVerboseLevel.High, m_computerUserName);

                // Send an AOK message :3.
                ConnectReplyPacket reply = new ConnectReplyPacket();
                SendPacket(reply, packet);

                lock (m_socket_lock)
                {
                    m_connectionEstablished = true;
                    lock (m_listenConnection.m_peers_lock)
                    {
                        m_listenConnection.m_connected_peers.Add(this);
                    }
                    m_encryptor = new StreamEncryptor(GenerateEncryptionKey());
                }

                return true;
            }

            // Finishes establishing a connection.
            else if (packet is ConnectReplyPacket)
            {
                // Generate new encryption keys.
                lock (m_socket_lock)
                {
                    m_encryptor = new StreamEncryptor(GenerateEncryptionKey());
                }

                m_connectionEstablished = true;
            }

            // Disconnects the user from the server gracefully.
            else if (packet is DisconnectPacket)
            {
                DisconnectAsync(true).Wait();
                return true;
            }

            return false;
        }
Пример #9
0
        /// <summary>
        ///     Connects to the service and updates over time.
        /// </summary>
        /// <param name="ip">IP address of the server to connect to.</param>
        /// <param name="port">Port number that service is running on.</param>
        public bool ConnectOverTime(string ip, UInt16 port)
        {
            // Close down old socket.
            DisposeSocket(false);

            m_connecting = true;
            m_connectionEstablished = false;
            m_encryptor = new StreamEncryptor("");
            m_compressor = new StreamCompressor();
            m_deltaEncoder = new StreamDeltaEncoder();

            // Local address?
            if (ip == "127.0.0.1" || ip == "::1" || ip == HardwareHelper.GetLocalIPAddress())
            {
                ip = "localhost";
            }

            // Create new socket!
            System.Net.IPAddress[] hosts = null;
            try
            {
                hosts = Dns.GetHostAddresses(ip);
            }
            catch (System.Net.Sockets.SocketException)
            {
                hosts = null;
            }
            if (hosts == null || hosts.Length == 0)
            {
                Logger.Error("Failed to connect to host - could not find address for {0}.", LoggerVerboseLevel.Normal, ip);
                m_connecting = false;
                return false;
            }

            System.Net.IPAddress host = hosts[0];
            try
            {
                bool result = false;
                SocketAsyncEventArgs args;

                lock (m_socket_lock)
                {
                    m_endPoint = new IPEndPoint(host, port);
                    m_socket = new Socket(host.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    m_socket.SendTimeout = Settings.CONNECTION_SEND_TIMEOUT;
                    m_socket.ReceiveTimeout = Settings.CONNECTION_RECIEVE_TIMEOUT;

                    // Get connecting!
                    args = new SocketAsyncEventArgs();//g_socketAsyncEventArgsPool.NewObject();//new SocketAsyncEventArgs();
                    args.RemoteEndPoint = m_endPoint;
                    args.UserToken = this;
                    args.Completed += SocketConnect_Completed;

                    Logger.Info("Attempting to connect to {0}:{1}.", LoggerVerboseLevel.High, ip, port);

                    result = m_socket.ConnectAsync(args);
                }

                if (result == false)
                {
                    SocketConnect_Completed(this, args);
                }
            }
            catch (SocketException)
            {
                Logger.Error("Encountered socket exception while attempting to connect to host.", LoggerVerboseLevel.Normal);
                DisposeSocket(false);
            }

            // And we are done!
            return m_connected;
        }