private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
{
var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);
if (authenticationMethodsCount == -1)
{
// SOCKS client closed connection
return false;
}
var authenticationMethods = new byte[authenticationMethodsCount];
if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
{
// SOCKS client closed connection
return false;
}
if (authenticationMethods.Min() == 0)
{
// no user authentication is one of the authentication methods supported
// by the SOCKS client
SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
}
else
{
// the SOCKS client requires authentication, which we currently do not support
SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);
// we continue business as usual but expect the client to close the connection
// so one of the subsequent reads should return -1 signaling that the client
// has effectively closed the connection
}
var version = SocketAbstraction.ReadByte(socket, timeout);
if (version == -1)
{
// SOCKS client closed connection
return false;
}
if (version != 5)
throw new ProxyException("SOCKS5: Version 5 is expected.");
var commandCode = SocketAbstraction.ReadByte(socket, timeout);
if (commandCode == -1)
{
// SOCKS client closed connection
return false;
}
var reserved = SocketAbstraction.ReadByte(socket, timeout);
if (reserved == -1)
{
// SOCKS client closed connection
return false;
}
if (reserved != 0)
{
throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
}
var addressType = SocketAbstraction.ReadByte(socket, timeout);
if (addressType == -1)
{
// SOCKS client closed connection
return false;
}
IPAddress ipAddress;
byte[] addressBuffer;
switch (addressType)
{
case 0x01:
{
addressBuffer = new byte[4];
if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0)
{
// SOCKS client closed connection
return false;
}
ipAddress = new IPAddress(addressBuffer);
}
break;
case 0x03:
{
var length = SocketAbstraction.ReadByte(socket, timeout);
if (length == -1)
{
// SOCKS client closed connection
return false;
}
addressBuffer = new byte[length];
if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
{
// SOCKS client closed connection
return false;
}
ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length));
//var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);
//ipAddress = Dns.GetHostEntry(hostName).AddressList[0];
}
break;
case 0x04:
{
addressBuffer = new byte[16];
if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
{
// SOCKS client closed connection
return false;
}
ipAddress = new IPAddress(addressBuffer);
}
break;
default:
throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
}
var portBuffer = new byte[2];
if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
{
// SOCKS client closed connection
return false;
}
var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
var host = ipAddress.ToString();
RaiseRequestReceived(host, port);
channel.Open(host, port, this, socket);
SocketAbstraction.SendByte(socket, 0x05);
if (channel.IsOpen)
{
SocketAbstraction.SendByte(socket, 0x00);
}
else
{
SocketAbstraction.SendByte(socket, 0x01);
}
// reserved
SocketAbstraction.SendByte(socket, 0x00);
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
SocketAbstraction.SendByte(socket, 0x01);
}
else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
SocketAbstraction.SendByte(socket, 0x04);
}
else
{
throw new NotSupportedException("Not supported address family.");
}
var addressBytes = ipAddress.GetAddressBytes();
SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
return true;
}