public TransferBuffer(TransferBuffer rhs) { lock (rhs.m_lock) { m_buffer = new byte[rhs.m_buffer.Length]; System.Buffer.BlockCopy(rhs.m_buffer, 0, m_buffer, 0, m_buffer.Length); m_offset = rhs.m_offset; m_size = rhs.m_size; m_lock = new object(); } }
private KeyValuePair<TransferBuffer, Packet> GetPacketToSend() { if (m_outgoing_packets.Count == 0) { throw (new Exception("[SecurityAPI::GetPacketToSend] No packets are avaliable to send.")); } Packet packet = m_outgoing_packets[0]; m_outgoing_packets.RemoveAt(0); if (packet.Massive) { ushort parts = 0; PacketWriter final = new PacketWriter(); PacketWriter final_data = new PacketWriter(); byte[] input_data = packet.GetBytes(); PacketReader input_reader = new PacketReader(input_data); TransferBuffer workspace = new TransferBuffer(4089, 0, (int)input_data.Length); while (workspace.Size > 0) { PacketWriter part_data = new PacketWriter(); int cur_size = workspace.Size > 4089 ? 4089 : workspace.Size; // Max buffer size is 4kb for the client part_data.Write((byte)0); // Data flag part_data.Write(input_data, workspace.Offset, cur_size); workspace.Offset += cur_size; workspace.Size -= cur_size; // Update the size final_data.Write(FormatPacket(0x600D, part_data.GetBytes(), false)); ++parts; // Track how many parts there are } // Write the final header packet to the front of the packet PacketWriter final_header = new PacketWriter(); final_header.Write((byte)1); // Header flag final_header.Write((short)parts); final_header.Write(packet.Opcode); final.Write(FormatPacket(0x600D, final_header.GetBytes(), false)); // Finish the large packet of all the data final.Write(final_data.GetBytes()); // Return the collated data byte[] raw_bytes = final.GetBytes(); packet.Lock(); return new KeyValuePair<TransferBuffer, Packet>(new TransferBuffer(raw_bytes, 0, raw_bytes.Length, true), packet); } else { bool encrypted = packet.Encrypted; if (!m_client_security) { if (m_enc_opcodes.Contains(packet.Opcode)) { encrypted = true; } } byte[] raw_bytes = FormatPacket(packet.Opcode, packet.GetBytes(), encrypted); packet.Lock(); return new KeyValuePair<TransferBuffer, Packet>(new TransferBuffer(raw_bytes, 0, raw_bytes.Length, true), packet); } }
// Default constructor public SecurityManager() { m_value_x = 0; m_value_g = 0; m_value_p = 0; m_value_A = 0; m_value_B = 0; m_value_K = 0; m_seed_count = 0; m_crc_seed = 0; m_initial_blowfish_key = 0; m_handshake_blowfish_key = 0; m_count_byte_seeds = new byte[3]; m_count_byte_seeds[0] = 0; m_count_byte_seeds[1] = 0; m_count_byte_seeds[2] = 0; m_client_key = 0; m_challenge_key = 0; m_client_security = false; m_security_flag = 0; m_security_flags = new SecurityFlags(); m_accepted_handshake = false; m_started_handshake = false; m_identity_flag = 0; m_identity_name = "<None>"; m_outgoing_packets = new List<Packet>(); m_incoming_packets = new List<Packet>(); m_enc_opcodes = new List<ushort>(); m_enc_opcodes.Add(0x2001); m_enc_opcodes.Add(0x6100); m_enc_opcodes.Add(0x6101); m_enc_opcodes.Add(0x6102); m_enc_opcodes.Add(0x6103); m_enc_opcodes.Add(0x6107); m_blowfish = new Blowfish(); m_recv_buffer = new TransferBuffer(8192); // must be at minimal 2 bytes! m_current_buffer = null; m_massive_count = 0; m_massive_packet = null; m_class_lock = new object(); }
// Transfers raw incoming data into the security object. Call TransferIncoming to // obtain a list of ready to process packets. public void Recv(TransferBuffer raw_buffer) { List<TransferBuffer> incoming_buffers_tmp = new List<TransferBuffer>(); lock (m_class_lock) { int length = raw_buffer.Size - raw_buffer.Offset; int index = 0; while (length > 0) { int max_length = length; int calc_length = m_recv_buffer.Buffer.Length - m_recv_buffer.Size; if (max_length > calc_length) { max_length = calc_length; } length -= max_length; Buffer.BlockCopy(raw_buffer.Buffer, raw_buffer.Offset + index, m_recv_buffer.Buffer, m_recv_buffer.Size, max_length); m_recv_buffer.Size += max_length; index += max_length; // Loop while we have data to process while (m_recv_buffer.Size > 0) { // If we do not have a current packet object, try to allocate one. if (m_current_buffer == null) { // We need at least two bytes to allocate a packet. if (m_recv_buffer.Size < 2) { break; } // Calculate the packet size. int packet_size = m_recv_buffer.Buffer[1] << 8 | m_recv_buffer.Buffer[0]; // Check to see if this packet is encrypted. if ((packet_size & 0x8000) > 0) { // If so, calculate the total payload size. packet_size &= 0x7FFF; // Mask off the encryption. if (m_security_flags.blowfish == 1) { packet_size = 2 + m_blowfish.GetOutputLength(packet_size + 4); } else { packet_size += 6; } } else { // The packet is unencrypted. The final size is simply // header size + payload size. packet_size += 6; } // Allocate the final buffer the packet will be written to m_current_buffer = new TransferBuffer(packet_size, 0, packet_size); } // Calculate how many bytes are left to receive in the packet. int max_copy_count = m_current_buffer.Size - m_current_buffer.Offset; // If we need more bytes than we currently have, update the size. if (max_copy_count > m_recv_buffer.Size) { max_copy_count = m_recv_buffer.Size; } // Copy the buffer data to the packet buffer Buffer.BlockCopy(m_recv_buffer.Buffer, 0, m_current_buffer.Buffer, m_current_buffer.Offset, max_copy_count); // Update how many bytes we now have m_current_buffer.Offset += max_copy_count; m_recv_buffer.Size -= max_copy_count; // If there is data remaining in the buffer, copy it over the data // we just removed (sliding buffer). if (m_recv_buffer.Size > 0) { Buffer.BlockCopy(m_recv_buffer.Buffer, max_copy_count, m_recv_buffer.Buffer, 0, m_recv_buffer.Size); } // Check to see if the current packet is now complete. if (m_current_buffer.Size == m_current_buffer.Offset) { // If so, dispatch it to the manager class for processing by the system. m_current_buffer.Offset = 0; incoming_buffers_tmp.Add(m_current_buffer); // Set the current packet to null so we can process the next packet // in the stream. m_current_buffer = null; } else { // Otherwise, we are done with this loop, since we need more // data for the current packet. break; } } } if (incoming_buffers_tmp.Count > 0) { foreach (TransferBuffer buffer in incoming_buffers_tmp) { bool packet_encrypted = false; int packet_size = buffer.Buffer[1] << 8 | buffer.Buffer[0]; if ((packet_size & 0x8000) > 0) { if (m_security_flags.blowfish == 1) { packet_size &= 0x7FFF; packet_encrypted = true; } else { packet_size &= 0x7FFF; } } if (packet_encrypted) { byte[] decrypted = m_blowfish.Decode(buffer.Buffer, 2, buffer.Size - 2); byte[] new_buffer = new byte[6 + packet_size]; Buffer.BlockCopy(BitConverter.GetBytes((ushort)packet_size), 0, new_buffer, 0, 2); Buffer.BlockCopy(decrypted, 0, new_buffer, 2, 4 + packet_size); buffer.Buffer = null; buffer.Buffer = new_buffer; } PacketReader packet_data = new PacketReader(buffer.Buffer); packet_size = packet_data.ReadUInt16(); ushort packet_opcode = packet_data.ReadUInt16(); byte packet_security_count = packet_data.ReadByte(); byte packet_security_crc = packet_data.ReadByte(); // Client object whose bytes the server might need to verify if (m_client_security) { if (m_security_flags.security_bytes == 1) { byte expected_count = GenerateCountByte(true); if (packet_security_count != expected_count) { //throw (new Exception("[SecurityAPI::Recv] Count byte mismatch on {packet_opcode.ToString("X4")}.")); StaticLogger.Instance.Warn($"[SecurityAPI::Recv] Count byte mismatch on {packet_opcode.ToString("X4")}. (Expected: {expected_count}, Packet: {packet_security_count})"); } if (packet_encrypted || (m_security_flags.security_bytes == 1 && m_security_flags.blowfish == 0)) { if (packet_encrypted || m_enc_opcodes.Contains(packet_opcode)) { packet_size |= 0x8000; Buffer.BlockCopy(BitConverter.GetBytes((ushort)packet_size), 0, buffer.Buffer, 0, 2); } } buffer.Buffer[5] = 0; byte expected_crc = GenerateCheckByte(buffer.Buffer); if (packet_security_crc != expected_crc) { //throw (new Exception("[SecurityAPI::Recv] CRC byte mismatch.")); StaticLogger.Instance.Warn($"[SecurityAPI::Recv] CRC byte mismatch on {packet_opcode.ToString("X4")}. (Expected: {expected_crc}, Packet: {packet_security_crc})"); } buffer.Buffer[4] = 0; if (packet_encrypted || (m_security_flags.security_bytes == 1 && m_security_flags.blowfish == 0)) { if (packet_encrypted || m_enc_opcodes.Contains(packet_opcode)) { packet_size &= 0x7FFF; Buffer.BlockCopy(BitConverter.GetBytes((ushort)packet_size), 0, buffer.Buffer, 0, 2); } } } } if (packet_opcode == 0x5000 || packet_opcode == 0x9000) // New logic processing! { Handshake(packet_opcode, packet_data, packet_encrypted); // Pass the handshake packets to the user so they can at least see them. // They do not need to actually do anything with them. This was added to // help debugging and make output logs complete. Packet packet = new Packet(packet_opcode, packet_encrypted, false, buffer.Buffer, 6, packet_size); packet.Lock(); m_incoming_packets.Add(packet); } else { if (m_client_security) { // Make sure the client accepted the security system first if (!m_accepted_handshake) { throw (new Exception("[SecurityAPI::Recv] The client has not accepted the handshake.")); } } if (packet_opcode == 0x600D) // Auto process massive messages for the user { byte mode = packet_data.ReadByte(); if (mode == 1) { m_massive_count = packet_data.ReadUInt16(); ushort contained_packet_opcode = packet_data.ReadUInt16(); m_massive_packet = new Packet(contained_packet_opcode, packet_encrypted, true); } else { if (m_massive_packet == null) { throw (new Exception("[SecurityAPI::Recv] A malformed 0x600D packet was received.")); } m_massive_packet.WriteByteArray(packet_data.ReadBytes(packet_size - 1)); m_massive_count--; if (m_massive_count == 0) { m_massive_packet.Lock(); m_incoming_packets.Add(m_massive_packet); m_massive_packet = null; } } } else { Packet packet = new Packet(packet_opcode, packet_encrypted, false, buffer.Buffer, 6, packet_size); packet.Lock(); m_incoming_packets.Add(packet); } } } } } }