Пример #1
0
        /// <summary>
        /// Handles a packet, reading the flags and processing all fragments.
        /// </summary>
        /// <param name="packet">ClientPacket to handle</param>
        private void HandlePacket(ClientPacket packet)
        {
            log.DebugFormat("[{0}] Handling packet {1}", session.Account, packet.Header.Sequence);

            uint issacXor = packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum) ? ConnectionData.IssacClient.GetOffset() : 0;

            if (!packet.VerifyChecksum(issacXor))
            {
                log.WarnFormat("[{0}] Packet {1} has invalid checksum", session.Account, packet.Header.Sequence);
            }

            // depending on the current session state:
            // Set the next timeout tick value, to compare against in the WorldManager
            // Sessions that have gone past the AuthLoginRequest step will stay active for a longer period of time (exposed via configuration)
            // Sessions that in the AuthLoginRequest will have a short timeout, as set in the AuthenticationHandler.DefaultAuthTimeout.
            // Example: Applications that check uptime will stay in the AuthLoginRequest state.
            session.Network.TimeoutTick = (session.State == Enum.SessionState.AuthLoginRequest) ?
                                          DateTime.UtcNow.AddSeconds(WorldManager.DefaultSessionTimeout).Ticks :
                                          DateTime.UtcNow.AddSeconds(AuthenticationHandler.DefaultAuthTimeout).Ticks;

            // If we have an EchoRequest flag, we should flag to respond with an echo response on next send.
            if (packet.Header.HasFlag(PacketHeaderFlags.EchoRequest))
            {
                FlagEcho(packet.HeaderOptional.EchoRequestClientTime);
            }

            // If we have an AcknowledgeSequence flag, we can clear our cached packet buffer up to that sequence.
            if (packet.Header.HasFlag(PacketHeaderFlags.AckSequence))
            {
                AcknowledgeSequence(packet.HeaderOptional.Sequence);
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.TimeSynch))
            {
                log.DebugFormat("[{0}] Incoming TimeSync TS: {1}", session.Account, packet.HeaderOptional.TimeSynch);
                // Do something with this...
                // Based on network traces these are not 1:1.  Server seems to send them every 20 seconds per port.
                // Client seems to send them alternatingly every 2 or 4 seconds per port.
                // We will send this at a 20 second time interval.  I don't know what to do with these when we receive them at this point.
            }

            // If the client is requesting a retransmission, pull those packets from the queue and resend them.
            if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit))
            {
                foreach (uint sequence in packet.HeaderOptional.RetransmitData)
                {
                    Retransmit(sequence);
                }
            }

            // This should be set on the first packet to the server indicating the client is logging in.
            // This is the start of a three-way handshake between the client and server (LoginRequest, ConnectRequest, ConnectResponse)
            // Note this would be sent to each server a client would connect too (Login and each world).
            // In our current implimenation we handle all roles in this one server.
            if (packet.Header.HasFlag(PacketHeaderFlags.LoginRequest))
            {
                AuthenticationHandler.HandleLoginRequest(packet, session);
                return;
            }

            // This should be set on the second packet to the server from the client.
            // This completes the three-way handshake.
            if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse))
            {
                sendResync = true;
                AuthenticationHandler.HandleConnectResponse(packet, session);
                return;
            }

            // Process all fragments out of the packet
            foreach (ClientPacketFragment fragment in packet.Fragments)
            {
                ProcessFragment(fragment);
            }

            // Update the last received sequence.
            if (packet.Header.Sequence != 0)
            {
                lastReceivedPacketSequence = packet.Header.Sequence;
            }
        }
Пример #2
0
        public static void HandlePacket(ConnectionType type, ClientPacket packet, Session session)
        {
            // CLinkStatusAverages::OnPingResponse
            if (packet.Header.HasFlag(PacketHeaderFlags.EchoRequest))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to calculate round trip time (ping)
                // client calculates: currentTime - requestTime - serverDrift
                var echoResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.EchoResponse);
                echoResponse.Payload.Write(packet.HeaderOptional.ClientTime);
                echoResponse.Payload.Write((float)connectionData.ServerTime - packet.HeaderOptional.ClientTime);

                NetworkManager.SendPacket(type, echoResponse, session);
            }

            // ClientNet::HandleTimeSynch
            if (packet.Header.HasFlag(PacketHeaderFlags.TimeSynch))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to update time at client and check for overspeed (60s desync and client will disconenct with speed hack warning)
                var timeSynchResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.TimeSynch);
                timeSynchResponse.Payload.Write(connectionData.ServerTime);

                NetworkManager.SendPacket(type, timeSynchResponse, session);
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit))
            {
                foreach (uint sequence in packet.HeaderOptional.RetransmitData)
                {
                    CachedPacket cachedPacket;
                    if (session.CachedPackets.TryGetValue(sequence, out cachedPacket))
                    {
                        if (!cachedPacket.Packet.Header.HasFlag(PacketHeaderFlags.Retransmission))
                        {
                            cachedPacket.Packet.Header.Flags |= PacketHeaderFlags.Retransmission;

                            uint issacXor;
                            cachedPacket.Packet.Header.Checksum = packet.CalculateChecksum(session, ConnectionType.World, cachedPacket.IssacXor, out issacXor);
                        }

                        NetworkManager.SendPacketDirect(ConnectionType.World, cachedPacket.Packet, session);
                    }
                }
            }

            if (type == ConnectionType.Login)
            {
                if (packet.Header.HasFlag(PacketHeaderFlags.LoginRequest))
                {
                    AuthenticationHandler.HandleLoginRequest(packet, session);
                    return;
                }

                if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse) && session.Authenticated)
                {
                    AuthenticationHandler.HandleConnectResponse(packet, session);
                    return;
                }
            }
            else
            {
                if (packet.Header.HasFlag(PacketHeaderFlags.WorldLoginRequest))
                {
                    AuthenticationHandler.HandleWorldLoginRequest(packet, session);
                    return;
                }

                if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse))
                {
                    AuthenticationHandler.HandleWorldConnectResponse(packet, session);
                    return;
                }
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.Disconnect))
            {
                HandleDisconnectResponse(packet, session);
            }

            foreach (ClientPacketFragment fragment in packet.Fragments)
            {
                var opcode = (FragmentOpcode)fragment.Payload.ReadUInt32();
                if (!fragmentHandlers.ContainsKey(opcode))
                {
                    Console.WriteLine($"Received unhandled fragment opcode: 0x{((uint)opcode).ToString("X4")}");
                }
                else
                {
                    FragmentHandler fragmentHandler;
                    if (fragmentHandlers.TryGetValue(opcode, out fragmentHandler))
                    {
                        fragmentHandler.Invoke(fragment, session);
                    }
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Handles a packet, reading the flags and processing all fragments.
        /// </summary>
        /// <param name="packet">ClientPacket to handle</param>
        private void HandlePacket(ClientPacket packet)
        {
            log.DebugFormat("[{0}] Handling packet {1}", session.Account, packet.Header.Sequence);

            uint issacXor = packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum) ? ConnectionData.IssacClient.GetOffset() : 0;

            if (!packet.VerifyChecksum(issacXor))
            {
                log.WarnFormat("[{0}] Packet {1} has invalid checksum", session.Account, packet.Header.Sequence);
            }

            // If we have an EchoRequest flag, we should flag to respond with an echo response on next send.
            if (packet.Header.HasFlag(PacketHeaderFlags.EchoRequest))
            {
                FlagEcho(packet.HeaderOptional.EchoRequestClientTime);
            }

            // If we have an AcknowledgeSequence flag, we can clear our cached packet buffer up to that sequence.
            if (packet.Header.HasFlag(PacketHeaderFlags.AckSequence))
            {
                AcknowledgeSequence(packet.HeaderOptional.Sequence);
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.TimeSynch))
            {
                // Do something with this...
                // Based on network traces these are not 1:1.  Server seems to send them every 20 seconds per port.
                // Client seems to send them alternatingly every 2 or 4 seconds per port.
                // We will send this at a 20 second time interval.  I don't know what to do with these when we receive them at this point.
            }

            // If the client is requesting a retransmission, pull those packets from the queue and resend them.
            if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit))
            {
                foreach (uint sequence in packet.HeaderOptional.RetransmitData)
                {
                    Retransmit(sequence);
                }
            }

            // This should be set on the first packet to the server indicating the client is logging in.
            // This is the start of a three-way handshake between the client and server (LoginRequest, ConnectRequest, ConnectResponse)
            // Note this would be sent to each server a client would connect too (Login and each world).
            // In our current implimenation we handle all roles in this one server.
            if (packet.Header.HasFlag(PacketHeaderFlags.LoginRequest))
            {
                AuthenticationHandler.HandleLoginRequest(packet, session);
                return;
            }

            // This should be set on the second packet to the server from the client.
            // This comletes the three-way handshake.
            if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse))
            {
                sendResync = true;
                AuthenticationHandler.HandleConnectResponse(packet, session);
                return;
            }

            // Process all fragments out of the packet
            foreach (ClientPacketFragment fragment in packet.Fragments)
            {
                ProcessFragment(fragment);
            }

            // Update the last received sequence.
            if (packet.Header.Sequence != 0)
            {
                lastReceivedPacketSequence = packet.Header.Sequence;
            }
        }
Пример #4
0
 private static void HandleDisconnectResponse(ClientPacket packet, Session session)
 {
     WorldManager.Remove(session);
 }
Пример #5
0
        public static void HandlePacket(ConnectionType type, ClientPacket packet, Session session)
        {
            if (!CheckPacketHeader(packet, session))
            {
                // server treats all packets sent during the first 30 seconds as invalid packets due to server crash, this will move clients to the disconnect screen
                if (DateTime.Now < WorldManager.WorldStartTime.AddSeconds(30d))
                {
                    session.SendCharacterError(CharacterError.ServerCrash);
                }
                return;
            }

            // CLinkStatusAverages::OnPingResponse
            if (packet.Header.HasFlag(PacketHeaderFlags.EchoRequest))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to calculate round trip time (ping)
                // client calculates: currentTime - requestTime - serverDrift
                var echoResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.EchoResponse);
                echoResponse.Payload.Write(packet.HeaderOptional.ClientTime);
                echoResponse.Payload.Write((float)connectionData.ServerTime - packet.HeaderOptional.ClientTime);

                NetworkManager.SendPacket(type, echoResponse, session);
            }

            // ClientNet::HandleTimeSynch
            if (packet.Header.HasFlag(PacketHeaderFlags.TimeSynch))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to update time at client and check for overspeed (60s desync and client will disconenct with speed hack warning)
                var timeSynchResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.TimeSynch);
                timeSynchResponse.Payload.Write(connectionData.ServerTime);

                NetworkManager.SendPacket(type, timeSynchResponse, session);
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit))
            {
                foreach (uint sequence in packet.HeaderOptional.RetransmitData)
                {
                    CachedPacket cachedPacket;
                    if (session.CachedPackets.TryGetValue(sequence, out cachedPacket))
                    {
                        if (!cachedPacket.Packet.Header.HasFlag(PacketHeaderFlags.Retransmission))
                        {
                            cachedPacket.Packet.Header.Flags |= PacketHeaderFlags.Retransmission;

                            uint issacXor;
                            cachedPacket.Packet.Header.Checksum = packet.CalculateChecksum(session, ConnectionType.World, cachedPacket.IssacXor, out issacXor);
                        }

                        NetworkManager.SendPacketDirect(ConnectionType.World, cachedPacket.Packet, session);
                    }
                }
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.LoginRequest))
            {
                AuthenticationHandler.HandleLoginRequest(packet, session);
                return;
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.WorldLoginRequest))
            {
                AuthenticationHandler.HandleWorldLoginRequest(packet, session);
                return;
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse))
            {
                if (type == ConnectionType.Login)
                {
                    AuthenticationHandler.HandleConnectResponse(packet, session);
                    return;
                }

                AuthenticationHandler.HandleWorldConnectResponse(packet, session);
                return;
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.Disconnect))
            {
                HandleDisconnectResponse(packet, session);
            }

            foreach (ClientPacketFragment fragment in packet.Fragments)
            {
                var opcode = (FragmentOpcode)fragment.Payload.ReadUInt32();
                if (!fragmentHandlers.ContainsKey(opcode))
                {
                    Console.WriteLine($"Received unhandled fragment opcode: 0x{(uint)opcode:X4}");
                }
                else
                {
                    FragmentHandlerInfo fragmentHandlerInfo;
                    if (fragmentHandlers.TryGetValue(opcode, out fragmentHandlerInfo))
                    {
                        if (fragmentHandlerInfo.Attribute.State == session.State)
                        {
                            fragmentHandlerInfo.Handler.Invoke(fragment, session);
                        }
                    }
                }
            }
        }