// 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) { PacketReader packet_data = null; try { 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; } 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.")); } 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.")); } 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. //CA2000 dont care 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.WriteUInt8Array(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 { //CA2000 dont care Packet packet = new Packet(packet_opcode, packet_encrypted, false, buffer.Buffer, 6, packet_size); packet.Lock(); m_incoming_packets.Add(packet); } } } finally { if (packet_data != null) { packet_data.Close(); } } } } } }