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
optionsSocks5ProxyClientOptionsThe proxy's address and the credentials to offer.
loggerILogger<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
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
tcpClientTcpClientThe 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.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation. It completes when the check passes.
Exceptions
- ArgumentNullException
tcpClientis 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
cancellationTokenwas 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
tcpClientTcpClientThe 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.
destinationIPEndPointThe destination's IPv4 or IPv6 address and port.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation. It completes when the tunnel is open.
Exceptions
- ArgumentNullException
tcpClientordestinationis 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
cancellationTokenwas 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
tcpClientTcpClientThe 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.
hoststringThe destination's host name or IP address literal. A name is sent for the proxy to resolve.
portintThe destination's port, from 1 to 65535.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation. It completes when the tunnel is open.
Exceptions
- ArgumentNullException
tcpClientorhostis null.- ArgumentException
hostis empty or white space, or its UTF-8 form exceeds 255 bytes; or a credential in the options exceeds 255 bytes.- ArgumentOutOfRangeException
portis 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
cancellationTokenwas 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
tcpClientTcpClientThe 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.
clientUdpEndPointIPEndPointThe 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.
cancellationTokenCancellationTokenThe 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
tcpClientis 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
clientUdpEndPointis 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
cancellationTokenwas 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
tcpClientTcpClientThe 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.
cancellationTokenCancellationTokenThe 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
tcpClientis 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
cancellationTokenwas 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
datagramReadOnlySpan<byte>The datagram as received from the relay.
payloadReadOnlySpan<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
datagramis 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
destinationBufferSpan<byte>The buffer to write to. It needs 10 bytes more than
datafor an IPv4 destination and 22 bytes more for an IPv6 one.destinationIPEndPointThe datagram's destination.
dataReadOnlySpan<byte>The datagram's payload.
Returns
- int
The number of bytes written to
destinationBuffer.
Exceptions
- ArgumentNullException
destinationis null.- ArgumentException
destinationBufferis too small.- NotSupportedException
destinationis neither IPv4 nor IPv6.