Esempio n. 1
0
        public void NetTest6_SocketAddressBasic()
        {
            Random random = new();

            for (int i = 0; i <= 30; i++)
            {
                int[] IPInts =
                {
                    random.Next(256),
                    random.Next(256),
                    random.Next(256),
                    random.Next(128)
                };

                Debug.WriteLine($"Random IP {IPInts[0]}.{IPInts[1]}.{IPInts[2]}.{IPInts[3]}");

                IPAddress address = new(
                    IPInts[0]
                    + IPInts[1] * 256
                    + IPInts[2] * 256 * 256
                    + IPInts[3] * 256 * 256 * 256);

                int portInt = random.Next(65536);

                IPEndPoint ipEndpoint1 = new(address, portInt);

                SocketAddress socketAddress1 = ipEndpoint1.Serialize();
                SocketAddress socketAddress2 = ipEndpoint1.Serialize();

                Assert.NotNull(socketAddress1, "socketAddress1 is null");
                Assert.NotNull(socketAddress2, "socketAddress2 is null");

                Type typeOfSocketAddress = socketAddress1.GetType();
                Assert.IsType(typeOfSocketAddress, Type.GetType("System.Net.SocketAddress"), "socketAddress1 Type is incorrect");

                typeOfSocketAddress = socketAddress2.GetType();
                Assert.IsType(typeOfSocketAddress, Type.GetType("System.Net.SocketAddress"), "socketAddress2 Type is incorrect");

                Assert.Equal(socketAddress1.ToString(), socketAddress2.ToString(), "ToString returns differently for same data");

                Assert.Equal(socketAddress1.GetHashCode(), socketAddress2.GetHashCode(), $"GetHashCode returns differently for same data");
                Assert.True(socketAddress1.Family == AddressFamily.InterNetwork, "socketAddress1 Family is incorrect");
                Assert.True(socketAddress2.Family == AddressFamily.InterNetwork, "socketAddress2 Family is incorrect");

                // Recreate a different Socket
                socketAddress2 = new SocketAddress(AddressFamily.Chaos, 8);

                Assert.NotEqual(socketAddress1.GetHashCode(), socketAddress2.GetHashCode(), "GetHashCode returns same for "
                                + socketAddress1.ToString() + " " + socketAddress1.GetHashCode()
                                + " as " + socketAddress2.ToString() + " " + socketAddress2.GetHashCode());
            }
        }
Esempio n. 2
0
        public static void Equals_Compare_Success()
        {
            SocketAddress sa1 = new SocketAddress(AddressFamily.InterNetwork, 64);
            SocketAddress sa2 = new SocketAddress(AddressFamily.InterNetwork, 64);
            SocketAddress sa3 = new SocketAddress(AddressFamily.InterNetworkV6, 64);
            SocketAddress sa4 = new SocketAddress(AddressFamily.InterNetwork, 60000);

            Assert.False(sa1.Equals(null));
            Assert.False(sa1.Equals(""));

            Assert.Equal(sa1, sa2);
            Assert.Equal(sa2, sa1);
            Assert.Equal(sa1.GetHashCode(), sa2.GetHashCode());

            Assert.NotEqual(sa1, sa3);
            Assert.NotEqual(sa1.GetHashCode(), sa3.GetHashCode());

            Assert.NotEqual(sa1, sa4);
        }
Esempio n. 3
0
        protected override int ReceiveFrom(byte[] buffer, out int connectionHash)
        {
            // where-allocation nonalloc ReceiveFrom.
            int           read          = socket.ReceiveFrom_NonAlloc(buffer, 0, buffer.Length, SocketFlags.None, reusableClientEP);
            SocketAddress remoteAddress = reusableClientEP.temp;

            // where-allocation nonalloc GetHashCode
            connectionHash = remoteAddress.GetHashCode();
            return(read);
        }
Esempio n. 4
0
        public static void Equals_Compare_Success()
        {
            SocketAddress sa1 = new SocketAddress(AddressFamily.InterNetwork, 64);
            SocketAddress sa2 = new SocketAddress(AddressFamily.InterNetwork, 64);
            SocketAddress sa3 = new SocketAddress(AddressFamily.InterNetworkV6, 64);
            SocketAddress sa4 = new SocketAddress(AddressFamily.InterNetwork, 60000);

            Assert.False(sa1.Equals(null));
            Assert.False(sa1.Equals(""));

            Assert.Equal(sa1, sa2);
            Assert.Equal(sa2, sa1);
            Assert.Equal(sa1.GetHashCode(), sa2.GetHashCode());

            Assert.NotEqual(sa1, sa3);
            Assert.NotEqual(sa1.GetHashCode(), sa3.GetHashCode());

            Assert.NotEqual(sa1, sa4);
        }
Esempio n. 5
0
        public override EndPoint Create(SocketAddress socketAddress)
        {
            if (SocketAddress != socketAddress)
            {
                SocketAddress = socketAddress;

                unchecked
                {
                    SocketAddress[0] += 1;
                    SocketAddress[0] -= 1;
                }

                if (SocketAddress.GetHashCode() == 0)
                {
                    throw new Exception($"0x0000001 fatal error ):");
                }
            }
            if (IPEndPoint == null)
            {
                IPEndPoint = GetIPEndPoint();
            }
            return(IPEndPoint);
        }
 /// <summary>
 /// Returns hash for efficient lookup in list
 /// </summary>
 /// <returns></returns>
 public override int GetHashCode()
 {
     return(SocketAddress.GetHashCode() ^ CanonicalName.GetHashCode());
 }
 public override int GetHashCode()
 {
     return(socketAddress.GetHashCode());
 }
Esempio n. 8
0
        public MFTestResults NetTest6_SocketAddressBasic()
        {
            /// <summary>
            /// 1. Creates 30 Random IPs between 0.0.0.0 and 255.255.255.127
            /// 2. Verifies that they can be constructed as SocketAddress
            /// 3. Verifies that they have the correct data (GetAddressBytes)
            /// 4. Verifies ToString and GetHashcode
            /// </summary>
            ///
            bool testResult = true;

            try
            {
                Random random = new Random();
                for (int i = 0; i <= 30; i++)
                {
                    int[] IPInts = { random.Next(256), random.Next(256),
                                     random.Next(256), random.Next(128) };
                    Log.Comment("Random IP " + IPInts[0] + "." + IPInts[1]
                                + "." + IPInts[2] + "." + IPInts[3]);
                    IPAddress address = new IPAddress((long)(
                                                          IPInts[0]
                                                          + IPInts[1] * 256
                                                          + IPInts[2] * 256 * 256
                                                          + IPInts[3] * 256 * 256 * 256));
                    int           portInt        = random.Next(65536);
                    IPEndPoint    ipEndpoint1    = new IPEndPoint(address, portInt);
                    SocketAddress socketAddress1 = ipEndpoint1.Serialize();
                    SocketAddress socketAddress2 = ipEndpoint1.Serialize();
                    if (socketAddress1 == null)
                    {
                        throw new Exception("socketAddress1 is null");
                    }
                    if (socketAddress2 == null)
                    {
                        throw new Exception("socketAddress2 is null");
                    }

                    Type typeOfSocketAddress = socketAddress1.GetType();
                    if (typeOfSocketAddress != Type.GetType("System.Net.SocketAddress"))
                    {
                        throw new Exception("socketAddress1 Type is incorrect");
                    }
                    typeOfSocketAddress = socketAddress2.GetType();
                    if (typeOfSocketAddress != Type.GetType("System.Net.SocketAddress"))
                    {
                        throw new Exception("socketAddress2 Type is incorrect");
                    }

                    if (socketAddress1.ToString() != socketAddress2.ToString())
                    {
                        throw new Exception("ToString returns differently for same data");
                    }

                    //21295	GetHashCode returns differently for cloned data
                    if (socketAddress1.GetHashCode() != socketAddress2.GetHashCode())
                    {
                        throw new Exception("GetHashCode returns differently for same data");
                    }

                    if (socketAddress1.Family != AddressFamily.InterNetwork)
                    {
                        throw new Exception("socketAddress1 Family is incorrect");
                    }

                    if (socketAddress2.Family != AddressFamily.InterNetwork)
                    {
                        throw new Exception("socketAddress2 Family is incorrect");
                    }

                    /*
                     * Pending Resolution of 17428
                     *
                     * Log.Comment("Recreating socketAddress2 with new data");
                     * int portInt2 = portInt % 2 + 1;
                     * long addressLong2 = (long)(
                     *  (IPInts[0] % 2 + 1)
                     + (IPInts[1] % 2 + 1) * 256
                     + (IPInts[2] % 2 + 1) * 256 * 256
                     + (IPInts[3] % 2 + 1) * 256 * 256 * 256);
                     +
                     + IPEndPoint ipEndpoint2 = new IPEndPoint(addressLong2, portInt2);
                     + socketAddress2 = ipEndpoint2.Serialize();
                     + socketAddress2.Family = AddressFamily.Chaos;
                     */
                    socketAddress2 = new SocketAddress(AddressFamily.Chaos, 8);
                    if (socketAddress1.GetHashCode() == socketAddress2.GetHashCode())
                    {
                        throw new Exception("GetHashCode returns same for "
                                            + socketAddress1.ToString()
                                            + " as " + socketAddress2.ToString());
                    }

                    if (socketAddress1.ToString() == socketAddress2.ToString())
                    {
                        throw new Exception("ToString returns same for different data");
                    }
                }
            }
            catch (Exception e)
            {
                Log.Comment("Caught exception: " + e.Message);
                testResult = false;
            }
            return(testResult ? MFTestResults.Pass : MFTestResults.KnownFailure);
        }
        // Create doesn't need to create anything.
        // SocketAddress object is already the one we returned in Serialize().
        // ReceiveFrom_Internal simply wrote into it.
        public override EndPoint Create(SocketAddress socketAddress)
        {
            // original IPEndPoint.Create validates:
            if (socketAddress.Family != AddressFamily)
            {
                throw new ArgumentException($"Unsupported socketAddress.AddressFamily: {socketAddress.Family}. Expected: {AddressFamily}");
            }
            if (socketAddress.Size < 8)
            {
                throw new ArgumentException($"Unsupported socketAddress.Size: {socketAddress.Size}. Expected: <8");
            }

            // double check to guarantee that ReceiveFrom actually did write
            // into our 'temp' field. just in case that's ever changed.
            if (socketAddress != temp)
            {
                // well this is fun.
                // in the latest mono from the above github links,
                // the result of Serialize() is passed as 'ref' so ReceiveFrom
                // does in fact write into it.
                //
                // in Unity 2019 LTS's mono version, it does create a new one
                // each time. this is from ILSpy Receive_From:
                //
                //     SocketPal.CheckDualModeReceiveSupport(this);
                //     ValidateBlockingMode();
                //     if (NetEventSource.IsEnabled)
                //     {
                //         NetEventSource.Info(this, $"SRC{LocalEndPoint} size:{size} remoteEP:{remoteEP}", "ReceiveFrom");
                //     }
                //     EndPoint remoteEP2 = remoteEP;
                //     System.Net.Internals.SocketAddress socketAddress = SnapshotAndSerialize(ref remoteEP2);
                //     System.Net.Internals.SocketAddress socketAddress2 = IPEndPointExtensions.Serialize(remoteEP2);
                //     int bytesTransferred;
                //     SocketError socketError = SocketPal.ReceiveFrom(_handle, buffer, offset, size, socketFlags, socketAddress.Buffer, ref socketAddress.InternalSize, out bytesTransferred);
                //     SocketException ex = null;
                //     if (socketError != 0)
                //     {
                //         ex = new SocketException((int)socketError);
                //         UpdateStatusAfterSocketError(ex);
                //         if (NetEventSource.IsEnabled)
                //         {
                //             NetEventSource.Error(this, ex, "ReceiveFrom");
                //         }
                //         if (ex.SocketErrorCode != SocketError.MessageSize)
                //         {
                //             throw ex;
                //         }
                //     }
                //     if (!socketAddress2.Equals(socketAddress))
                //     {
                //         try
                //         {
                //             remoteEP = remoteEP2.Create(socketAddress);
                //         }
                //         catch
                //         {
                //         }
                //         if (_rightEndPoint == null)
                //         {
                //             _rightEndPoint = remoteEP2;
                //         }
                //     }
                //     if (ex != null)
                //     {
                //         throw ex;
                //     }
                //     if (NetEventSource.IsEnabled)
                //     {
                //         NetEventSource.DumpBuffer(this, buffer, offset, size, "ReceiveFrom");
                //         NetEventSource.Exit(this, bytesTransferred, "ReceiveFrom");
                //     }
                //     return bytesTransferred;
                //

                // so until they upgrade their mono version, we are stuck with
                // some allocations.
                //
                // for now, let's pass the newly created on to our temp so at
                // least we reuse it next time.
                temp = socketAddress;

                // SocketAddress.GetHashCode() depends on SocketAddress.m_changed.
                // ReceiveFrom only sets the buffer, it does not seem to set m_changed.
                // we need to reset m_changed for two reasons:
                // * if m_changed is false, GetHashCode() returns the cahced m_hash
                //   which is '0'. that would be a problem.
                //   https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/SocketAddress.cs#L262
                // * if we have a cached m_hash, but ReceiveFrom modified the buffer
                //   then the GetHashCode() should change too. so we need to reset
                //   either way.
                //
                // the only way to do that is by _actually_ modifying the buffer:
                // https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/SocketAddress.cs#L99
                // so let's do that.
                // -> unchecked in case it's byte.Max
                unchecked
                {
                    temp[0] += 1;
                    temp[0] -= 1;
                }

                // make sure this worked.
                // at least throw an Exception to make it obvious if the trick does
                // not work anymore, in case ReceiveFrom is ever changed.
                if (temp.GetHashCode() == 0)
                {
                    throw new Exception($"SocketAddress GetHashCode() is 0 after ReceiveFrom. Does the m_changed trick not work anymore?");
                }

                // in the future, enable this again:
                //throw new Exception($"Socket.ReceiveFrom(): passed SocketAddress={socketAddress} but expected {temp}. This should never happen. Did ReceiveFrom() change?");
            }

            // ReceiveFrom sets seed_endpoint to the result of Create():
            // https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1764
            // so let's return ourselves at least.
            // (seed_endpoint only seems to matter for BeginSend etc.)
            return(this);
        }
 // we need to overwrite GetHashCode() for two reasons.
 // https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPEndPoint.cs#L160
 // * it uses m_Address. but our true SocketAddress is in m_temp.
 //   m_Address might not be set at all.
 // * m_Address.GetHashCode() allocates:
 //   https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699
 public override int GetHashCode() => temp.GetHashCode();
        public void TickIncoming()
        {
            while (socket != null && socket.Poll(0, SelectMode.SelectRead))
            {
                try
                {
                    // NOTE: ReceiveFrom allocates.
                    //   we pass our IPEndPoint to ReceiveFrom.
                    //   receive from calls newClientEP.Create(socketAddr).
                    //   IPEndPoint.Create always returns a new IPEndPoint.
                    //   https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761
                    //int msgLength = socket.ReceiveFrom(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, ref newClientEP);
                    //Log.Info($"KCP: server raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}");

                    // where-allocation nonalloc ReceiveFrom.
                    int           msgLength     = socket.ReceiveFrom_NonAlloc(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, reusableClientEP);
                    SocketAddress remoteAddress = reusableClientEP.temp;

                    // calculate connectionId from endpoint
                    // NOTE: IPEndPoint.GetHashCode() allocates.
                    //  it calls m_Address.GetHashCode().
                    //  m_Address is an IPAddress.
                    //  GetHashCode() allocates for IPv6:
                    //  https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699
                    //
                    // => using only newClientEP.Port wouldn't work, because
                    //    different connections can have the same port.
                    //int connectionId = newClientEP.GetHashCode();

                    // where-allocation nonalloc GetHashCode
                    int connectionId = remoteAddress.GetHashCode();

                    // IMPORTANT: detect if buffer was too small for the received
                    //            msgLength. otherwise the excess data would be
                    //            silently lost.
                    //            (see ReceiveFrom documentation)
                    if (msgLength <= rawReceiveBuffer.Length)
                    {
                        // is this a new connection?
                        if (!connections.TryGetValue(connectionId, out KcpServerConnection connection))
                        {
                            // IPEndPointNonAlloc is reused all the time.
                            // we can't store that as the connection's endpoint.
                            // we need a new copy!
                            IPEndPoint newClientEP = reusableClientEP.DeepCopyIPEndPoint();

                            // for allocation free sending, we also need another
                            // IPEndPointNonAlloc...
                            IPEndPointNonAlloc reusableSendEP = new IPEndPointNonAlloc(newClientEP.Address, newClientEP.Port);

                            // create a new KcpConnection
                            // -> where-allocation IPEndPointNonAlloc is reused.
                            //    need to create a new one from the temp address.
                            connection = new KcpServerConnection(socket, newClientEP, reusableSendEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);

                            // DO NOT add to connections yet. only if the first message
                            // is actually the kcp handshake. otherwise it's either:
                            // * random data from the internet
                            // * or from a client connection that we just disconnected
                            //   but that hasn't realized it yet, still sending data
                            //   from last session that we should absolutely ignore.
                            //
                            //
                            // TODO this allocates a new KcpConnection for each new
                            // internet connection. not ideal, but C# UDP Receive
                            // already allocated anyway.
                            //
                            // expecting a MAGIC byte[] would work, but sending the raw
                            // UDP message without kcp's reliability will have low
                            // probability of being received.
                            //
                            // for now, this is fine.

                            // setup authenticated event that also adds to connections
                            connection.OnAuthenticated = () =>
                            {
                                // only send handshake to client AFTER we received his
                                // handshake in OnAuthenticated.
                                // we don't want to reply to random internet messages
                                // with handshakes each time.
                                connection.SendHandshake();

                                // add to connections dict after being authenticated.
                                connections.Add(connectionId, connection);
                                Log.Info($"KCP: server added connection({connectionId}): {newClientEP}");

                                // setup Data + Disconnected events only AFTER the
                                // handshake. we don't want to fire OnServerDisconnected
                                // every time we receive invalid random data from the
                                // internet.

                                // setup data event
                                connection.OnData = (message) =>
                                {
                                    // call mirror event
                                    //Log.Info($"KCP: OnServerDataReceived({connectionId}, {BitConverter.ToString(message.Array, message.Offset, message.Count)})");
                                    OnData.Invoke(connectionId, message);
                                };

                                // setup disconnected event
                                connection.OnDisconnected = () =>
                                {
                                    // flag for removal
                                    // (can't remove directly because connection is updated
                                    //  and event is called while iterating all connections)
                                    connectionsToRemove.Add(connectionId);

                                    // call mirror event
                                    Log.Info($"KCP: OnServerDisconnected({connectionId})");
                                    OnDisconnected.Invoke(connectionId);
                                };

                                // finally, call mirror OnConnected event
                                Log.Info($"KCP: OnServerConnected({connectionId})");
                                OnConnected.Invoke(connectionId);
                            };

                            // now input the message & process received ones
                            // connected event was set up.
                            // tick will process the first message and adds the
                            // connection if it was the handshake.
                            connection.RawInput(rawReceiveBuffer, msgLength);
                            connection.TickIncoming();

                            // again, do not add to connections.
                            // if the first message wasn't the kcp handshake then
                            // connection will simply be garbage collected.
                        }
                        // existing connection: simply input the message into kcp
                        else
                        {
                            connection.RawInput(rawReceiveBuffer, msgLength);
                        }
                    }
                    else
                    {
                        Log.Error($"KCP Server: message of size {msgLength} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting connectionId={connectionId}.");
                        Disconnect(connectionId);
                    }
                }
                // this is fine, the socket might have been closed in the other end
                catch (SocketException) {}
            }

            // process inputs for all server connections
            // (even if we didn't receive anything. need to tick ping etc.)
            foreach (KcpServerConnection connection in connections.Values)
            {
                connection.TickIncoming();
            }

            // remove disconnected connections
            // (can't do it in connection.OnDisconnected because Tick is called
            //  while iterating connections)
            foreach (int connectionId in connectionsToRemove)
            {
                connections.Remove(connectionId);
            }
            connectionsToRemove.Clear();
        }
Esempio n. 12
0
 public override int GetHashCode() => SocketAddress.GetHashCode();