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