C# SOCKS5 Proxy
I am working on developing an extensible MUD/ORPG game server framework in C#. As part of the package, I would like to include a SOCKS5 proxy client that could be installed on user's machines. This proxy client would sit between the user's standard telnet client (zMUD, GMUD, MUSHclient, or just plain telnet) and look for certain packets. These packets would tell the client to bring up different kinds of multimedia data traditionally not available on telnet client. Since this is a game application, a world map or a list of worn equipment could be display, for example. I'm looking for any information/guidance on how proxy development works, and where I could get started on making this. I currently have a basic C# telnet client, and the server itself has a multi-threaded telnet listener that's all worked out - so the system can be fully tested, I just need to know what I should do to make the middleman (the proxy). Any suggestions greatly appreciated.
Roei BarPosted Aug 8, 2009, 3:22 PM
here is a sample Socks5 Client:
public class ConnectionException:ApplicationException
{
public ConnectionException(string message)
:base(message)
{
}
}
///
/// Provides sock5 functionality to clients (Connect only).
///
public class SocksProxy
{
private SocksProxy(){}
#region ErrorMessages
private static string[] errorMsgs= {
"Operation completed successfully.",
"General SOCKS server failure.",
"Connection not allowed by ruleset.",
"Network unreachable.",
"Host unreachable.",
"Connection refused.",
"TTL expired.",
"Command not supported.",
"Address type not supported.",
"Unknown error."
};
#endregion
public static Socket ConnectToSocks5Proxy(string proxyAdress, ushort proxyPort, string destAddress, ushort destPort,
string userName, string password)
{
IPAddress destIP = null;
IPAddress proxyIP = null;
byte[] request = new byte[257];
byte[] response = new byte[257];
ushort nIndex;
try
{
proxyIP = IPAddress.Parse(proxyAdress);
}
catch(FormatException)
{ // get the IP address
proxyIP = Dns.GetHostByAddress(proxyAdress).AddressList[0];
}
// Parse destAddress (assume it in string dotted format "212.116.65.112" )
try
{
destIP = IPAddress.Parse(destAddress);
}
catch(FormatException)
{
// wrong assumption its in domain name format "www.microsoft.com"
}
IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP,proxyPort);
// open a TCP connection to SOCKS server...
Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
s.Connect(proxyEndPoint);
nIndex = 0;
request[nIndex++]=0x05; // Version 5.
request[nIndex++]=0x02; // 2 Authentication methods are in packet...
request[nIndex++]=0x00; // NO AUTHENTICATION REQUIRED
request[nIndex++]=0x02; // USERNAME/PASSWORD
// Send the authentication negotiation request...
s.Send(request,nIndex,SocketFlags.None);
// Receive 2 byte response...
int nGot = s.Receive(response,2,SocketFlags.None);
if (nGot!=2)
throw new ConnectionException("Bad response received from proxy server.");
if (response[1]==0xFF)
{ // No authentication method was accepted close the socket.
s.Close();
throw new ConnectionException("None of the authentication method was accepted by proxy server.");
}
byte[] rawBytes;
if (/*response[1]==0x02*/true)
{//Username/Password Authentication protocol
nIndex = 0;
request[nIndex++]=0x05; // Version 5.
// add user name
request[nIndex++]=(byte)userName.Length;
rawBytes = Encoding.Default.GetBytes(userName);
rawBytes.CopyTo(request,nIndex);
nIndex+=(ushort)rawBytes.Length;
// add password
request[nIndex++]=(byte)password.Length;
rawBytes = Encoding.Default.GetBytes(password);
rawBytes.CopyTo(request,nIndex);
nIndex+=(ushort)rawBytes.Length;
// Send the Username/Password request
s.Send(request,nIndex,SocketFlags.None);
// Receive 2 byte response...
nGot = s.Receive(response,2,SocketFlags.None);
if (nGot!=2)
throw new ConnectionException("Bad response received from proxy server.");
if (response[1] != 0x00)
throw new ConnectionException("Bad Usernaem/Password.");
}
// This version only supports connect command.
// UDP and Bind are not supported.
// Send connect request now...
nIndex = 0;
request[nIndex++]=0x05; // version 5.
request[nIndex++]=0x01; // command = connect.
request[nIndex++]=0x00; // Reserve = must be 0x00
if (destIP != null)
{// Destination adress in an IP.
switch(destIP.AddressFamily)
{
case AddressFamily.InterNetwork:
// Address is IPV4 format
request[nIndex++]=0x01;
rawBytes = destIP.GetAddressBytes();
rawBytes.CopyTo(request,nIndex);
nIndex+=(ushort)rawBytes.Length;
break;
case AddressFamily.InterNetworkV6:
// Address is IPV6 format
request[nIndex++]=0x04;
rawBytes = destIP.GetAddressBytes();
rawBytes.CopyTo(request,nIndex);
nIndex+=(ushort)rawBytes.Length;
break;
}
}
else
{// Dest. address is domain name.
request[nIndex++]=0x03; // Address is full-qualified domain name.
request[nIndex++]=Convert.ToByte(destAddress.Length); // length of address.
rawBytes = Encoding.Default.GetBytes(destAddress);
rawBytes.CopyTo(request,nIndex);
nIndex+=(ushort)rawBytes.Length;
}
// using big-edian byte order
byte[] portBytes = BitConverter.GetBytes(destPort);
for (int i=portBytes.Length-1;i>=0;i--)
request[nIndex++]=portBytes[i];
// send connect request.
s.Send(request,nIndex,SocketFlags.None);
s.Receive(response); // Get variable length response...
if (response[1]!=0x00)
throw new ConnectionException(errorMsgs[response[1]]);
// Success Connected...
return s;
}
}
If you like this click on the "Accept this Answer"