using System;
using System.Net.Sockets;
using System.Text;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
namespace Renci.SshNet.Connection
{
///
/// Establishes a tunnel via a SOCKS4 proxy server.
///
///
/// https://www.openssh.com/txt/socks4.protocol.
///
internal sealed class Socks4Connector : ProxyConnector
{
public Socks4Connector(ISocketFactory socketFactory)
: base(socketFactory)
{
}
///
/// Establishes a connection to the server via a SOCKS5 proxy.
///
/// The connection information.
/// The .
protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
{
var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername);
SocketAbstraction.Send(socket, connectionRequest);
// Read reply version
if (SocketReadByte(socket, connectionInfo.Timeout) != 0x00)
{
throw new ProxyException("SOCKS4: Null is expected.");
}
// Read response code
var code = SocketReadByte(socket, connectionInfo.Timeout);
switch (code)
{
case 0x5a:
break;
case 0x5b:
throw new ProxyException("SOCKS4: Connection rejected.");
case 0x5c:
throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server.");
case 0x5d:
throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request.");
default:
throw new ProxyException("SOCKS4: Not valid response.");
}
var destBuffer = new byte[6]; // destination port and IP address should be ignored
_ = SocketRead(socket, destBuffer, 0, destBuffer.Length, connectionInfo.Timeout);
}
private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username)
{
var addressBytes = GetSocks4DestinationAddress(hostname);
var proxyUserBytes = GetProxyUserBytes(username);
var connectionRequest = new byte
[
// SOCKS version number
1 +
// Command code
1 +
// Port number
2 +
// IP address
addressBytes.Length +
// Username
proxyUserBytes.Length +
// Null terminator
1
];
var index = 0;
// SOCKS version number
connectionRequest[index++] = 0x04;
// Command code
connectionRequest[index++] = 0x01; // establish a TCP/IP stream connection
// Port number
Pack.UInt16ToBigEndian(port, connectionRequest, index);
index += 2;
// Address
Buffer.BlockCopy(addressBytes, 0, connectionRequest, index, addressBytes.Length);
index += addressBytes.Length;
// User name
Buffer.BlockCopy(proxyUserBytes, 0, connectionRequest, index, proxyUserBytes.Length);
index += proxyUserBytes.Length;
// Null terminator
connectionRequest[index] = 0x00;
return connectionRequest;
}
private static byte[] GetSocks4DestinationAddress(string hostname)
{
var addresses = DnsAbstraction.GetHostAddresses(hostname);
for (var i = 0; i < addresses.Length; i++)
{
var address = addresses[i];
if (address.AddressFamily == AddressFamily.InterNetwork)
{
return address.GetAddressBytes();
}
}
throw new ProxyException(string.Format("SOCKS4 only supports IPv4. No such address found for '{0}'.", hostname));
}
private static byte[] GetProxyUserBytes(string proxyUser)
{
if (proxyUser == null)
{
return Array.Empty();
}
return Encoding.ASCII.GetBytes(proxyUser);
}
}
}