// encrypts a packet and sends it to the endpoint private void sendPacketToClient(NetcodePacketHeader packetHeader, byte[] packetData, int packetDataLen, EndPoint endpoint, byte[] key) { // assign a sequence number to this packet packetHeader.SequenceNumber = this.nextSequenceNumber++; // encrypt packet data byte[] encryptedPacketBuffer = BufferPool.GetBuffer(2048); int encryptedBytes = PacketIO.EncryptPacketData(packetHeader, protocolID, packetData, packetDataLen, key, encryptedPacketBuffer); int packetLen = 0; // write packet to byte array var packetBuffer = BufferPool.GetBuffer(2048); using (var packetWriter = ByteArrayReaderWriter.Get(packetBuffer)) { packetHeader.Write(packetWriter); packetWriter.WriteBuffer(encryptedPacketBuffer, encryptedBytes); packetLen = (int)packetWriter.WritePosition; } // send packet listenSocket.SendTo(packetBuffer, packetLen, endpoint); BufferPool.ReturnBuffer(packetBuffer); BufferPool.ReturnBuffer(encryptedPacketBuffer); }
private void processConnectionKeepAlive(NetcodePacketHeader header, int length, ByteArrayReaderWriter stream) { if (checkReplay(header)) { return; } var decryptKey = serverToClientKey; var keepAlive = new NetcodeKeepAlivePacket() { Header = header }; if (!keepAlive.Read(stream, length, decryptKey, connectToken.ProtocolID)) { return; } if (this.state == ClientState.Connected || this.state == ClientState.SendingChallengeResponse) { lastResponseTime = time; } if (this.state == ClientState.SendingChallengeResponse) { this.clientIndex = keepAlive.ClientIndex; this.maxSlots = keepAlive.MaxSlots; changeState(ClientState.Connected); } }
private void processConnectionPayload(NetcodePacketHeader header, int length, ByteArrayReaderWriter stream) { if (checkReplay(header)) { return; } if (this.state != ClientState.Connected) { return; } var decryptKey = serverToClientKey; var payloadPacket = new NetcodePayloadPacket() { Header = header }; if (!payloadPacket.Read(stream, length, decryptKey, connectToken.ProtocolID)) { return; } lastResponseTime = time; if (OnMessageReceived != null) { OnMessageReceived(payloadPacket.Payload, payloadPacket.Length); } payloadPacket.Release(); }
private void serializePacket(NetcodePacketHeader packetHeader, Action <ByteArrayReaderWriter> write, EndPoint endpoint, byte[] key) { byte[] tempPacket = BufferPool.GetBuffer(2048); int writeLen = 0; using (var writer = ByteArrayReaderWriter.Get(tempPacket)) { write(writer); writeLen = (int)writer.WritePosition; } sendPacketToClient(packetHeader, tempPacket, writeLen, endpoint, key); BufferPool.ReturnBuffer(tempPacket); }
private void processConnectionDenied(NetcodePacketHeader header, int length, ByteArrayReaderWriter stream) { var decryptKey = serverToClientKey; var denyPacket = new NetcodeDenyConnectionPacket() { Header = header }; if (!denyPacket.Read(stream, length, decryptKey, connectToken.ProtocolID)) { return; } pendingDisconnectState = ClientState.ConnectionDenied; // move onto the next server timer = 0.0; connectionTimer = 0.0; connectionMoveNextEndpoint(); }
// check the packet against the client's replay protection, returning true if packet was replayed, false otherwise private bool checkReplay(NetcodePacketHeader header, EndPoint sender) { var cryptIdx = encryptionManager.FindEncryptionMapping(sender, time); if (cryptIdx == -1) { log("Replay protection failed to find encryption mapping", NetcodeLogLevel.Debug); return(true); } var clientIndex = encryptionManager.GetClientID(cryptIdx); var client = clientSlots[clientIndex]; if (client == null) { log("Replay protection failed to find client", NetcodeLogLevel.Debug); return(true); } return(client.replayProtection.AlreadyReceived(header.SequenceNumber)); }
// process an incoming payload private void processConnectionPayload(ByteArrayReaderWriter reader, NetcodePacketHeader header, int size, EndPoint sender) { if (checkReplay(header, sender)) { return; } // encryption mapping was not registered, so don't bother int cryptIdx = encryptionManager.FindEncryptionMapping(sender, time); if (cryptIdx == -1) { log("No crytpo key for sender", NetcodeLogLevel.Debug); return; } // grab the decryption key and decrypt the packet var decryptKey = encryptionManager.GetReceiveKey(cryptIdx); var payloadPacket = new NetcodePayloadPacket() { Header = header }; if (!payloadPacket.Read(reader, size - (int)reader.ReadPosition, decryptKey, protocolID)) { return; } var clientIndex = encryptionManager.GetClientID(cryptIdx); var client = clientSlots[clientIndex]; // trigger callback if (OnClientMessageReceived != null) { OnClientMessageReceived(client, payloadPacket.Payload, payloadPacket.Length); } payloadPacket.Release(); }
// process a received datagram private void processDatagram(byte[] payload, int size, EndPoint sender) { using (var reader = ByteArrayReaderWriter.Get(payload)) { NetcodePacketHeader packetHeader = new NetcodePacketHeader(); packetHeader.Read(reader); if (packetHeader.PacketType == NetcodePacketType.ConnectionRequest) { if (!debugIgnoreConnectionRequest) { processConnectionRequest(reader, size, sender); } } else { switch (packetHeader.PacketType) { case NetcodePacketType.ChallengeResponse: if (!debugIgnoreChallengeResponse) { processConnectionResponse(reader, packetHeader, size, sender); } break; case NetcodePacketType.ConnectionKeepAlive: processConnectionKeepAlive(reader, packetHeader, size, sender); break; case NetcodePacketType.ConnectionPayload: processConnectionPayload(reader, packetHeader, size, sender); break; case NetcodePacketType.ConnectionDisconnect: processConnectionDisconnect(reader, packetHeader, size, sender); break; } } } }
private void processDatagram(Datagram datagram) { if (!MiscUtils.AddressEqual(datagram.sender, currentServerEndpoint)) { return; } using (var reader = ByteArrayReaderWriter.Get(datagram.payload)) { NetcodePacketHeader packetHeader = new NetcodePacketHeader(); packetHeader.Read(reader); int length = datagram.payloadSize - (int)reader.ReadPosition; switch (packetHeader.PacketType) { case NetcodePacketType.ConnectionChallenge: processChallengePacket(packetHeader, length, reader); break; case NetcodePacketType.ConnectionDenied: processConnectionDenied(packetHeader, length, reader); break; case NetcodePacketType.ConnectionKeepAlive: processConnectionKeepAlive(packetHeader, length, reader); break; case NetcodePacketType.ConnectionPayload: processConnectionPayload(packetHeader, length, reader); break; case NetcodePacketType.ConnectionDisconnect: processConnectionDisconnect(packetHeader, length, reader); break; } } }
private void processChallengePacket(NetcodePacketHeader header, int length, ByteArrayReaderWriter stream) { var decryptKey = serverToClientKey; var challengePacket = new NetcodeConnectionChallengeResponsePacket() { Header = header }; if (!challengePacket.Read(stream, length, decryptKey, connectToken.ProtocolID)) { return; } if (state == ClientState.SendingConnectionRequest) { this.challengeResponse = challengePacket; this.challengeResponse.Header.PacketType = NetcodePacketType.ChallengeResponse; // move onto sending challenge response timer = 0.0; connectionTimer = 0.0; changeState(ClientState.SendingChallengeResponse); } }
private void processConnectionDisconnect(NetcodePacketHeader header, int length, ByteArrayReaderWriter stream) { if (checkReplay(header)) { return; } if (this.state != ClientState.Connected) { return; } var decryptKey = serverToClientKey; var disconnectPacket = new NetcodeDisconnectPacket() { Header = header }; if (!disconnectPacket.Read(stream, length, decryptKey, connectToken.ProtocolID)) { return; } Disconnect(); }
// process an incoming connection response packet private void processConnectionResponse(ByteArrayReaderWriter reader, NetcodePacketHeader header, int size, EndPoint sender) { log("Got connection response", NetcodeLogLevel.Debug); // encryption mapping was not registered, so don't bother int cryptIdx = encryptionManager.FindEncryptionMapping(sender, time); if (cryptIdx == -1) { log("No crytpo key for sender", NetcodeLogLevel.Debug); return; } // grab the decryption key and decrypt the packet var decryptKey = encryptionManager.GetReceiveKey(cryptIdx); var connectionResponsePacket = new NetcodeConnectionChallengeResponsePacket() { Header = header }; if (!connectionResponsePacket.Read(reader, size - (int)reader.ReadPosition, decryptKey, protocolID)) { log("Failed to decrypt packet", NetcodeLogLevel.Debug); return; } var challengeToken = new NetcodeChallengeToken(); if (!challengeToken.Read(connectionResponsePacket.ChallengeTokenBytes, connectionResponsePacket.ChallengeTokenSequence, challengeKey)) { log("Failed to read challenge token", NetcodeLogLevel.Debug); connectionResponsePacket.Release(); 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 is already connected, ignore the packet if (clientSlots.Any(x => x != null && x.ClientID == challengeToken.ClientID)) { log("Client ID {0} already connected", NetcodeLogLevel.Debug, challengeToken.ClientID); return; } // if the server is full, deny the connection int nextSlot = getFreeClientSlot(); if (nextSlot == -1) { log("Server full, denying connection", NetcodeLogLevel.Info); denyConnection(sender, encryptionManager.GetSendKey(cryptIdx)); return; } // assign the endpoint and client ID to a free client slot and set connected to true RemoteClient client = new RemoteClient(this); client.ClientID = challengeToken.ClientID; client.RemoteEndpoint = sender; client.Connected = true; client.replayProtection = new NetcodeReplayProtection(); // assign timeout to client client.timeoutSeconds = encryptionManager.GetTimeoutSeconds(cryptIdx); // assign client to a free slot client.ClientIndex = (uint)nextSlot; this.clientSlots[nextSlot] = client; encryptionManager.SetClientID(cryptIdx, client.ClientIndex); // copy user data so application can make use of it, and set confirmed to false client.UserData = challengeToken.UserData; client.Confirmed = false; client.Touch(time); // respond with a connection keep alive packet sendKeepAlive(client); }
// process an incoming connection keep alive packet private void processConnectionKeepAlive(ByteArrayReaderWriter reader, NetcodePacketHeader header, int size, EndPoint sender) { if (checkReplay(header, sender)) { log("Detected replay in keep-alive", NetcodeLogLevel.Debug); return; } // encryption mapping was not registered, so don't bother int cryptIdx = encryptionManager.FindEncryptionMapping(sender, time); if (cryptIdx == -1) { log("No crytpo key for sender", NetcodeLogLevel.Debug); return; } // grab the decryption key and decrypt the packet var decryptKey = encryptionManager.GetReceiveKey(cryptIdx); var keepAlivePacket = new NetcodeKeepAlivePacket() { Header = header }; if (!keepAlivePacket.Read(reader, size - (int)reader.ReadPosition, decryptKey, protocolID)) { log("Failed to decrypt", NetcodeLogLevel.Debug); return; } if (keepAlivePacket.ClientIndex >= maxSlots) { log("Invalid client index", NetcodeLogLevel.Debug); return; } var client = this.clientSlots[(int)keepAlivePacket.ClientIndex]; if (client == null) { log("Failed to find client for endpoint", NetcodeLogLevel.Debug); return; } if (!client.RemoteEndpoint.Equals(sender)) { log("Client does not match sender", NetcodeLogLevel.Debug); return; } if (!client.Confirmed) { // trigger callback if (OnClientConnected != null) { OnClientConnected(client); } log("Client {0} connected", NetcodeLogLevel.Info, client.RemoteEndpoint); } client.Confirmed = true; client.Touch(time); int idx = encryptionManager.FindEncryptionMapping(client.RemoteEndpoint, time); encryptionManager.Touch(idx, client.RemoteEndpoint, time); }
// process an incoming disconnect message private void processConnectionDisconnect(ByteArrayReaderWriter reader, NetcodePacketHeader header, int size, EndPoint sender) { if (checkReplay(header, sender)) { return; } // encryption mapping was not registered, so don't bother int cryptIdx = encryptionManager.FindEncryptionMapping(sender, time); if (cryptIdx == -1) { log("No crytpo key for sender", NetcodeLogLevel.Debug); return; } var decryptKey = encryptionManager.GetReceiveKey(cryptIdx); var disconnectPacket = new NetcodeDisconnectPacket() { Header = header }; if (!disconnectPacket.Read(reader, size - (int)reader.ReadPosition, decryptKey, protocolID)) { return; } // locate the client by endpoint and free their slot var clientIndex = encryptionManager.GetClientID(cryptIdx); var client = clientSlots[clientIndex]; if (client == null) { return; } clientSlots[clientIndex] = null; // remove encryption mapping encryptionManager.RemoveEncryptionMapping(sender, time); // make sure all other clients still have their encryption mappings foreach (RemoteClient otherClient in clientSlots) { if (otherClient == null) { continue; } if (encryptionManager.FindEncryptionMapping(otherClient.RemoteEndpoint, time) == -1) { log("Encryption mapping removed wrong mapping!", NetcodeLogLevel.Debug); } } // trigger client disconnect callback if (OnClientDisconnected != null) { OnClientDisconnected(client); } log("Client {0} disconnected", NetcodeLogLevel.Info, client.RemoteEndpoint); }
private bool checkReplay(NetcodePacketHeader packetHeader) { return(replayProtection.AlreadyReceived(packetHeader.SequenceNumber)); }