Skip to content

Commit

Permalink
ProxyChecker moved from HolyClient ot QuickProxyNet
Browse files Browse the repository at this point in the history
  • Loading branch information
TitleHHHH authored and TitleHHHH committed Sep 29, 2024
1 parent dd800fc commit dae783c
Show file tree
Hide file tree
Showing 13 changed files with 397 additions and 204 deletions.
12 changes: 12 additions & 0 deletions src/CoreLibs/HolyClient.Common/ProxyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using QuickProxyNet;
using QuickProxyNet.ProxyChecker;

namespace HolyClient.Common;

Expand Down Expand Up @@ -92,4 +94,14 @@ public override bool Equals([NotNullWhen(true)] object? obj)
&& Password == proxyInfo.Password;
return false;
}

public static implicit operator ProxyRecord(ProxyInfo info)
{
NetworkCredential? credential = null;
if (!(string.IsNullOrEmpty(info.Login) || string.IsNullOrEmpty(info.Password)))
credential = new NetworkCredential(info.Login, info.Password);

return new ProxyRecord(info.Type, info.Host, info.Port, credential);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Fody;
using QuickProxyNet;

namespace HolyClient.StressTest;
namespace HolyClient.Proxy;

[ConfigureAwait(false)]
internal class ProxyProvider : IProxyProvider
Expand Down
16 changes: 0 additions & 16 deletions src/CoreLibs/HolyClient.StressTest/ProxyCheckResult.cs

This file was deleted.

141 changes: 0 additions & 141 deletions src/CoreLibs/HolyClient.StressTest/ProxyChecker.cs

This file was deleted.

16 changes: 0 additions & 16 deletions src/CoreLibs/HolyClient.StressTest/ProxyCheckerOptions.cs

This file was deleted.

59 changes: 30 additions & 29 deletions src/QuickProxyNet/Internal/SocksHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;

namespace QuickProxyNet;
Expand All @@ -26,7 +27,7 @@ internal static class SocksHelper


internal static async ValueTask EstablishSocks5TunnelAsync(Stream stream, string host, int port,
NetworkCredential? credentials, bool async)
NetworkCredential? credentials, CancellationToken cancellationToken)
{
var buffer = ArrayPool<byte>.Shared.Rent(BufferSize);
try
Expand All @@ -51,14 +52,14 @@ internal static async ValueTask EstablishSocks5TunnelAsync(Stream stream, string
buffer[3] = METHOD_USERNAME_PASSWORD;
}

await WriteAsync(stream, buffer.AsMemory(0, buffer[1] + 2), async).ConfigureAwait(false);
await WriteAsync(stream, buffer.AsMemory(0, buffer[1] + 2), cancellationToken).ConfigureAwait(false);

// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
await ReadToFillAsync(stream, buffer.AsMemory(0, 2), async).ConfigureAwait(false);
await ReadToFillAsync(stream, buffer.AsMemory(0, 2), cancellationToken).ConfigureAwait(false);
VerifyProtocolVersion(ProtocolVersion5, buffer[0]);

switch (buffer[1])
Expand Down Expand Up @@ -88,15 +89,15 @@ internal static async ValueTask EstablishSocks5TunnelAsync(Stream stream, string
var passwordLength = EncodeString(credentials.Password, buffer.AsSpan(3 + usernameLength),
nameof(credentials.Password));
buffer[2 + usernameLength] = passwordLength;
await WriteAsync(stream, buffer.AsMemory(0, 3 + usernameLength + passwordLength), async)
await WriteAsync(stream, buffer.AsMemory(0, 3 + usernameLength + passwordLength), cancellationToken)
.ConfigureAwait(false);

// +----+--------+
// |VER | STATUS |
// +----+--------+
// | 1 | 1 |
// +----+--------+
await ReadToFillAsync(stream, buffer.AsMemory(0, 2), async).ConfigureAwait(false);
await ReadToFillAsync(stream, buffer.AsMemory(0, 2), cancellationToken).ConfigureAwait(false);
if (buffer[0] != SubnegotiationVersion || buffer[1] != Socks5_Success)
throw new ProxyProtocolException("Failed to authenticate with the SOCKS server.");
break;
Expand Down Expand Up @@ -145,24 +146,25 @@ await WriteAsync(stream, buffer.AsMemory(0, 3 + usernameLength + passwordLength)

BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(addressLength + 4), (ushort)port);

await WriteAsync(stream, buffer.AsMemory(0, addressLength + 6), async).ConfigureAwait(false);
await WriteAsync(stream, buffer.AsMemory(0, addressLength + 6), cancellationToken).ConfigureAwait(false);

// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
await ReadToFillAsync(stream, buffer.AsMemory(0, 5), async).ConfigureAwait(false);
await ReadToFillAsync(stream, buffer.AsMemory(0, 5), cancellationToken).ConfigureAwait(false);
VerifyProtocolVersion(ProtocolVersion5, buffer[0]);
if (buffer[1] != Socks5_Success) throw new ProxyProtocolException("SOCKS server failed to connect to the destination.");
if (buffer[1] != Socks5_Success)
throw new ProxyProtocolException("SOCKS server failed to connect to the destination.");
var bytesToSkip = buffer[3] switch
{
ATYP_IPV4 => 5,
ATYP_IPV6 => 17,
ATYP_DOMAIN_NAME => buffer[4] + 2,
_ => throw new ProxyProtocolException("SOCKS server returned an unknown address type.")
};
await ReadToFillAsync(stream, buffer.AsMemory(0, bytesToSkip), async).ConfigureAwait(false);
await ReadToFillAsync(stream, buffer.AsMemory(0, bytesToSkip), cancellationToken).ConfigureAwait(false);
// response address not used
}
finally
Expand All @@ -172,9 +174,10 @@ await WriteAsync(stream, buffer.AsMemory(0, 3 + usernameLength + passwordLength)
}

internal static async ValueTask EstablishSocks4TunnelAsync(Stream stream, bool isVersion4a, string host, int port,
NetworkCredential? credentials, bool async)
NetworkCredential? credentials, CancellationToken cancellationToken)
{
var buffer = ArrayPool<byte>.Shared.Rent(BufferSize);

try
{
// https://www.openssh.com/txt/socks4.protocol
Expand Down Expand Up @@ -204,16 +207,17 @@ internal static async ValueTask EstablishSocks4TunnelAsync(Stream stream, bool i
IPAddress[] addresses;
try
{
addresses = async
? await Dns.GetHostAddressesAsync(host, AddressFamily.InterNetwork).ConfigureAwait(false)
: Dns.GetHostAddresses(host, AddressFamily.InterNetwork);
addresses =
await Dns.GetHostAddressesAsync(host, AddressFamily.InterNetwork, cancellationToken)
.ConfigureAwait(false);
}
catch (Exception ex)
{
throw new ProxyProtocolException("Failed to resolve the destination host to an IPv4 address.s", ex);
}

if (addresses.Length == 0) throw new ProxyProtocolException("Failed to resolve the destination host to an IPv4 address.s");
if (addresses.Length == 0)
throw new ProxyProtocolException("Failed to resolve the destination host to an IPv4 address.s");

ipv4Address = addresses[0];
}
Expand Down Expand Up @@ -244,15 +248,15 @@ internal static async ValueTask EstablishSocks4TunnelAsync(Stream stream, bool i
totalLength += hostLength + 1;
}

await WriteAsync(stream, buffer.AsMemory(0, totalLength), async).ConfigureAwait(false);
await WriteAsync(stream, buffer.AsMemory(0, totalLength), cancellationToken).ConfigureAwait(false);

// +----+----+----+----+----+----+----+----+
// | VN | CD | DSTPORT | DSTIP |
// +----+----+----+----+----+----+----+----+
// 1 1 2 4


await ReadToFillAsync(stream, buffer.AsMemory(0, 8), async).ConfigureAwait(false);
await ReadToFillAsync(stream, buffer.AsMemory(0, 8), cancellationToken).ConfigureAwait(false);

switch (buffer[1])
{
Expand Down Expand Up @@ -281,7 +285,7 @@ private static byte EncodeString(ReadOnlySpan<char> chars, Span<byte> buffer, st
catch
{
Debug.Assert(Encoding.UTF8.GetByteCount(chars) > 255);
throw new ProxyProtocolException($"Encoding the {parameterName} took more than the maximum of 255 bytes" );
throw new ProxyProtocolException($"Encoding the {parameterName} took more than the maximum of 255 bytes");
}
}

Expand All @@ -292,22 +296,19 @@ private static void VerifyProtocolVersion(byte expected, byte version)
$"Unexpected SOCKS protocol version. Required {expected}, got {version}.");
}

private static ValueTask WriteAsync(Stream stream, Memory<byte> buffer, bool async)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ValueTask WriteAsync(Stream stream, Memory<byte> buffer, CancellationToken cancellationToken)
{
if (async)
{
return stream.WriteAsync(buffer);
}

stream.Write(buffer.Span);
return default;
return stream.WriteAsync(buffer, cancellationToken);
}

private static async ValueTask ReadToFillAsync(Stream stream, Memory<byte> buffer, bool async)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static async ValueTask ReadToFillAsync(Stream stream, Memory<byte> buffer,
CancellationToken cancellationToken)
{
var bytesRead = async
? await stream.ReadAtLeastAsync(buffer, buffer.Length, false).ConfigureAwait(false)
: stream.ReadAtLeast(buffer.Span, buffer.Length, false);
var bytesRead = await stream.ReadAtLeastAsync(buffer, buffer.Length, false, cancellationToken)
.ConfigureAwait(false);


if (bytesRead < buffer.Length) throw new IOException("The response ended prematurely.");
}
Expand Down
Loading

0 comments on commit dae783c

Please sign in to comment.