/// <summary> /// request retransmission of lost sequences /// </summary> /// <param name="rcvdSeq">the sequence of the packet that was just received.</param> private void DoRequestForRetransmission(uint rcvdSeq) { var desiredSeq = lastReceivedPacketSequence + 1; List <uint> needSeq = new List <uint>(); needSeq.Add(desiredSeq); uint bottom = desiredSeq + 1; for (uint a = bottom; a < rcvdSeq; a++) { if (!outOfOrderPackets.ContainsKey(a)) { needSeq.Add(a); } } ServerPacket reqPacket = new ServerPacket(); byte[] reqData = new byte[4 + (needSeq.Count * 4)]; MemoryStream msReqData = new MemoryStream(reqData); msReqData.Write(BitConverter.GetBytes((uint)needSeq.Count), 0, 4); needSeq.ForEach(k => msReqData.Write(BitConverter.GetBytes(k), 0, 4)); reqPacket.Data = msReqData; reqPacket.Header.Flags = PacketHeaderFlags.RequestRetransmit; EnqueueSend(reqPacket); LastRequestForRetransmitTime = DateTime.Now; packetLog.DebugFormat("[{0}] Requested retransmit of {1}", session.LoggingIdentifier, needSeq.Select(k => k.ToString()).Aggregate((a, b) => a + ", " + b)); NetworkStatistics.S2C_RequestsForRetransmit_Aggregate_Increment(); }
public bool VerifyCRC(CryptoSystem fq) { if (Header.HasFlag(PacketHeaderFlags.EncryptedChecksum)) { var key = ((Header.Checksum - headerChecksum) ^ payloadChecksum); if (fq.Search(key)) { fq.ConsumeKey(key); return(true); } } else { if (headerChecksum + payloadChecksum == Header.Checksum) { packetLog.DebugFormat("{0}", this); return(true); } packetLog.DebugFormat("{0}, Checksum Failed", this); } NetworkStatistics.C2S_CRCErrors_Aggregate_Increment(); return(false); }
// This is called from ConnectionListener.OnDataReceieve()->Session.ProcessPacket()->This /// <summary> /// Processes and incoming packet from a client. /// </summary> /// <param name="packet">The ClientPacket to process.</param> public void ProcessPacket(ClientPacket packet) { packetLog.DebugFormat("[{0}] Processing packet {1}", session.LoggingIdentifier, packet.Header.Sequence); NetworkStatistics.C2S_Packets_Aggregate_Increment(); // If the client is requesting a retransmission, pull those packets from the queue and resend them. if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit)) { if (VerifyCRC(packet)) { foreach (uint sequence in packet.HeaderOptional.RetransmitData) { Retransmit(sequence); } NetworkStatistics.C2S_RequestsForRetransmit_Aggregate_Increment(); } return; } // Check if this packet's sequence is a sequence which we have already processed. // There are some exceptions: // Sequence 0 as we have several Seq 0 packets during connect. This also cathes a case where it seems CICMDCommand arrives at any point with 0 sequence value too. // If the only header on the packet is AckSequence. It seems AckSequence can come in with the same sequence value sometimes. if (packet.Header.Sequence <= lastReceivedPacketSequence && packet.Header.Sequence != 0 && !(packet.Header.Flags == PacketHeaderFlags.AckSequence && packet.Header.Sequence == lastReceivedPacketSequence)) { packetLog.WarnFormat("[{0}] Packet {1} received again", session.LoggingIdentifier, packet.Header.Sequence); return; } // Check if this packet's sequence is greater then the next one we should be getting. // If true we must store it to replay once we have caught up. var desiredSeq = lastReceivedPacketSequence + 1; if (packet.Header.Sequence > desiredSeq) { packetLog.DebugFormat("[{0}] Packet {1} received out of order", session.LoggingIdentifier, packet.Header.Sequence); if (!outOfOrderPackets.ContainsKey(packet.Header.Sequence)) { outOfOrderPackets.TryAdd(packet.Header.Sequence, packet); } if (desiredSeq + 2 <= packet.Header.Sequence && DateTime.Now - LastRequestForRetransmitTime > new TimeSpan(0, 0, 1)) { DoRequestForRetransmission(packet.Header.Sequence); } return; } // If we reach here, this is a packet we should proceed with processing. HandlePacket(packet); // Finally check if we have any out of order packets or fragments we need to process; CheckOutOfOrderPackets(); CheckOutOfOrderFragments(); }
private void SendPacket(ServerPacket packet) { packetLog.DebugFormat("[{0}] Sending packet {1}", session.LoggingIdentifier, packet.GetHashCode()); NetworkStatistics.S2C_Packets_Aggregate_Increment(); if (packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum)) { uint issacXor = ConnectionData.IssacServer.GetOffset(); packetLog.DebugFormat("[{0}] Setting Issac for packet {1} to {2}", session.LoggingIdentifier, packet.GetHashCode(), issacXor); packet.IssacXor = issacXor; } SendPacketRaw(packet); }
const uint MaxNumNakSeqIds = 115; //464 + header = 484; (464 - 4) / 4 /// <summary> /// request retransmission of lost sequences /// </summary> /// <param name="rcvdSeq">the sequence of the packet that was just received.</param> private void DoRequestForRetransmission(uint rcvdSeq) { var desiredSeq = lastReceivedPacketSequence + 1; List <uint> needSeq = new List <uint>(); needSeq.Add(desiredSeq); uint bottom = desiredSeq + 1; if (rcvdSeq < bottom || rcvdSeq - bottom > CryptoSystem.MaximumEffortLevel) { session.Terminate(SessionTerminationReason.AbnormalSequenceReceived); return; } uint seqIdCount = 1; for (uint a = bottom; a < rcvdSeq; a++) { if (!outOfOrderPackets.ContainsKey(a)) { needSeq.Add(a); seqIdCount++; if (seqIdCount >= MaxNumNakSeqIds) { break; } } } ServerPacket reqPacket = new ServerPacket(); byte[] reqData = new byte[4 + (needSeq.Count * 4)]; MemoryStream msReqData = new MemoryStream(reqData, 0, reqData.Length, true, true); msReqData.Write(BitConverter.GetBytes((uint)needSeq.Count), 0, 4); needSeq.ForEach(k => msReqData.Write(BitConverter.GetBytes(k), 0, 4)); reqPacket.Data = msReqData; reqPacket.Header.Flags = PacketHeaderFlags.RequestRetransmit; EnqueueSend(reqPacket); LastRequestForRetransmitTime = DateTime.UtcNow; packetLog.DebugFormat("[{0}] Requested retransmit of {1}", session.LoggingIdentifier, needSeq.Select(k => k.ToString()).Aggregate((a, b) => a + ", " + b)); NetworkStatistics.S2C_RequestsForRetransmit_Aggregate_Increment(); }
private bool VerifyCRC(ClientPacket packet) { bool encryptedChecksum = !packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit) && packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum); if (encryptedChecksum) { #if NETDIAG int?gen = GetGeneration(ConnectionData.IssacClient, packet); if (gen != null) { // gen should always be 1 notch forward, but some programming errors have revealed gen to be 2 or more and since fixed // generational ISSAC helps immensely when troubleshooting certain kinds of protocol problems ConnectionData.IssacClient = GetGeneration(ConnectionData.IssacClient, gen.Value); if (gen.Value != 1) { packetLog.Warn($"Packet CRC encryption generation out of sequence for packet {packet.Header.Sequence} gen {gen} {UnfoldFlags(packet.Header.Flags)}"); } packetLog.Debug($"Verified encrypted CRC for packet {packet.Header.Sequence} gen {gen} {UnfoldFlags(packet.Header.Flags)}"); return(true); } #else if (packet.VerifyChecksum(ConnectionData.IssacClient.GetOffset())) { return(true); } #endif } else { if (packet.VerifyChecksum(0)) { packetLog.Debug($"Verified CRC for packet {packet.Header.Sequence} {UnfoldFlags(packet.Header.Flags)}"); return(true); } } NetworkStatistics.C2S_CRCErrors_Aggregate_Increment(); return(false); }
public bool VerifyCRC(CryptoSystem fq, bool rangeAdvance) { if (Header.HasFlag(PacketHeaderFlags.EncryptedChecksum)) { if (VerifyEncryptedCRCAndLogResult(fq, rangeAdvance)) { CRCVerified = true; return(true); } } else { if (Header.HasFlag(PacketHeaderFlags.RequestRetransmit)) { // discard retransmission request with cleartext CRC // client sends one encrypted version and one non encrypted version of each retransmission request // honoring these causes client to drop because it's only expecting one of the two retransmission requests to be honored // and it's more secure to only accept the trusted version return(false); } else { if (VerifyChecksum(0)) { packetLog.Debug($"{this}"); return(true); } else { packetLog.Debug($"{this}, Checksum Failed"); } } } NetworkStatistics.C2S_CRCErrors_Aggregate_Increment(); return(false); }
public bool VerifyCRC(CryptoSystem fq) { if (Header.HasFlag(PacketHeaderFlags.EncryptedChecksum)) { if (VerifyEncryptedCRCAndLogResult(fq)) { return(true); } } else { if (VerifyChecksum()) { packetLog.DebugFormat("{0}", this); return(true); } packetLog.DebugFormat("{0}, Checksum Failed", this); } NetworkStatistics.C2S_CRCErrors_Aggregate_Increment(); return(false); }
// This is called from ConnectionListener.OnDataReceieve()->Session.ProcessPacket()->This /// <summary> /// Processes and incoming packet from a client. /// </summary> /// <param name="packet">The ClientPacket to process.</param> public void ProcessPacket(ClientPacket packet) { if (isReleased) // Session has been removed { return; } packetLog.DebugFormat("[{0}] Processing packet {1}", session.LoggingIdentifier, packet.Header.Sequence); NetworkStatistics.C2S_Packets_Aggregate_Increment(); if (!packet.VerifyCRC(ConnectionData.CryptoClient, true)) { return; } #region order-insensitive "half-processing" if (packet.Header.HasFlag(PacketHeaderFlags.Disconnect)) { session.Terminate(SessionTerminationReason.PacketHeaderDisconnect); return; } if (packet.Header.HasFlag(PacketHeaderFlags.NetErrorDisconnect)) { session.Terminate(SessionTerminationReason.ClientSentNetworkErrorDisconnect); return; } // If the client is requesting a retransmission process it immediately if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit)) { foreach (uint sequence in packet.HeaderOptional.RetransmitData) { Retransmit(sequence); } NetworkStatistics.C2S_RequestsForRetransmit_Aggregate_Increment(); } // 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 == SessionState.AuthLoginRequest) ? DateTime.UtcNow.AddSeconds(AuthenticationHandler.DefaultAuthTimeout).Ticks : // Default is 15s DateTime.UtcNow.AddSeconds(NetworkManager.DefaultSessionTimeout).Ticks; // Default is 60s #endregion #region Reordering stage // Reordering stage // Check if this packet's sequence is a sequence which we have already processed. // There are some exceptions: // Sequence 0 as we have several Seq 0 packets during connect. This also cathes a case where it seems CICMDCommand arrives at any point with 0 sequence value too. // If the only header on the packet is AckSequence. It seems AckSequence can come in with the same sequence value sometimes. if (packet.Header.Sequence <= lastReceivedPacketSequence && packet.Header.Sequence != 0 && !(packet.Header.Flags == PacketHeaderFlags.AckSequence && packet.Header.Sequence == lastReceivedPacketSequence)) { packetLog.WarnFormat("[{0}] Packet {1} received again", session.LoggingIdentifier, packet.Header.Sequence); return; } // Check if this packet's sequence is greater then the next one we should be getting. // If true we must store it to replay once we have caught up. var desiredSeq = lastReceivedPacketSequence + 1; if (packet.Header.Sequence > desiredSeq) { packetLog.DebugFormat("[{0}] Packet {1} received out of order", session.LoggingIdentifier, packet.Header.Sequence); if (!outOfOrderPackets.ContainsKey(packet.Header.Sequence)) { outOfOrderPackets.TryAdd(packet.Header.Sequence, packet); } if (desiredSeq + 2 <= packet.Header.Sequence && DateTime.Now - LastRequestForRetransmitTime > new TimeSpan(0, 0, 1)) { DoRequestForRetransmission(packet.Header.Sequence); } return; } #endregion #region Final processing stage // Processing stage // If we reach here, this is a packet we should proceed with processing. HandleOrderedPacket(packet); // Process data now in sequence // Finally check if we have any out of order packets or fragments we need to process; CheckOutOfOrderPackets(); CheckOutOfOrderFragments(); #endregion }