public static void SendControlMsg(UdpClient udpClient, IPEndPoint remote, int ack, bool useToken, uint token, ConnectionMessages msg, byte[] extra) { NetworkChunkConstruct packet; if (extra == null || extra.Length == 0) { packet = new NetworkChunkConstruct(1) { DataSize = 1 }; } else { packet = new NetworkChunkConstruct(1 + extra.Length) { DataSize = 1 + extra.Length }; Buffer.BlockCopy(extra, 0, packet.Data, 1, extra.Length); } packet.NumChunks = 0; packet.Flags = PacketFlags.CONTROL | (useToken ? PacketFlags.TOKEN : PacketFlags.NONE); packet.Ack = ack; packet.Token = token; packet.Data[0] = (byte)msg; SendPacket(udpClient, remote, packet); }
public static void SendPacket(UdpClient udpClient, IPEndPoint endPoint, NetworkChunkConstruct packet) { var buffer = new byte[MAX_PACKET_SIZE]; var headerSize = PACKET_HEADER_SIZE_WITHOUT_TOKEN; var compressedSize = -1; if (packet.Flags.HasFlag(PacketFlags.TOKEN)) { headerSize = PACKET_HEADER_SIZE; packet.Token.ToByteArray(buffer, 3); } if (!packet.Flags.HasFlag(PacketFlags.CONTROL)) { compressedSize = _huffman.Compress(packet.Data, 0, packet.DataSize, buffer, headerSize, buffer.Length - headerSize); } //compressedSize = _huffman.Compress(packet.Data, 0, packet.DataSize, // buffer, PACKET_HEADER_SIZE, buffer.Length - PACKET_HEADER_SIZE); int finalSize; if (compressedSize > 0 && compressedSize < packet.DataSize) { finalSize = compressedSize; packet.Flags |= PacketFlags.COMPRESSION; } else { finalSize = packet.DataSize; Buffer.BlockCopy(packet.Data, 0, buffer, headerSize, packet.DataSize); packet.Flags &= ~PacketFlags.COMPRESSION; } finalSize += headerSize; buffer[0] = (byte)((((int)packet.Flags << 2) & 0xfc) | ((packet.Ack >> 8) & 0x3)); buffer[1] = (byte)(packet.Ack & 0xff); buffer[2] = (byte)packet.NumChunks; udpClient.Send(buffer, finalSize, endPoint); }
protected virtual void ConstructLegacyHandshake( NetworkChunkConstruct packet1, NetworkChunkConstruct packet2, uint legacyToken) { throw new NotImplementedException(); packet1.Flags = PacketFlags.CONTROL; packet1.Ack = 0; packet1.NumChunks = 0; packet1.DataSize = 1; packet1.Data[0] = (byte)ConnectionMessages.CONNECTACCEPT; packet2.Flags = PacketFlags.NONE; packet2.Ack = 0; packet2.NumChunks = 0; packet2.DataSize = 0; var packer = new Packer(); packer.Reset(); packer.AddInt(System(NetworkMessages.SV_MAP_CHANGE)); packer.AddString("dm1"); //packer.AddInt(4061503086); packer.AddInt(5805); AddChunk(packet2, 1, packer.Data(), packer.Size()); packer.Reset(); packer.AddInt(System(NetworkMessages.SV_CON_READY)); AddChunk(packet2, 2, packer.Data(), packer.Size()); for (var i = -2; i <= 0; i++) { packer.Reset(); packer.AddInt(System(NetworkMessages.SV_SNAPEMPTY)); packer.AddInt((int)(legacyToken + i)); packer.AddInt((int)(i == -2 ? legacyToken + i + 1 : 1)); AddChunk(packet2, -1, packer.Data(), packer.Size()); } }
private void AddChunk(NetworkChunkConstruct packet, int sequence, byte[] data, int dataSize) { Debug.Assert(packet.DataSize + NetworkCore.MAX_CHUNK_HEADER_SIZE + dataSize <= packet.Data.Length, "too much data"); var header = new NetworkChunkHeader { Flags = sequence >= 0 ? ChunkFlags.VITAL : ChunkFlags.NONE, Size = dataSize, Sequence = sequence >= 0 ? sequence : 0 }; var packetDataOffset = packet.DataSize; var chunkStartOffset = packetDataOffset; packetDataOffset = header.Pack(packet.Data, packetDataOffset); packet.DataSize += packetDataOffset - chunkStartOffset; Buffer.BlockCopy(data, 0, packet.Data, packetDataOffset, dataSize); packet.DataSize += dataSize; packet.NumChunks++; }
public ChunkReceiver() { ChunkConstruct = new NetworkChunkConstruct(); }
/* * Buffer.BlockCopy(shortSamples, 0, packetBytes, 0, shortSamples.Length * sizeof(short)). * -And the same trick works in reverse as well: * Buffer.BlockCopy(packetBytes, readPosition, shortSamples, 0, payloadLength); */ public static bool UnpackPacket(byte[] buffer, int size, NetworkChunkConstruct packet) { if (size < PACKET_HEADER_SIZE_WITHOUT_TOKEN || size > MAX_PACKET_SIZE) { Debug.Warning("network", $"packet too small, size={size}"); return(false); } packet.Flags = (PacketFlags)(buffer[0] >> 2); packet.Ack = ((buffer[0] & 0x3) << 8) | buffer[1]; packet.NumChunks = buffer[2]; packet.DataSize = size - PACKET_HEADER_SIZE_WITHOUT_TOKEN; packet.Token = 0; if (packet.Flags.HasFlag(PacketFlags.CONNLESS)) { if (size < DATA_OFFSET) { Debug.Warning("network", $"connection less packet too small, size={size}"); return(false); } packet.Flags = PacketFlags.CONNLESS; packet.Ack = 0; packet.NumChunks = 0; packet.DataSize = size - DATA_OFFSET; Buffer.BlockCopy(buffer, DATA_OFFSET, packet.Data, 0, packet.DataSize); } else { var dataStart = PACKET_HEADER_SIZE_WITHOUT_TOKEN; if (packet.Flags.HasFlag(PacketFlags.TOKEN)) { if (size < PACKET_HEADER_SIZE) { Debug.Warning("network", $"packet with token too small, {size}"); return(false); } dataStart = PACKET_HEADER_SIZE; packet.DataSize -= 4; packet.Token = buffer.ToUInt32(3); } if (packet.Flags.HasFlag(PacketFlags.COMPRESSION)) { packet.DataSize = _huffman.Decompress(buffer, dataStart, packet.DataSize, packet.Data, 0, MAX_PAYLOAD); } else { Buffer.BlockCopy(buffer, dataStart, packet.Data, 0, packet.DataSize); } /*if (flags.HasFlag(PacketFlags.COMPRESSION)) * { * if (flags.HasFlag(PacketFlags.CONTROL)) * return false; * * dataSize = _huffman.Decompress(buffer, PACKET_HEADER_SIZE, * dataSize, packet.Data, 0, MAX_PAYLOAD); * } * else * { * Buffer.BlockCopy(buffer, PACKET_HEADER_SIZE, packet.Data, 0, dataSize); * } * * packet.Flags = flags; * packet.Ack = ack; * packet.NumChunks = numChunks; * packet.DataSize = dataSize;*/ } if (packet.DataSize < 0) { Debug.Warning("network", "error during packet decoding"); return(false); } return(true); }
public override bool Receive(out NetworkChunk packet) { while (true) { if (ChunkReceiver.FetchChunk(out packet)) { return(true); } if (UdpClient.Available <= 0) { return(false); } var remote = (IPEndPoint)null; byte[] data; try { data = UdpClient.Receive(ref remote); } catch { continue; } if (data.Length <= 0) { continue; } if (!NetworkCore.UnpackPacket(data, data.Length, ChunkReceiver.ChunkConstruct)) { continue; } var useToken = false; var token = (uint)0; if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.TOKEN)) { useToken = true; token = ChunkReceiver.ChunkConstruct.Token; } else if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONTROL) && ChunkReceiver.ChunkConstruct.Data[0] == (int)ConnectionMessages.CONNECT && ChunkReceiver.ChunkConstruct.DataSize >= 1 + 512) { useToken = true; token = ChunkReceiver.ChunkConstruct.Data.ToUInt32(5); } if (NetworkBan.IsBanned(remote, out var reason)) { NetworkCore.SendControlMsg(UdpClient, remote, 0, useToken, token, ConnectionMessages.CLOSE, reason); continue; } if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONNLESS)) { packet = new NetworkChunk { ClientId = -1, Flags = SendFlags.CONNLESS, EndPoint = remote, DataSize = ChunkReceiver.ChunkConstruct.DataSize, Data = ChunkReceiver.ChunkConstruct.Data }; return(true); } var clientId = FindSlot(remote, true); if (ChunkReceiver.ChunkConstruct.Flags.HasFlag(PacketFlags.CONTROL) && ChunkReceiver.ChunkConstruct.Data[0] == (int)ConnectionMessages.CONNECT) { if (clientId != -1) { continue; } if (ChunkReceiver.ChunkConstruct.DataSize >= 1 + 512) { var connectAccept = new byte[4]; GetToken(remote).ToByteArray(connectAccept, 0); NetworkCore.SendControlMsg(UdpClient, remote, 0, true, token, ConnectionMessages.CONNECTACCEPT, connectAccept); Debug.Log("netserver", "got connect, sending connect+accept challenge"); } else if (Config["SvAllowOldClients"].AsBoolean() && string.IsNullOrWhiteSpace(Config["Password"].AsString())) { if (LegacyRateLimit()) { Debug.Log("netserver", "dropping legacy connect due to ratelimit"); continue; } var packets = new NetworkChunkConstruct[] { new NetworkChunkConstruct(), new NetworkChunkConstruct(), }; var legacyToken = GetLegacyToken(remote); ConstructLegacyHandshake(packets[0], packets[1], legacyToken); for (var i = 0; i < 2; i++) { NetworkCore.SendPacket(UdpClient, remote, packets[i]); } Debug.Log("netserver", "got legacy connect, sending legacy challenge"); } else { Debug.Log("netserver", $"dropping short connect packet, size={ChunkReceiver.ChunkConstruct.DataSize}"); } } else { if (clientId == -1) { if (!useToken || !IsCorrectToken(remote, token)) { if (!useToken && Config["SvAllowOldClients"].AsBoolean()) { ChunkReceiver.Start(remote, null, -1); var chunk = new NetworkChunk(); var correct = false; while (ChunkReceiver.FetchChunk(out chunk)) { if (DecodeLegacyHandShake(chunk.Data, chunk.DataSize, out var legacyToken)) { if (IsCorrectLegacyToken(remote, legacyToken)) { correct = true; break; } } } ChunkReceiver.Clear(); if (!correct) { continue; } } else { Debug.Log("netserver", !useToken ? "dropping packet with missing token" : $"dropping packet with invalid token, token={token}"); continue; } } var sameIps = 0; for (var i = 0; i < Connections.Length; i++) { if (Connections[i].State == ConnectionState.OFFLINE) { if (clientId < 0) { clientId = i; } continue; } if (!NetworkCore.CompareEndPoints(Connections[i].EndPoint, remote, false)) { continue; } sameIps++; if (sameIps >= ServerConfig.MaxClientsPerIp) { NetworkCore.SendControlMsg(UdpClient, remote, 0, useToken, token, ConnectionMessages.CLOSE, $"Only {ServerConfig.MaxClientsPerIp} players with the same IP are allowed"); return(false); } } if (clientId < 0) { for (var i = 0; i < Connections.Length; i++) { if (Connections[i].State == ConnectionState.OFFLINE) { clientId = i; break; } } } if (clientId < 0) { NetworkCore.SendControlMsg(UdpClient, remote, 0, useToken, token, ConnectionMessages.CLOSE, "This server is full"); return(false); } if (useToken) { Connections[clientId].Accept(remote, token); } else { Connections[clientId].AcceptLegacy(remote); } NewClientCallback?.Invoke(clientId, !useToken); if (!useToken) { continue; } Connections[clientId].Feed(ChunkReceiver.ChunkConstruct, remote); } if (Connections[clientId].Feed(ChunkReceiver.ChunkConstruct, remote)) { if (ChunkReceiver.ChunkConstruct.DataSize != 0) { ChunkReceiver.Start(remote, Connections[clientId], clientId); } } } } }
public abstract bool Feed(NetworkChunkConstruct packet, IPEndPoint remote);
public NetworkConnection() { Error = string.Empty; ResendQueueConstruct = new NetworkChunkConstruct(); ResendQueue = new Queue <NetworkChunkResend>(); }
public override bool Feed(NetworkChunkConstruct packet, IPEndPoint remote) { if (packet.Flags.HasFlag(PacketFlags.RESEND)) { Resend(); } if (UseToken) { if (!packet.Flags.HasFlag(PacketFlags.TOKEN)) { if (!packet.Flags.HasFlag(PacketFlags.CONTROL) || packet.DataSize < 1) { Debug.Log("connection", "dropping msg without token"); return(false); } if (packet.Data[0] == (int)ConnectionMessages.CONNECTACCEPT) { if (!Config["ClAllowOldServers"]) { Debug.Log("connection", "dropping connect+accept without token"); return(false); } } else { Debug.Log("connection", "dropping ctrl msg without token"); return(false); } } else { if (packet.Token != Token) { Debug.Log("connection", $"dropping msg with invalid token, wanted={Token} got={packet.Token}"); return(false); } } } if (Sequence >= PeerAck) { if (packet.Ack < PeerAck || packet.Ack > Sequence) { return(false); } } else { if (packet.Ack < PeerAck && packet.Ack > Sequence) { return(false); } } PeerAck = packet.Ack; if (packet.Flags.HasFlag(PacketFlags.RESEND)) { Resend(); } if (packet.Flags.HasFlag(PacketFlags.CONTROL)) { var msg = (ConnectionMessages)packet.Data[0]; if (msg == ConnectionMessages.CLOSE) { if (!NetworkCore.CompareEndPoints(EndPoint, remote, true)) { return(false); } State = ConnectionState.ERROR; RemoteClosed = true; var reason = ""; if (packet.DataSize > 1) { reason = Encoding.UTF8.GetString(packet.Data, 1, Math.Clamp(packet.DataSize - 1, 1, 128)); } Error = reason; Debug.Log("connection", $"closed reason='{reason}'"); return(false); } else { if (State == ConnectionState.CONNECT) { if (msg == ConnectionMessages.CONNECTACCEPT) { if (packet.Flags.HasFlag(PacketFlags.TOKEN)) { if (packet.DataSize < 1 + 4) { Debug.Log("connection", $"got short connect+accept, size={packet.DataSize}"); return(true); } Token = packet.Data.ToUInt32(1); } else { UseToken = false; } LastReceiveTime = Time.Get(); State = ConnectionState.ONLINE; Debug.Log("connection", "got connect+accept, sending accept. connection online"); } } } } if (State == ConnectionState.ONLINE) { LastReceiveTime = Time.Get(); AckChunks(packet.Ack); } return(true); }