diff --git a/v2rayN/ServiceLib/Services/Udp/Socks5UdpChannel.cs b/v2rayN/ServiceLib/Services/Udp/Socks5UdpChannel.cs index 3f8a63dd..df008bbf 100644 --- a/v2rayN/ServiceLib/Services/Udp/Socks5UdpChannel.cs +++ b/v2rayN/ServiceLib/Services/Udp/Socks5UdpChannel.cs @@ -4,9 +4,9 @@ namespace ServiceLib.Services.Udp; public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposable { - private TcpClient tcpClient; - private UdpClient udpClient; - private IPEndPoint relayEndPoint; + private TcpClient _tcpClient; + private UdpClient _udpClient; + private IPEndPoint _relayEndPoint; private bool _initialized = false; @@ -24,7 +24,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl Port = (ushort)remote.Port }; var packet = BuildSocks5UdpPacket(addrData, data); - await udpClient.SendAsync(packet, packet.Length, relayEndPoint); + await _udpClient.SendAsync(packet, packet.Length, _relayEndPoint); } /// @@ -55,7 +55,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl addrData.Port = port; var packet = BuildSocks5UdpPacket(addrData, data); - await udpClient.SendAsync(packet, packet.Length, relayEndPoint); + await _udpClient.SendAsync(packet, packet.Length, _relayEndPoint); } /// @@ -66,7 +66,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl public async Task<(Socks5RemoteEndpoint Remote, byte[] Data)> ReceiveAsync( CancellationToken cancellationToken = default) { - var result = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); + var result = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); var (remote, payload) = ParseSocks5UdpPacket(result.Buffer); return (remote, payload); } @@ -200,8 +200,8 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl public void Dispose() { - tcpClient.Dispose(); - udpClient.Dispose(); + _tcpClient.Dispose(); + _udpClient.Dispose(); } #region SOCKS5 Connection Handling @@ -217,20 +217,20 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl _initialized = false; } - udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); - tcpClient = new TcpClient(); + _udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); + _tcpClient = new TcpClient(); try { - await tcpClient.ConnectAsync(socks5Host, socks5TcpPort, cancellationToken).ConfigureAwait(false); + await _tcpClient.ConnectAsync(socks5Host, socks5TcpPort, cancellationToken).ConfigureAwait(false); } catch (SocketException) { return false; } - var tcpControlStream = tcpClient.GetStream(); + var tcpControlStream = _tcpClient.GetStream(); - byte[] handshakeRequest = { Socks5Version, 0x01, 0x00 }; + byte[] handshakeRequest = [Socks5Version, 0x01, 0x00]; await tcpControlStream.WriteAsync(handshakeRequest, cancellationToken).ConfigureAwait(false); var handshakeResponse = new byte[2]; if (await tcpControlStream.ReadAsync(handshakeResponse, cancellationToken).ConfigureAwait(false) < 2 || @@ -264,7 +264,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl return false; } - relayEndPoint = new IPEndPoint(proxyRelayIp, proxyRelaySocksAddr.Port); + _relayEndPoint = new IPEndPoint(proxyRelayIp, proxyRelaySocksAddr.Port); _initialized = true; return true; } @@ -296,7 +296,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl } else { - ms.Write(new byte[] { 0, 0, 0, 0 }); + ms.Write([0, 0, 0, 0]); } break; diff --git a/v2rayN/ServiceLib/Services/Udp/Test/DnsService.cs b/v2rayN/ServiceLib/Services/Udp/Test/DnsService.cs index 9c9c0912..c72bb72c 100644 --- a/v2rayN/ServiceLib/Services/Udp/Test/DnsService.cs +++ b/v2rayN/ServiceLib/Services/Udp/Test/DnsService.cs @@ -7,16 +7,15 @@ public class DnsService : IUdpTest private const int DnsDefaultPort = 53; private const string DnsDefaultServer = "8.8.8.8"; // Google Public DNS private static readonly byte[] DnsQueryPacket = - new byte[] - { + [ // Header: ID=0x1234, Standard query with RD set, QDCOUNT=1 0x12, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Question: www.google.com, Type A, Class IN 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, - 0x00, 0x01, 0x00, 0x01, - }; + 0x00, 0x01, 0x00, 0x01 + ]; public byte[] BuildUdpRequestPacket() { @@ -25,7 +24,7 @@ public class DnsService : IUdpTest public bool VerifyAndExtractUdpResponse(byte[] dnsResponseBytes) { - if (dnsResponseBytes == null || dnsResponseBytes.Length < 12) + if (dnsResponseBytes.Length < 12) { return false; } @@ -76,4 +75,4 @@ public class DnsService : IUdpTest { return DnsDefaultServer; } -} \ No newline at end of file +} diff --git a/v2rayN/ServiceLib/Services/Udp/Test/McBeService.cs b/v2rayN/ServiceLib/Services/Udp/Test/McBeService.cs index bde48a29..6a90eb9b 100644 --- a/v2rayN/ServiceLib/Services/Udp/Test/McBeService.cs +++ b/v2rayN/ServiceLib/Services/Udp/Test/McBeService.cs @@ -6,8 +6,7 @@ public class McBeService : IUdpTest private const string McBeDefaultServer = "pms.mc-complex.com"; // 0x01 | client alive time in ms (unsigned long long) | magic | client GUID private static readonly byte[] McBeQueryPacket = - new byte[] - { + [ // 0x01 0x01, // Client alive time (1000 ms) @@ -18,19 +17,19 @@ public class McBeService : IUdpTest // Client GUID (random 16 bytes) 0x66, 0x0E, 0xAB, 0xBC, 0x61, 0x0D, 0x1F, 0x4E, 0xA4, 0x40, 0x8C, 0x65, 0xC1, 0xBE, 0xF5, 0x4B - }; - private static readonly byte[] McBeMagicBytes = new byte[] - { + ]; + private static readonly byte[] McBeMagicBytes = + [ 0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78 - }; - private static readonly List ValidGameModes = new List - { + ]; + private static readonly List ValidGameModes = + [ "Survival", "Creative", "Adventure", "Spectator" - }; + ]; public byte[] BuildUdpRequestPacket() { @@ -45,7 +44,7 @@ public class McBeService : IUdpTest // Edition Example: // // MCPE;Dedicated Server;527;1.19.1;0;10;13253860892328930865;Bedrock level;Survival;1;19132;19133; - if (mcbeResponseBytes == null || mcbeResponseBytes.Length < 48) + if (mcbeResponseBytes.Length < 48) { return false; } diff --git a/v2rayN/ServiceLib/Services/Udp/Test/NtpService.cs b/v2rayN/ServiceLib/Services/Udp/Test/NtpService.cs index d5a72d75..72ffdc8f 100644 --- a/v2rayN/ServiceLib/Services/Udp/Test/NtpService.cs +++ b/v2rayN/ServiceLib/Services/Udp/Test/NtpService.cs @@ -1,5 +1,3 @@ -using System.Buffers.Binary; - namespace ServiceLib.Services.Udp.Test; public class NtpService : IUdpTest @@ -16,7 +14,7 @@ public class NtpService : IUdpTest public bool VerifyAndExtractUdpResponse(byte[] ntpResponseBytes) { - if (ntpResponseBytes == null || ntpResponseBytes.Length < 48) + if (ntpResponseBytes.Length < 48) { return false; } @@ -24,17 +22,7 @@ public class NtpService : IUdpTest { return false; } - try - { - var secsSince1900 = BinaryPrimitives.ReadUInt32BigEndian(ntpResponseBytes.AsSpan(40, 4)); - const long ntpToUnixEpochOffsetSeconds = 2208988800L; - var unixSecs = (long)secsSince1900 - ntpToUnixEpochOffsetSeconds; - return true; - } - catch - { - return false; - } + return true; } public ushort GetDefaultTargetPort() @@ -46,4 +34,4 @@ public class NtpService : IUdpTest { return NtpDefaultServer; } -} \ No newline at end of file +} diff --git a/v2rayN/ServiceLib/Services/Udp/Test/StunService.cs b/v2rayN/ServiceLib/Services/Udp/Test/StunService.cs index 890e4f70..e25a925a 100644 --- a/v2rayN/ServiceLib/Services/Udp/Test/StunService.cs +++ b/v2rayN/ServiceLib/Services/Udp/Test/StunService.cs @@ -4,47 +4,32 @@ public class StunService : IUdpTest { private const int StunDefaultPort = 3478; private const string StunDefaultServer = "stun.voztovoice.org"; - private byte[] _transactionId; + private static readonly byte[] StunBindingRequestPacket = + [ + // STUN Binding Request + 0x00, 0x01, // Message Type: Binding Request (0x0001) + 0x00, 0x00, // Message Length: 0 (no attributes) + 0x21, 0x12, 0xA4, 0x42, // Magic Cookie: 0x2112A442 + // Transaction ID: 96 bits (12 bytes) random + 0x66, 0x0E, 0xAB, 0xBC, 0x61, 0x0D, + 0xA4, 0x40, 0x8C, 0x65, 0xC1, 0xBE, + ]; public byte[] BuildUdpRequestPacket() { - // STUN Binding Request - var packet = new byte[20]; - - // Message Type: Binding Request (0x0001) - packet[0] = 0x00; - packet[1] = 0x01; - - // Message Length: 0 (no attributes) - packet[2] = 0x00; - packet[3] = 0x00; - - // Magic Cookie: 0x2112A442 - packet[4] = 0x21; - packet[5] = 0x12; - packet[6] = 0xA4; - packet[7] = 0x42; - - // Transaction ID: 96 bits (12 bytes) random - _transactionId = new byte[12]; - RandomNumberGenerator.Fill(_transactionId); - Array.Copy(_transactionId, 0, packet, 8, 12); - - return packet; + return (byte[])StunBindingRequestPacket.Clone(); } public bool VerifyAndExtractUdpResponse(byte[] stunResponseBytes) { - if (stunResponseBytes == null || stunResponseBytes.Length < 20) + if (stunResponseBytes.Length < 20) { return false; } - // Message Type: Binding Success Response (0x0101) 或 Binding Error Response (0x0111) if (stunResponseBytes.Length >= 2) { var messageType = (stunResponseBytes[0] << 8) | stunResponseBytes[1]; - // 0x0101 = Success Response, 0x0111 = Error Response if (messageType is 0x0101 or 0x0111) { return true; @@ -63,4 +48,4 @@ public class StunService : IUdpTest { return StunDefaultServer; } -} \ No newline at end of file +} diff --git a/v2rayN/ServiceLib/Services/Udp/UdpService.cs b/v2rayN/ServiceLib/Services/Udp/UdpService.cs index 49bbfc44..f23279f6 100644 --- a/v2rayN/ServiceLib/Services/Udp/UdpService.cs +++ b/v2rayN/ServiceLib/Services/Udp/UdpService.cs @@ -52,7 +52,7 @@ public class UdpService } // Handle IPv6 format: [::1]:port or [2001:db8::1]:port - if (targetServerHost.StartsWith("[")) + if (targetServerHost.StartsWith('[')) { var closeBracketIndex = targetServerHost.IndexOf(']'); if (closeBracketIndex > 0) @@ -96,64 +96,57 @@ public class UdpService throw new InvalidOperationException("Failed to build UDP request packet."); } using var channel = new Socks5UdpChannel(Global.Loopback, socks5Port); - try + if (!await channel.EstablishUdpAssociationAsync(cancellationToken).ConfigureAwait(false)) { - if (!await channel.EstablishUdpAssociationAsync(cancellationToken).ConfigureAwait(false)) + throw new Exception("Failed to establish UDP association with SOCKS5 proxy."); + } + + var (targetHost, targetPort) = ParseHostAndPort(targetServerHost); + + byte[] udpReceiveResult = null; + + // Get minimum round trip time from two attempts + var roundTripTime = TimeSpan.MaxValue; + + for (var attempt = 0; attempt < 2; attempt++) + { + try { - throw new Exception("Failed to establish UDP association with SOCKS5 proxy."); - } + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await channel.SendAsync(targetHost, targetPort, udpRequestPacket).ConfigureAwait(false); + var (_, receiveResult) = await channel.ReceiveAsync(cancellationToken).ConfigureAwait(false); + stopwatch.Stop(); - var (targetHost, targetPort) = ParseHostAndPort(targetServerHost); + udpReceiveResult = receiveResult; - byte[] udpReceiveResult = null; - - // Get minimum round trip time from two attempts - var roundTripTime = TimeSpan.MaxValue; - - for (var attempt = 0; attempt < 2; attempt++) - { - try + var currentRoundTripTime = stopwatch.Elapsed; + if (currentRoundTripTime < roundTripTime) { - var stopwatch = new Stopwatch(); - stopwatch.Start(); - await channel.SendAsync(targetHost, targetPort, udpRequestPacket).ConfigureAwait(false); - var (_, receiveResult) = await channel.ReceiveAsync(cancellationToken).ConfigureAwait(false); - stopwatch.Stop(); - - udpReceiveResult = receiveResult; - - var currentRoundTripTime = stopwatch.Elapsed; - if (currentRoundTripTime < roundTripTime) - { - roundTripTime = currentRoundTripTime; - } - } - catch - { - if (attempt == 1 && roundTripTime == TimeSpan.MaxValue) - { - throw; - } + roundTripTime = currentRoundTripTime; } } - - if ((udpReceiveResult?.Length ?? 0) < 4 + 1 + 4 + 2) + catch { - throw new Exception("Received NTP response is too short."); - } - - if (_udpTest.VerifyAndExtractUdpResponse(udpReceiveResult)) - { - return roundTripTime; - } - else - { - throw new Exception("Failed to verify and extract UDP response."); + if (attempt == 1 && roundTripTime == TimeSpan.MaxValue) + { + throw; + } } } - catch + + if ((udpReceiveResult?.Length ?? 0) < 4 + 1 + 4 + 2) { - throw; + throw new Exception("Received NTP response is too short."); + } + + if (udpReceiveResult != null && _udpTest.VerifyAndExtractUdpResponse(udpReceiveResult)) + { + return roundTripTime; + } + else + { + throw new Exception("Failed to verify and extract UDP response."); } } }