Table of Contents

Class Socks5ProxyClient

Namespace
VpnHood.Net.Proxies.Socks5ProxyClients
Assembly
VpnHood.Net.Proxies.dll

Opens TCP tunnels and UDP associations through a SOCKS5 proxy (RFC 1928), with optional username and password authentication (RFC 1929).

public class Socks5ProxyClient : IProxyClient
Inheritance
Socks5ProxyClient
Implements
Inherited Members

Examples

var client = new Socks5ProxyClient(new Socks5ProxyClientOptions {
    ProxyEndPoint = proxyEndPoint
});

using var tcp = new TcpClient();

// A host name goes to the proxy as a SOCKS5 domain name, so the proxy resolves it: no local DNS query.
await client.ConnectAsync(tcp, destinationHost, destinationPort, CancellationToken.None);

var stream = tcp.GetStream(); // the tunnel: speak HTTP, TLS or anything else

Remarks

One instance can serve many connections at once. The method negotiation and the authentication run once for each TcpClient, and the operations on one connection run one at a time: a call waits for the one before it, and a call that is canceled during that wait leaves the connection open. Keep the options unchanged while the client is in use. A host name is sent to the proxy as a domain name, so the proxy resolves it and no DNS query for it leaves this machine.

Constructors

Socks5ProxyClient(Socks5ProxyClientOptions, ILogger<Socks5ProxyClient>?)

Opens TCP tunnels and UDP associations through a SOCKS5 proxy (RFC 1928), with optional username and password authentication (RFC 1929).

public Socks5ProxyClient(Socks5ProxyClientOptions options, ILogger<Socks5ProxyClient>? logger = null)

Parameters

options Socks5ProxyClientOptions

The proxy's address and the credentials to offer.

logger ILogger<Socks5ProxyClient>

The logger for the client's progress and failures, or null for none.

Examples

var client = new Socks5ProxyClient(new Socks5ProxyClientOptions {
    ProxyEndPoint = proxyEndPoint
});

using var tcp = new TcpClient();

// A host name goes to the proxy as a SOCKS5 domain name, so the proxy resolves it: no local DNS query.
await client.ConnectAsync(tcp, destinationHost, destinationPort, CancellationToken.None);

var stream = tcp.GetStream(); // the tunnel: speak HTTP, TLS or anything else

Remarks

One instance can serve many connections at once. The method negotiation and the authentication run once for each TcpClient, and the operations on one connection run one at a time: a call waits for the one before it, and a call that is canceled during that wait leaves the connection open. Keep the options unchanged while the client is in use. A host name is sent to the proxy as a domain name, so the proxy resolves it and no DNS query for it leaves this machine.

Properties

ProxyEndPoint

Gets the address and port of the proxy server.

public IPEndPoint ProxyEndPoint { get; }

Property Value

IPEndPoint

Methods

CheckConnectionAsync(TcpClient, CancellationToken)

Connects to the proxy and runs the method negotiation and the authentication, without asking for a tunnel.

public Task CheckConnectionAsync(TcpClient tcpClient, CancellationToken cancellationToken)

Parameters

tcpClient TcpClient

The connection to check. When it is not connected, it is connected to ProxyEndPoint first. After the check it stays open and authenticated, so a later ConnectAsync(TcpClient, string, int, CancellationToken) or CreateUdpAssociateAsync(TcpClient, CancellationToken) on it skips the negotiation. Send that request soon: a proxy closes a connection that asks for nothing, as the server of this library does when its handshake timeout ends. When the check fails, it is closed.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation. It completes when the check passes.

Exceptions

ArgumentNullException

tcpClient is null.

ArgumentException

A credential in the options exceeds 255 bytes.

UnauthorizedAccessException

The proxy requires credentials the options lack, refused them, or accepts none of the offered methods.

ProtocolViolationException

The proxy's answer is not SOCKS5.

NotSupportedException

The proxy chose an authentication method this client lacks.

SocketException

The connection to the proxy failed.

EndOfStreamException

The proxy closed the connection before its answer was complete.

OperationCanceledException

cancellationToken was canceled.

ConnectAsync(TcpClient, IPEndPoint, CancellationToken)

Opens a tunnel through the proxy to an IP endpoint.

public Task ConnectAsync(TcpClient tcpClient, IPEndPoint destination, CancellationToken cancellationToken)

Parameters

tcpClient TcpClient

The connection to use. When it is not connected, it is connected to ProxyEndPoint and authenticated first. When the task completes, its GetStream() carries the tunnel; when the exchange with the proxy fails, it is closed.

destination IPEndPoint

The destination's IPv4 or IPv6 address and port.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation. It completes when the tunnel is open.

Exceptions

ArgumentNullException

tcpClient or destination is null.

ArgumentException

A credential in the options exceeds 255 bytes.

ProxyClientException

The proxy refused the request; SocketErrorCode tells why.

UnauthorizedAccessException

The proxy requires credentials the options lack, refused them, or accepts none of the offered methods.

ProtocolViolationException

The proxy's answer is not SOCKS5.

NotSupportedException

The destination is neither IPv4 nor IPv6, or the proxy chose an authentication method this client lacks, or answered with an unknown address type.

SocketException

The connection to the proxy failed.

EndOfStreamException

The proxy closed the connection before its answer was complete.

OperationCanceledException

cancellationToken was canceled.

ConnectAsync(TcpClient, string, int, CancellationToken)

Opens a tunnel through the proxy to a host, given by name or IP address.

public Task ConnectAsync(TcpClient tcpClient, string host, int port, CancellationToken cancellationToken)

Parameters

tcpClient TcpClient

The connection to use. When it is not connected, it is connected to ProxyEndPoint and authenticated first. When the task completes, its GetStream() carries the tunnel; when the exchange with the proxy fails, it is closed.

host string

The destination's host name or IP address literal. A name is sent for the proxy to resolve.

port int

The destination's port, from 1 to 65535.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation. It completes when the tunnel is open.

Exceptions

ArgumentNullException

tcpClient or host is null.

ArgumentException

host is empty or white space, or its UTF-8 form exceeds 255 bytes; or a credential in the options exceeds 255 bytes.

ArgumentOutOfRangeException

port is less than 1 or greater than 65535.

ProxyClientException

The proxy refused the request; SocketErrorCode tells why.

UnauthorizedAccessException

The proxy requires credentials the options lack, refused them, or accepts none of the offered methods.

ProtocolViolationException

The proxy's answer is not SOCKS5.

NotSupportedException

The proxy chose an authentication method this client lacks, or answered with an unknown address type.

SocketException

The connection to the proxy failed.

EndOfStreamException

The proxy closed the connection before its answer was complete.

OperationCanceledException

cancellationToken was canceled.

CreateUdpAssociateAsync(TcpClient, IPEndPoint?, CancellationToken)

Asks the proxy to relay UDP datagrams sent from a local endpoint.

public Task<IPEndPoint> CreateUdpAssociateAsync(TcpClient tcpClient, IPEndPoint? clientUdpEndPoint, CancellationToken cancellationToken)

Parameters

tcpClient TcpClient

The control connection. When it is not connected, it is connected to ProxyEndPoint and authenticated first. The association lasts as long as it stays open: keep it open while datagrams flow, and close it to end the association. When the exchange with the proxy fails, it is closed.

clientUdpEndPoint IPEndPoint

The address and port the client will send datagrams from, as the proxy sees them, or null when unknown, for example behind NAT. A proxy may relay only datagrams from it, as the server of this library does; that server takes a port of 0 as unknown, address included, and an unspecified address with a port as the control connection's address.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task<IPEndPoint>

A task that represents the asynchronous operation. Its result is the proxy's relay endpoint: send each datagram there, wrapped by WriteUdpRequest(Span<byte>, IPEndPoint, ReadOnlySpan<byte>). When the proxy answers with an unspecified address, the result has the address of ProxyEndPoint.

Examples

var client = new Socks5ProxyClient(new Socks5ProxyClientOptions {
    ProxyEndPoint = proxyEndPoint
});

// the local UDP socket that exchanges datagrams with the proxy
using var udp = new UdpClient(new IPEndPoint(IPAddress.Loopback, 0));
var localUdpEndPoint = (IPEndPoint)udp.Client.LocalEndPoint!;

// the association lasts while this TCP control connection stays open
using var controlTcp = new TcpClient();
var relayEndPoint = await client.CreateUdpAssociateAsync(controlTcp, localUdpEndPoint, CancellationToken.None);

// wrap the payload in the SOCKS5 UDP header (10 bytes for IPv4, 22 for IPv6) and send it to the relay
var buffer = new byte[22 + payload.Length];
var length = Socks5ProxyClient.WriteUdpRequest(buffer, destination, payload);
await udp.SendAsync(buffer.AsMemory(0, length), relayEndPoint);

// receive the answer and unwrap it
var result = await udp.ReceiveAsync();
var from = Socks5ProxyClient.ParseUdpResponse(result.Buffer, out var answer);

Exceptions

ArgumentNullException

tcpClient is null.

ArgumentException

A credential in the options exceeds 255 bytes.

ProxyClientException

The proxy refused the request; SocketErrorCode tells why.

UnauthorizedAccessException

The proxy requires credentials the options lack, refused them, or accepts none of the offered methods.

ProtocolViolationException

The proxy's answer is not SOCKS5.

NotSupportedException

clientUdpEndPoint is neither IPv4 nor IPv6, the proxy chose an authentication method this client lacks, or it gave its relay endpoint as a domain name or with an unknown address type.

SocketException

The connection to the proxy failed.

EndOfStreamException

The proxy closed the connection before its answer was complete.

OperationCanceledException

cancellationToken was canceled.

CreateUdpAssociateAsync(TcpClient, CancellationToken)

Asks the proxy to relay UDP datagrams for this client, without saying which local endpoint will send them.

public Task<IPEndPoint> CreateUdpAssociateAsync(TcpClient tcpClient, CancellationToken cancellationToken)

Parameters

tcpClient TcpClient

The control connection. When it is not connected, it is connected to ProxyEndPoint and authenticated first. The association lasts as long as it stays open: keep it open while datagrams flow, and close it to end the association. When the exchange with the proxy fails, it is closed.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task<IPEndPoint>

A task that represents the asynchronous operation. Its result is the proxy's relay endpoint: send each datagram there, wrapped by WriteUdpRequest(Span<byte>, IPEndPoint, ReadOnlySpan<byte>).

Remarks

The proxy learns the client's UDP endpoint from the datagrams it receives; the server of this library takes the first datagram from the address of tcpClient's connection.

Exceptions

ArgumentNullException

tcpClient is null.

ArgumentException

A credential in the options exceeds 255 bytes.

ProxyClientException

The proxy refused the request; SocketErrorCode tells why.

UnauthorizedAccessException

The proxy requires credentials the options lack, refused them, or accepts none of the offered methods.

ProtocolViolationException

The proxy's answer is not SOCKS5.

NotSupportedException

The proxy chose an authentication method this client lacks, or gave its relay endpoint as a domain name or with an unknown address type.

SocketException

The connection to the proxy failed.

EndOfStreamException

The proxy closed the connection before its answer was complete.

OperationCanceledException

cancellationToken was canceled.

ParseUdpResponse(ReadOnlySpan<byte>, out ReadOnlySpan<byte>)

Reads the SOCKS5 UDP header of a datagram received from the relay endpoint of a UDP association.

public static Socks5Endpoint ParseUdpResponse(ReadOnlySpan<byte> datagram, out ReadOnlySpan<byte> payload)

Parameters

datagram ReadOnlySpan<byte>

The datagram as received from the relay.

payload ReadOnlySpan<byte>

When this method returns, the datagram's payload: a slice of datagram, not a copy.

Returns

Socks5Endpoint

The address and port the payload came from.

Exceptions

ArgumentException

datagram is shorter than 7 bytes, or it ends before its header does: inside the address or the port that follows it.

NotSupportedException

The datagram is a fragment, its reserved bytes are not zero, or its address type is unknown.

WriteUdpRequest(Span<byte>, IPEndPoint, ReadOnlySpan<byte>)

Wraps a datagram in the SOCKS5 UDP request header (RFC 1928, section 7), ready to send to the relay endpoint of a UDP association.

public static int WriteUdpRequest(Span<byte> destinationBuffer, IPEndPoint destination, ReadOnlySpan<byte> data)

Parameters

destinationBuffer Span<byte>

The buffer to write to. It needs 10 bytes more than data for an IPv4 destination and 22 bytes more for an IPv6 one.

destination IPEndPoint

The datagram's destination.

data ReadOnlySpan<byte>

The datagram's payload.

Returns

int

The number of bytes written to destinationBuffer.

Exceptions

ArgumentNullException

destination is null.

ArgumentException

destinationBuffer is too small.

NotSupportedException

destination is neither IPv4 nor IPv6.