/// <summary> /// Handles a solution packet /// </summary> /// <param name="solution">Solution.</param> private void HandleSolution(UdpSolution solution, EndPoint endpoint) { var ipEndpoint = (IPEndPoint)endpoint; var address = ipEndpoint.Address; if (connections.TryGetValue(ipEndpoint, out var oldcon)) { if (oldcon.salt != solution.salt) { Log.Write("New connection failed: already an existing connection"); return; } Send(new UdpConnected(oldcon.salt, (ushort)oldcon.LocalPort), endpoint); return; } if (!requestStates.TryGetValue(ipEndpoint, out var state)) { Log.Write("New connection failed: no request state found"); return; } var saltSolution = Udp.CreateSalt(state.clientSalt, state.serverSalt); if (solution.salt != saltSolution) { Log.Write("New connection failed: salt solution invalid"); return; } if (!requestStates.TryRemove(ipEndpoint, out state)) { Log.Write("New connection failed: no request state to remove"); return; } if (!availablePorts.TryDequeue(out int port)) { Log.Write("New connection failed: failed to assign port"); return; } var connection = (TCon)Activator.CreateInstance(typeof(TCon)); if (!connections.TryAdd(ipEndpoint, connection)) { availablePorts.Enqueue(port); // return port connection.Disconnect(UdpDisconnectReason.Custom, true, "Failed to add connection"); Log.Write("New connection failed: failed to add connection"); return; } connection.SetConnectedTo(endpoint, saltSolution, port); connection.OnDisconnect += ClientDisconnected; HandleConnection(connection); connection.StartRead(); Send(new UdpConnected(saltSolution, (ushort)port), endpoint); }
/// <summary> /// Read callback for the receiving socket /// </summary> /// <param name="ar">Ar.</param> private void OnRead(IAsyncResult ar) { EndPoint fromEndpoint = new IPEndPoint(IPAddress.Any, 0); int length = 0; try { length = socket.EndReceiveFrom(ar, ref fromEndpoint); } catch (ObjectDisposedException) { return; } byte[] data = new byte[length]; System.Buffer.BlockCopy(buffer.data, 0, data, 0, length); BeginRead(); // start reading for more packets BitReader r = new BitReader(data, length); bool isUdp = r.ReadBool(); if (!isUdp) { return; // only accept udp packets } byte id = r.ReadUInt8(); var packet = Udp.CreateUdpPacket(id); if (packet == null) { return; // failed to create a packet from the given id value } packet.ReadPacket(r); HandlePacket(packet, fromEndpoint); }
/// <summary> /// Handles a connect packet /// </summary> /// <param name="connect">Connect.</param> private void HandleConnect(UdpConnect connect, EndPoint endpoint) { var ipEndpoint = (IPEndPoint)endpoint; //var address = ipEndpoint.Address; if (availablePorts.Count == 0) // no available ports { Send(new UdpDisconnect(connect.clientSalt, UdpDisconnectReason.ServerFull), endpoint); return; } if (connections.TryGetValue(ipEndpoint, out var connection)) { Send(new UdpDisconnect(connect.clientSalt, UdpDisconnectReason.ExistingConnection), endpoint); return; } var state = CreateConnectionRequest(connect.clientSalt, ipEndpoint); requestStates[ipEndpoint] = state; var saltSolution = Udp.CreateSalt(state.clientSalt, state.serverSalt); Send(new UdpChallenge(state.clientSalt, state.serverSalt), endpoint); }
/// <summary> /// Connects to a given EndPoint /// </summary> /// <param name="endpoint"></param> public void Connect(EndPoint endpoint) { // return if already started connection process if (!SetConnectionState(ConnectionState.AwaitingChallenge, ConnectionState.ReadyToConnect)) { return; } remoteEndPoint = endpoint; socket.Bind(new IPEndPoint(IPAddress.Any, 0)); localSalt = Udp.GenerateLocalSalt(); timer.Start(); StartRead(); SendConnect(); }
/// <summary> /// Responds to a challenge packet received from the remote server /// </summary> /// <param name="challenge">Challenge.</param> private void HandleChallenge(UdpChallenge challenge) { if (challenge.clientSalt != localSalt) { return; // salt mismatch, could be spoofed sender } if (!SetConnectionState(ConnectionState.AwaitingConnected, ConnectionState.AwaitingChallenge)) { return; } remoteSalt = challenge.serverSalt; salt = Udp.CreateSalt(localSalt, remoteSalt); retryCount = 0; SendSolution(); }
/// <summary> /// Packet data received /// </summary> /// <param name="length"></param> protected void ReceivedData(byte[] data, int length) { BitReader r = new BitReader(data, length); bool isUdp = r.ReadBool(); byte id; if (isUdp) { id = r.ReadUInt8(); var udpPacket = Udp.CreateUdpPacket(id); if (udpPacket == null) { return; } udpPacket.ReadPacket(r); HandleUdpPacket(udpPacket); return; } ulong receivedSalt = r.ReadUInt64(); if (receivedSalt != salt) { return; // salt mismatch, TODO disconnect } #if DEBUG if (Rand.Next(10000) / 100.0 < Simulate_Packet_Loss_Percent) { Log.Error("Stopped packet: " + r.ReadUInt16()); return; } #endif lastReceived = DateTime.Now; id = r.ReadUInt8(); // read packet type var channel = channels[id]; // get channel for packet type channel.ReceivePacket(r, id); }
private ConnectRequestState CreateConnectionRequest(ulong clientSalt, IPEndPoint ipEndpoint) { ulong serverSalt = Udp.GenerateLocalSalt(); return(new ConnectRequestState(clientSalt, serverSalt, ipEndpoint)); }