| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 | using System;using UnityEngine;namespace Mirror{    // message packing all in one place, instead of constructing headers in all    // kinds of different places    //    //   MsgType     (2 bytes)    //   Content     (ContentSize bytes)    public static class MessagePacking    {        // message header size        public const int HeaderSize = sizeof(ushort);        // max message content size (without header) calculation for convenience        // -> Transport.GetMaxPacketSize is the raw maximum        // -> Every message gets serialized into <<id, content>>        // -> Every serialized message get put into a batch with a header        public static int MaxContentSize =>            Transport.activeTransport.GetMaxPacketSize()            - HeaderSize            - Batcher.HeaderSize;        public static ushort GetId<T>() where T : struct, NetworkMessage        {            // paul: 16 bits is enough to avoid collisions            //  - keeps the message size small            //  - in case of collisions,  Mirror will display an error            return (ushort)(typeof(T).FullName.GetStableHashCode() & 0xFFFF);        }        // pack message before sending        // -> NetworkWriter passed as arg so that we can use .ToArraySegment        //    and do an allocation free send before recycling it.        public static void Pack<T>(T message, NetworkWriter writer)            where T : struct, NetworkMessage        {            ushort msgType = GetId<T>();            writer.WriteUShort(msgType);            // serialize message into writer            writer.Write(message);        }        // unpack message after receiving        // -> pass NetworkReader so it's less strange if we create it in here        //    and pass it upwards.        // -> NetworkReader will point at content afterwards!        public static bool Unpack(NetworkReader messageReader, out ushort msgType)        {            // read message type            try            {                msgType = messageReader.ReadUShort();                return true;            }            catch (System.IO.EndOfStreamException)            {                msgType = 0;                return false;            }        }        internal static NetworkMessageDelegate WrapHandler<T, C>(Action<C, T> handler, bool requireAuthentication)            where T : struct, NetworkMessage            where C : NetworkConnection            => (conn, reader, channelId) =>        {            // protect against DOS attacks if attackers try to send invalid            // data packets to crash the server/client. there are a thousand            // ways to cause an exception in data handling:            // - invalid headers            // - invalid message ids            // - invalid data causing exceptions            // - negative ReadBytesAndSize prefixes            // - invalid utf8 strings            // - etc.            //            // let's catch them all and then disconnect that connection to avoid            // further attacks.            T message = default;            // record start position for NetworkDiagnostics because reader might contain multiple messages if using batching            int startPos = reader.Position;            try            {                if (requireAuthentication && !conn.isAuthenticated)                {                    // message requires authentication, but the connection was not authenticated                    Debug.LogWarning($"Closing connection: {conn}. Received message {typeof(T)} that required authentication, but the user has not authenticated yet");                    conn.Disconnect();                    return;                }                //Debug.Log($"ConnectionRecv {conn} msgType:{typeof(T)} content:{BitConverter.ToString(reader.buffer.Array, reader.buffer.Offset, reader.buffer.Count)}");                // if it is a value type, just use default(T)                // otherwise allocate a new instance                message = reader.Read<T>();            }            catch (Exception exception)            {                Debug.LogError($"Closed connection: {conn}. This can happen if the other side accidentally (or an attacker intentionally) sent invalid data. Reason: {exception}");                conn.Disconnect();                return;            }            finally            {                int endPos = reader.Position;                // TODO: Figure out the correct channel                NetworkDiagnostics.OnReceive(message, channelId, endPos - startPos);            }            // user handler exception should not stop the whole server            try            {                // user implemented handler                handler((C)conn, message);            }            catch (Exception e)            {                Debug.LogError($"Disconnecting connId={conn.connectionId} to prevent exploits from an Exception in MessageHandler: {e.GetType().Name} {e.Message}\n{e.StackTrace}");                conn.Disconnect();            }        };    }}
 |