示例#1
0
        // sends a connection challenge packet to the endpoint
        private void sendConnectionChallenge(NetcodePrivateConnectToken connectToken, EndPoint endpoint)
        {
            log("Sending connection challenge", NetcodeLogLevel.Debug);

            var challengeToken = new NetcodeChallengeToken();

            challengeToken.ClientID = connectToken.ClientID;
            challengeToken.UserData = connectToken.UserData;

            ulong challengeSequence = nextChallengeSequenceNumber++;

            byte[] tokenBytes = BufferPool.GetBuffer(300);
            using (var tokenWriter = ByteArrayReaderWriter.Get(tokenBytes))
                challengeToken.Write(tokenWriter);

            byte[] encryptedToken = BufferPool.GetBuffer(300);
            int    encryptedTokenBytes;

            try
            {
                encryptedTokenBytes = PacketIO.EncryptChallengeToken(challengeSequence, tokenBytes, challengeKey, encryptedToken);
            }
            catch
            {
                BufferPool.ReturnBuffer(tokenBytes);
                BufferPool.ReturnBuffer(encryptedToken);
                return;
            }

            var challengePacket = new NetcodeConnectionChallengeResponsePacket();

            challengePacket.ChallengeTokenSequence = challengeSequence;
            challengePacket.ChallengeTokenBytes    = encryptedToken;

            var cryptIdx = encryptionManager.FindEncryptionMapping(endpoint, time);

            if (cryptIdx == -1)
            {
                return;
            }

            var cryptKey = encryptionManager.GetSendKey(cryptIdx);

            serializePacket(new NetcodePacketHeader()
            {
                PacketType = NetcodePacketType.ConnectionChallenge
            }, (writer) =>
            {
                challengePacket.Write(writer);
            }, endpoint, cryptKey);

            BufferPool.ReturnBuffer(tokenBytes);
            BufferPool.ReturnBuffer(encryptedToken);
        }
示例#2
0
        // process an incoming connection request packet
        private void processConnectionRequest(ByteArrayReaderWriter reader, int size, EndPoint sender)
        {
            log("Got connection request", NetcodeLogLevel.Debug);

            var connectionRequestPacket = new NetcodeConnectionRequestPacket();

            if (!connectionRequestPacket.Read(reader, size - (int)reader.ReadPosition, protocolID))
            {
                log("Failed to read request", NetcodeLogLevel.Debug);
                return;
            }

            // expiration timestamp should be greater than current timestamp
            if (connectionRequestPacket.Expiration <= (ulong)Math.Truncate(time))
            {
                log("Connect token expired", NetcodeLogLevel.Debug);
                connectionRequestPacket.Release();
                return;
            }

            var privateConnectToken = new NetcodePrivateConnectToken();

            if (!privateConnectToken.Read(connectionRequestPacket.ConnectTokenBytes, privateKey, protocolID, connectionRequestPacket.Expiration, connectionRequestPacket.TokenSequenceNum))
            {
                log("Failed to read private token", NetcodeLogLevel.Debug);
                connectionRequestPacket.Release();
                return;
            }

            // if this server's public IP is not in the list of endpoints, packet is not valid
            bool serverAddressInEndpoints = privateConnectToken.ConnectServers.Any(x => x.Endpoint.CompareEndpoint(this.listenEndpoint, this.Port));

            if (!serverAddressInEndpoints)
            {
                log("Server address not listen in token", NetcodeLogLevel.Debug);
                return;
            }

            // if a client from packet source IP / port is already connected, ignore the packet
            if (clientSlots.Any(x => x != null && x.RemoteEndpoint.Equals(sender)))
            {
                log("Client {0} already connected", NetcodeLogLevel.Debug, sender.ToString());
                return;
            }

            // if a client with the same id as the connect token is already connected, ignore the packet
            if (clientSlots.Any(x => x != null && x.ClientID == privateConnectToken.ClientID))
            {
                log("Client ID {0} already connected", NetcodeLogLevel.Debug, privateConnectToken.ClientID);
                return;
            }

            // if the connect token has already been used by a different endpoint, ignore the packet
            // otherwise, add the token hmac and endpoint to the used token history
            // compares the last 16 bytes (token mac)
            byte[] token_mac = BufferPool.GetBuffer(Defines.MAC_SIZE);
            System.Array.Copy(connectionRequestPacket.ConnectTokenBytes, Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - Defines.MAC_SIZE, token_mac, 0, Defines.MAC_SIZE);
            if (!findOrAddConnectToken(sender, token_mac, time))
            {
                log("Token already used", NetcodeLogLevel.Debug);
                BufferPool.ReturnBuffer(token_mac);
                return;
            }

            BufferPool.ReturnBuffer(token_mac);

            // if we have no slots, we need to respond with a connection denied packet
            var nextSlot = getFreeClientSlot();

            if (nextSlot == -1)
            {
                denyConnection(sender, privateConnectToken.ServerToClientKey);
                log("Server is full, denying connection", NetcodeLogLevel.Info);
                return;
            }

            // add encryption mapping for this endpoint as well as timeout
            // packets received from this endpoint are to be decrypted with the client-to-server key
            // packets sent to this endpoint are to be encrypted with the server-to-client key
            // if no messages are received within timeout from this endpoint, it is disconnected (unless timeout is negative)
            if (!encryptionManager.AddEncryptionMapping(sender,
                                                        privateConnectToken.ServerToClientKey,
                                                        privateConnectToken.ClientToServerKey,
                                                        time,
                                                        time + 30,
                                                        privateConnectToken.TimeoutSeconds,
                                                        0))
            {
                log("Failed to add encryption mapping", NetcodeLogLevel.Error);
                return;
            }

            // finally, send a connection challenge packet
            sendConnectionChallenge(privateConnectToken, sender);
        }
        internal byte[] GenerateConnectToken(IPEndPoint[] addressList, double time, int expirySeconds, int serverTimeout, ulong sequence, ulong clientID, byte[] userData)
        {
            if (userData.Length > 256)
            {
                throw new ArgumentOutOfRangeException("User data cannot be larger than 256 bytes");
            }

            if (addressList == null)
            {
                throw new NullReferenceException("Address list cannot be null");
            }
            else if (addressList.Length == 0)
            {
                throw new ArgumentOutOfRangeException("Address list cannot be empty");
            }
            else if (addressList.Length > Defines.MAX_SERVER_ADDRESSES)
            {
                throw new ArgumentOutOfRangeException("Address list cannot contain more than " + Defines.MAX_SERVER_ADDRESSES + " entries");
            }

            NetcodePrivateConnectToken privateConnectToken = new NetcodePrivateConnectToken();

            privateConnectToken.ClientID       = clientID;
            privateConnectToken.TimeoutSeconds = serverTimeout;

            // generate random crypto keys
            byte[] clientToServerKey = new byte[32];
            byte[] serverToClientKey = new byte[32];
            KeyUtils.GenerateKey(clientToServerKey);
            KeyUtils.GenerateKey(serverToClientKey);

            privateConnectToken.ClientToServerKey = clientToServerKey;
            privateConnectToken.ServerToClientKey = serverToClientKey;
            privateConnectToken.UserData          = new byte[256];

            Buffer.BlockCopy(userData, 0, privateConnectToken.UserData, 0, userData.Length);

            privateConnectToken.ConnectServers = new ConnectTokenServerEntry[addressList.Length];
            for (int i = 0; i < privateConnectToken.ConnectServers.Length; i++)
            {
                privateConnectToken.ConnectServers[i] = new ConnectTokenServerEntry()
                {
                    AddressType = addressList[i].AddressFamily == AddressFamily.InterNetwork ? NetcodeAddressType.IPv4 : NetcodeAddressType.IPv6,
                    Endpoint    = addressList[i]
                };
            }

            byte[] privateConnectTokenBytes = new byte[1024];
            using (var writer = ByteArrayReaderWriter.Get(privateConnectTokenBytes))
            {
                privateConnectToken.Write(writer);
            }

            ulong createTimestamp = (ulong)Math.Truncate(time);
            ulong expireTimestamp = expirySeconds >= 0 ? (createTimestamp + (ulong)expirySeconds) : 0xFFFFFFFFFFFFFFFFUL;

            byte[] encryptedPrivateToken = new byte[1024];
            PacketIO.EncryptPrivateConnectToken(privateConnectTokenBytes, protocolID, expireTimestamp, sequence, privateKey, encryptedPrivateToken);

            NetcodePublicConnectToken publicToken = new NetcodePublicConnectToken();

            publicToken.ProtocolID               = protocolID;
            publicToken.CreateTimestamp          = createTimestamp;
            publicToken.ExpireTimestamp          = expireTimestamp;
            publicToken.ConnectTokenSequence     = sequence;
            publicToken.PrivateConnectTokenBytes = encryptedPrivateToken;
            publicToken.ConnectServers           = privateConnectToken.ConnectServers;
            publicToken.ClientToServerKey        = clientToServerKey;
            publicToken.ServerToClientKey        = serverToClientKey;
            publicToken.TimeoutSeconds           = serverTimeout;

            byte[] publicTokenBytes = new byte[2048];
            using (var writer = ByteArrayReaderWriter.Get(publicTokenBytes))
            {
                publicToken.Write(writer);
            }

            return(publicTokenBytes);
        }