// 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);
        }