internal void Encode(NetConnection connection, NetBuffer intoBuffer) { if (m_type == NetMessageType.None) m_type = NetMessageType.User; intoBuffer.Write((byte)m_type, 3); switch(m_type) { case NetMessageType.UserFragmented: case NetMessageType.User: case NetMessageType.Acknowledge: case NetMessageType.AcknowledgeBitField: intoBuffer.Write((byte)m_sequenceChannel, 5); // encode channel intoBuffer.Write(m_sequenceNumber, 12); // encode sequence number break; } int byteLen = (m_buffer == null ? 0 : m_buffer.LengthBytes); if (m_type == NetMessageType.UserFragmented || m_type == NetMessageType.User || m_type == NetMessageType.Discovery || m_type == NetMessageType.Handshake) intoBuffer.Write(byteLen, 12); // encode length // encode payload if (byteLen > 0) intoBuffer.Write(m_buffer.Data, 0, byteLen); return; }
internal int ExecuteSend(NetBuffer buffer, NetConnection connection, IPEndPoint remoteEP) { if (buffer.LengthBytes < 0) { Log.Warning("ExecuteSend passed 0 bytes to send"); return 0; } #if DEBUG if (connection != null) { if (connection.Configuration.SimulateLagLoss && m_lagLoss != null) return m_lagLoss.ExecuteSend(this, buffer, connection, remoteEP); } #endif // encrypt if (connection != null && connection.m_encryption.SymmetricEncryptionKeyBytes != null) { // Log.Debug("SEND: Encrypting packet using key: " + Convert.ToBase64String(connection.SymmetricEncryptionKey)); connection.m_encryption.EncryptSymmetric(buffer); } try { int bytesSent = m_socket.SendTo(buffer.Data, 0, buffer.LengthBytes, SocketFlags.None, remoteEP); Debug.Assert(bytesSent == buffer.LengthBytes, "Ouch, sent partial UDP message?!"); //Log.Verbose(string.Format(CultureInfo.InvariantCulture, "Sent {0} bytes to {1}", bytesSent, remoteEP)); return bytesSent; } catch (SocketException sex) { if (sex.SocketErrorCode == SocketError.WouldBlock) { // send buffer overflow? #if DEBUG Log.Error("SocketException.WouldBlock thrown during sending; send buffer overflow? Increase buffer using NetAppConfiguration.SendBufferSize"); throw new NetException("SocketException.WouldBlock thrown during sending; send buffer overflow? Increase buffer using NetAppConfiguration.SendBufferSize", sex); #else // let reliability handle it, but log warning Log.Warning("Network send buffer overflow"); #endif } if (sex.SocketErrorCode == SocketError.ConnectionReset || sex.SocketErrorCode == SocketError.ConnectionRefused || sex.SocketErrorCode == SocketError.ConnectionAborted) { Log.Warning("Remote socket forcefully closed: " + sex.SocketErrorCode); if (connection != null) connection.Disconnect("Socket forcefully closed: " + sex.SocketErrorCode); return 0; } Log.Warning("Execute SocketException: " + sex.SocketErrorCode); return 0; } }
public int ExecuteSend(NetBase netBase, NetBuffer buffer, NetConnection connection, IPEndPoint remoteEP) { int len = buffer.LengthBytes; #if DEBUG if (connection != null) { NetConnectionConfiguration config = connection.Configuration; if (config.LossChance > 0.0f && NetRandom.Default.Chance(config.LossChance)) { //m_log.Debug("(simulating loss of sent packet)"); return len; } if (config.LagDelayChance > 0.0f && NetRandom.Default.Chance(config.LagDelayChance)) { float delayAmount = config.LagDelayMinimum + (NetRandom.Default.NextFloat() * config.LagDelayVariance); DelayedPacket pk = new DelayedPacket(); pk.Data = new byte[len]; Array.Copy(buffer.Data, pk.Data, buffer.LengthBytes); pk.DelayAmount = delayAmount; pk.DelayedUntil = NetTime.Now + delayAmount; pk.RemoteEP = remoteEP; m_delayed.Add(pk); //m_log.Debug("(queueing packet for " + (int)(pk.DelayAmount * 1000.0f) + " ms)"); return len; } } #endif try { int bytesSent = netBase.m_socket.SendTo(buffer.Data, 0, len, SocketFlags.None, remoteEP); m_log.Verbose(string.Format(CultureInfo.InvariantCulture, "Sent {0} bytes to {1}", bytesSent, remoteEP)); #if DEBUG if (connection != null) { NetConnectionConfiguration config = connection.Configuration; if (NetRandom.Default.Chance(config.DuplicatedPacketChance)) { m_log.Debug("(simulating send packet duplication)"); netBase.m_socket.SendTo(buffer.Data, 0, buffer.LengthBytes, SocketFlags.None, remoteEP); } } #endif return bytesSent; } catch (SocketException sex) { if (sex.SocketErrorCode == SocketError.ConnectionReset || sex.SocketErrorCode == SocketError.ConnectionRefused || sex.SocketErrorCode == SocketError.ConnectionAborted) { m_log.Warning("Remote socket forcefully closed: " + sex.SocketErrorCode); if (connection != null) connection.Disconnect("Socket forcefully closed: " + sex.SocketErrorCode); return 0; } m_log.Warning("Execute SocketException: " + sex.SocketErrorCode); return 0; } }
/// <summary> /// Create a net message /// </summary> public NetMessage() { m_type = NetMessageType.User; System.Diagnostics.Debug.Assert(NetBase.CurrentContext != null, "No network context; create a NetClient or NetServer first"); m_buffer = new NetBuffer(NetBase.CurrentContext.Configuration.DefaultNetMessageBufferSize); }
protected void InitBase(NetAppConfiguration config, NetLog log) { if (config == null) throw new ArgumentNullException("config"); if (log == null) throw new ArgumentNullException("log"); IsLittleEndian = BitConverter.IsLittleEndian; //if (BitConverter.IsLittleEndian) BitWriter = new LittleEndianBitWriter(); //else // BitWriter = new BigEndianBitWriter(); Configuration = config; Log = log; Configuration.m_isLocked = true; // prevent changes // validate config if (config.ApplicationIdentifier == NetConstants.DefaultApplicationIdentifier) log.Error("Warning! ApplicationIdentifier not set in configuration!"); if (this is NetServer) { if (config.MaximumConnections == -1) throw new ArgumentException("MaximumConnections must be set in configuration!"); if (config.ServerName == NetConstants.DefaultServerName) log.Warning("Warning! Server name not set!"); } // create buffers m_sendBuffer = new NetBuffer(config.SendBufferSize); m_receiveBuffer = new NetBuffer(config.ReceiveBufferSize); // Bind to port try { IPEndPoint iep = new IPEndPoint(IPAddress.Any, config.Port); EndPoint ep = (EndPoint)iep; m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); m_socket.Blocking = false; m_socket.Bind(ep); if (iep.Port != 0) Log.Info("Bound to port " + iep.Port); } catch (SocketException sex) { if (sex.SocketErrorCode != SocketError.AddressAlreadyInUse) throw new NetException("Failed to bind to port " + config.Port + " - Address already in use!", sex); } catch (Exception ex) { throw new NetException("Failed to bind to port " + config.Port, ex); } m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, config.ReceiveBufferSize); m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, config.SendBufferSize); m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0); #if DEBUG m_lagLoss = new NetLogLossInducer(log); #endif return; }
internal NetMessage(NetMessageType tp, int initialCapacity) { m_type = tp; m_buffer = new NetBuffer(initialCapacity); }
/// <summary> /// Create a message with specified initial capacity in bytes /// </summary> public NetMessage(int initialCapacity) { m_type = NetMessageType.User; m_buffer = new NetBuffer(initialCapacity); }
/// <summary> /// Decrypt using XTEA algo and verify CRC /// </summary> /// <returns>true for success, false for failure</returns> internal bool DecryptSymmetric(NetBuffer buffer) { int bufLen = buffer.LengthBytes; if (bufLen % 8 != 0) { if (NetBase.CurrentContext != null && NetBase.CurrentContext.Log != null) NetBase.CurrentContext.Log.Info("Bad buffer size in DecryptSymmetricInPlace()"); return false; } //NetBase.CurrentContext.Log.Debug.Debug("Decrypting using key: " + Convert.ToBase64String(m_xtea.Key)); // decrypt for (int i = 0; i < bufLen; i += 8) m_xtea.DecryptBlock(buffer.Data, i, buffer.Data, i); int numPadBits = buffer.Data[bufLen - 1]; buffer.Data[bufLen - 1] = 0; // zap for correct crc calculation int dataBits = (bufLen * 8) - (24 + numPadBits); // include pad and crc buffer.ResetReadPointer(dataBits); ushort statedCrc = buffer.ReadUInt16(); // zap crc to be able to compare buffer.ResetWritePointer(dataBits); buffer.Write((ushort)0); ushort dataCrc = Checksum.Adler16(buffer.Data, 0, bufLen); //NetBase.CurrentContext.Log.Debug("Plain (len " + bufLen + "): " + Convert.ToBase64String(buffer.Data, 0, bufLen) + " Stated CRC: " + statedCrc + " Calc: " + realCrc); if (statedCrc != dataCrc) { if (NetBase.CurrentContext != null && NetBase.CurrentContext.Log != null) NetBase.CurrentContext.Log.Warning("CRC failure; expected " + dataCrc + " found " + statedCrc + " dropping packet!"); return false; } // clean up buffer.LengthBits = dataBits; buffer.ResetReadPointer(); return true; }
public static bool SimpleUnitTest() { // encryption test NetEncryption enc = new NetEncryption(); byte[] key = new byte[16]; NetRandom.Default.NextBytes(key); enc.SetSymmetricKey(key); byte[] data = Encoding.ASCII.GetBytes("Michael"); // 7 bytes byte[] correct = new byte[data.Length]; data.CopyTo(correct, 0); NetBuffer buf = new NetBuffer(data); enc.EncryptSymmetric(buf); bool ok = enc.DecryptSymmetric(buf); if (!ok) return false; // compare if (buf.LengthBytes != correct.Length) return false; for (int i = 0; i < correct.Length; i++) if (buf.Data[i] != correct[i]) return false; return true; }
internal NetMessage(NetBuffer reuseBuffer) { m_buffer = reuseBuffer; }
/// <summary> /// Append a CRC checksum and encrypt data in place using XTEA /// </summary> internal void EncryptSymmetric(NetBuffer buffer) { //string plain = Convert.ToBase64String(buffer.Data, 0, buffer.LengthBytes); int bufferLen = buffer.LengthBytes; // calculate number of pad bits int dataBits = buffer.LengthBits; int bitsNeeded = dataBits + 24; // 24 extra for crc and num-pad-bits int totalBits = bitsNeeded + (64 - (bitsNeeded % 64)); int padBits = totalBits - bitsNeeded; // write to ensure zeroes in buffer (crc and num-pad-bits space) buffer.Write((uint)0, 24); if (padBits > 0) buffer.Write((ulong)0, padBits); int writePadBitsPosition = buffer.LengthBits - 8; // write crc ushort crc = Checksum.Adler16(buffer.Data, 0, buffer.LengthBytes); buffer.ResetWritePointer(dataBits); buffer.Write(crc); // write num-pad-bits in LAST byte buffer.ResetWritePointer(writePadBitsPosition); buffer.Write((byte)padBits); // encrypt in place int ptr = 0; bufferLen = buffer.LengthBytes; while (ptr < bufferLen) { m_xtea.EncryptBlock(buffer.Data, ptr, buffer.Data, ptr); ptr += 8; } return; }
internal NetMessage(NetMessageType tp, byte[] payload) { m_type = tp; m_buffer = new NetBuffer(payload); }
internal static NetMessage Decode(NetBuffer buffer) { try { NetMessage retval = new NetMessage(true); NetMessageType type = (NetMessageType)buffer.ReadByte(3); retval.m_type = type; switch (type) { case NetMessageType.None: // packet padding (due to encryption); we've reached the end return null; case NetMessageType.User: case NetMessageType.UserFragmented: case NetMessageType.Acknowledge: case NetMessageType.AcknowledgeBitField: retval.m_sequenceChannel = (NetChannel)buffer.ReadByte(5); retval.m_sequenceNumber = (ushort)buffer.ReadUInt32(12); break; } int msgLen = 0; switch (type) { case NetMessageType.User: case NetMessageType.UserFragmented: msgLen = (int)buffer.ReadUInt32(12); break; case NetMessageType.Acknowledge: msgLen = 0; break; case NetMessageType.AcknowledgeBitField: msgLen = 4; break; case NetMessageType.Discovery: msgLen = (int)buffer.ReadUInt32(12); break; case NetMessageType.Handshake: msgLen = (int)buffer.ReadUInt32(12); break; case NetMessageType.PingPong: msgLen = 3; break; } byte[] payload = null; if (msgLen > 0) payload = buffer.ReadBytes(msgLen); retval.m_buffer = new NetBuffer(payload); return retval; } catch { NetBase.CurrentContext.Log.Warning("Failed to decode NetMessage from buffer!"); return null; } }
internal override void HandlePacket(NetBuffer buffer, int bytesReceived, IPEndPoint senderEndpoint) { double now = NetTime.Now; NetConnection sender = FindConnection(senderEndpoint); if (sender != null) { sender.m_lastHeardFromRemote = now; if (sender.m_encryption.SymmetricEncryptionKeyBytes != null) { bool ok = sender.m_encryption.DecryptSymmetric(buffer); if (!ok) { Log.Warning("Failed to decrypt packet from client " + sender); return; } } } try { NetMessage response; int messagesReceived = 0; int usrMessagesReceived = 0; int ackMessagesReceived = 0; while (buffer.ReadBitsLeft > 7) { NetMessage msg = NetMessage.Decode(buffer); if (msg == null) break; // done messagesReceived++; msg.Sender = sender; switch (msg.m_type) { case NetMessageType.Acknowledge: case NetMessageType.AcknowledgeBitField: if (sender == null) { Log.Warning("Received Ack from not-connected source!"); } else { //Log.Debug("Received ack " + msg.SequenceChannel + "|" + msg.SequenceNumber); sender.ReceiveAcknowledge(msg); ackMessagesReceived++; } break; case NetMessageType.Handshake: NetHandshakeType tp = (NetHandshakeType)msg.ReadByte(); if (tp == NetHandshakeType.ConnectResponse) { Log.Warning("Received ConnectResponse?!"); } else if (tp == NetHandshakeType.Connect) { if (sender == null) { NetHandshake.HandleConnect(msg, this, senderEndpoint); } else { // resend response NetHandshake.SendConnectResponse(this, sender, senderEndpoint); Log.Verbose("Redundant Connect received; resending response"); } } else if (tp == NetHandshakeType.ConnectionEstablished) { if (sender == null) { Log.Warning("Received ConnectionEstablished, but no sender connection?!"); } else { float rt = (float)(now - sender.m_firstSentHandshake); sender.m_ping.Initialize(rt); ushort remoteValue = msg.ReadUInt16(); sender.RemoteClockOffset = NetTime.CalculateOffset(now, remoteValue, rt); Log.Verbose("Reinitializing remote clock offset to " + sender.RemoteClockOffset + " ms (roundtrip " + NetUtil.SecToMil(rt) + " ms)"); if (sender.Status == NetConnectionStatus.Connected) { Log.Verbose("Redundant ConnectionEstablished received"); } else { Log.Debug("Connection established"); sender.SetStatus(NetConnectionStatus.Connected, "Connected by established"); } } } else { // disconnected if (sender == null) { Log.Warning("Disconnected received from unconnected source: " + senderEndpoint); return; } string reason = msg.ReadString(); sender.Disconnected(reason); } break; case NetMessageType.Discovery: Log.Debug("Answering discovery response from " + senderEndpoint); response = NetDiscovery.EncodeResponse(this); SendSingleMessageAtOnce(response, null, senderEndpoint); break; case NetMessageType.PingPong: if (sender == null) return; bool isPong = msg.ReadBoolean(); bool isOptimizeInfo = msg.ReadBoolean(); if (isOptimizeInfo) { // DON'T handle optimizeinfo... only clients should adapt to server ping info } else if (isPong) { if (sender.Status == NetConnectionStatus.Connected) sender.m_ping.HandlePong(now, msg); } else { NetPing.ReplyPong(msg, sender); // send pong } break; case NetMessageType.User: case NetMessageType.UserFragmented: usrMessagesReceived++; if (sender == null) { Log.Warning("User message received from unconnected source: " + senderEndpoint); return; // don't handle user messages from unconnected sources } if (sender.Status == NetConnectionStatus.Connecting) sender.SetStatus(NetConnectionStatus.Connected, "Connected by user message"); sender.ReceiveMessage(now, msg); break; default: Log.Warning("Bad message type: " + msg); break; } } if (sender != null) { NetStatistics stats = sender.Statistics; stats.PacketsReceived++; stats.MessagesReceived += messagesReceived; stats.UserMessagesReceived += usrMessagesReceived; stats.AckMessagesReceived += ackMessagesReceived; stats.BytesReceived += bytesReceived; } } catch (Exception ex) { Log.Error("Failed to parse packet correctly; read/write mismatch? " + ex); } }
internal override void HandlePacket(NetBuffer buffer, int bytesReceived, IPEndPoint sender) { double now = NetTime.Now; try { NetMessage msg; if (m_serverConnection == null || m_serverConnection.Status == NetConnectionStatus.Disconnected) { // // unconnected packet // msg = NetMessage.Decode(buffer); if (msg == null) { Log.Warning("Malformed NetMessage?"); return; // malformed? } Log.Verbose("Received unconnected message: " + msg); // discovery response? if (msg.m_type == NetMessageType.Discovery) { NetServerInfo info = NetDiscovery.DecodeResponse(msg, sender); if (ServerDiscovered != null) ServerDiscovered(this, new NetServerDiscoveredEventArgs(info)); return; } if (m_serverConnection != null && sender.Equals(m_serverConnection.RemoteEndpoint)) { // we have m_serverConnection, but status is disconnected Log.Warning("Received " + buffer.LengthBytes + " from server; but we're disconnected!"); return; } Log.Warning("Received " + buffer.LengthBytes + " bytes from non-server source: " + sender + "(server is " + m_serverConnection.RemoteEndpoint + ")"); return; } // decrypt in place if (m_serverConnection.m_encryption.SymmetricEncryptionKeyBytes != null) { byte[] savedBuffer = null; if (m_serverConnection.Status == NetConnectionStatus.Connecting) { // special case; save buffer in case we get unencrypted response savedBuffer = new byte[buffer.LengthBytes]; Array.Copy(buffer.Data, 0, savedBuffer, 0, buffer.LengthBytes); } bool ok = m_serverConnection.m_encryption.DecryptSymmetric(buffer); if (!ok) { // failed to decrypt; drop this packet UNLESS we're in a connecting state, // in which case the server may want to tell us something if (m_serverConnection.Status == NetConnectionStatus.Connecting) { // ok let this one thru unencrypted Array.Copy(savedBuffer, buffer.Data, savedBuffer.Length); } else { Log.Warning("Failed to decrypt packet from server!"); return; } } } m_serverConnection.m_lastHeardFromRemote = now; int messagesReceived = 0; int usrMessagesReceived = 0; int ackMessagesReceived = 0; while (buffer.ReadBitsLeft > 7) { msg = NetMessage.Decode(buffer); if (msg == null) break; // done messagesReceived++; msg.Sender = m_serverConnection; switch (msg.m_type) { case NetMessageType.Handshake: NetHandshakeType tp = (NetHandshakeType)msg.ReadByte(); if (tp == NetHandshakeType.Connect || tp == NetHandshakeType.ConnectionEstablished) { Log.Warning("Client received " + tp + "?!"); } else if (tp == NetHandshakeType.ConnectResponse) { if (m_serverConnection.Status != NetConnectionStatus.Connecting) { Log.Verbose("Received redundant ConnectResponse!"); break; } // Log.Debug("ConnectResponse received"); m_serverConnection.SetStatus(NetConnectionStatus.Connected, "Connected"); Log.Info("Connected to " + m_serverConnection.RemoteEndpoint); // initialize ping to now - m_firstSentConnect float initRoundtrip = (float)(now - m_serverConnection.m_firstSentHandshake); m_serverConnection.m_ping.Initialize(initRoundtrip); ushort remoteValue = msg.ReadUInt16(); m_serverConnection.RemoteClockOffset = NetTime.CalculateOffset(now, remoteValue, initRoundtrip); Log.Verbose("Initializing remote clock offset to " + m_serverConnection.RemoteClockOffset + " ms (roundtrip " + NetUtil.SecToMil(initRoundtrip) + " ms)"); NetMessage established = new NetMessage(NetMessageType.Handshake, 3); established.Write((byte)NetHandshakeType.ConnectionEstablished); established.WriteSendStamp(); SendSingleMessageAtOnce(established, m_serverConnection, m_serverConnection.RemoteEndpoint); } else { // Disconnected string reason = msg.ReadString(); m_serverConnection.SetStatus(NetConnectionStatus.Disconnected, reason); } break; case NetMessageType.Acknowledge: //Log.Debug("Received ack " + msg.SequenceChannel + "|" + msg.SequenceNumber); m_serverConnection.ReceiveAcknowledge(msg); ackMessagesReceived++; break; case NetMessageType.PingPong: bool isPong = msg.ReadBoolean(); bool isOptimizeInfo = msg.ReadBoolean(); if (isOptimizeInfo) { m_serverConnection.m_ping.HandleOptimizeInfo(now, msg); } else if (isPong) { if (m_serverConnection.Status == NetConnectionStatus.Connected) m_serverConnection.m_ping.HandlePong(now, msg); } else { NetPing.ReplyPong(msg, m_serverConnection); } break; case NetMessageType.User: case NetMessageType.UserFragmented: //Log.Debug("User message received; " + msg.m_buffer.LengthBytes + " bytes"); m_serverConnection.ReceiveMessage(now, msg); usrMessagesReceived++; break; case NetMessageType.Discovery: NetServerInfo info = NetDiscovery.DecodeResponse(msg, sender); if (ServerDiscovered != null) ServerDiscovered(this, new NetServerDiscoveredEventArgs(info)); break; default: Log.Warning("Client received " + msg.m_type + "?!"); break; } } // add statistics NetStatistics stats = m_serverConnection.Statistics; stats.PacketsReceived++; stats.MessagesReceived += messagesReceived; stats.UserMessagesReceived += usrMessagesReceived; stats.AckMessagesReceived += ackMessagesReceived; stats.BytesReceived += bytesReceived; } catch (Exception ex) { Log.Error("Failed to parse packet correctly; read/write mismatch? " + ex); } }
internal abstract void HandlePacket(NetBuffer buffer, int bytesReceived, IPEndPoint sender);
internal static NetMessage Decode(NetBuffer buffer) { try { NetMessage retval = new NetMessage(true); NetMessageType type = (NetMessageType)buffer.ReadByte(3); retval.m_type = type; switch (type) { case NetMessageType.None: // packet padding (due to encryption); we've reached the end return(null); case NetMessageType.User: case NetMessageType.UserFragmented: case NetMessageType.Acknowledge: case NetMessageType.AcknowledgeBitField: retval.m_sequenceChannel = (NetChannel)buffer.ReadByte(5); retval.m_sequenceNumber = (ushort)buffer.ReadUInt32(12); break; } int msgLen = 0; switch (type) { case NetMessageType.User: case NetMessageType.UserFragmented: msgLen = (int)buffer.ReadUInt32(12); break; case NetMessageType.Acknowledge: msgLen = 0; break; case NetMessageType.AcknowledgeBitField: msgLen = 4; break; case NetMessageType.Discovery: msgLen = (int)buffer.ReadUInt32(12); break; case NetMessageType.Handshake: msgLen = (int)buffer.ReadUInt32(12); break; case NetMessageType.PingPong: msgLen = 3; break; } byte[] payload = null; if (msgLen > 0) { payload = buffer.ReadBytes(msgLen); } retval.m_buffer = new NetBuffer(payload); return(retval); } catch { NetBase.CurrentContext.Log.Warning("Failed to decode NetMessage from buffer!"); return(null); } }