Introduction
Golabi proxy is a kind of reserved proxy. This proxy has encryption in requests of browsers for passing through filterings of internet.
Background
In some countries, the ISPs of that country have some kind of request checking to block unauthorized requests to websites. For example, Facebook is one of those websites that is blocked!
These request checkings have a big weakness! They cannot check encrypted request and responses. Because of that, we encrypt requests with AES algorithm. The keys of AES algorithm can be sent with some secret IP and secret ports. But in this project, we use one key as secret.
Using the Code
In this project, we have 2 sides - server side and client side.
First, we receive the requests from browser in the client computer with client Golabi proxy. Next, we encrypt the request. After that, the server side receives the encrypted request and decrypts it. In the server side, our application (server Golabi proxy) has unlimited access to internet. It makes a request and receives a response. Then response returns back to client and browser.
This project is the start of an idea! It has a few problems, but it can be fixed by some improvements.
Client Side Handler
private void Handler()
{
bool recvRequest = true;
string EOL = "\r\n";
string requestPayload = "";
List<string> requestLines = new List<string>();
byte[] requestBuffer = new byte[1];
byte[] responseBuffer = new byte[1];
requestLines.Clear();
try
{
while (recvRequest && _ContinueRecive)
{
if (this.clientSocket.Receive(requestBuffer) == 0)
{
break;
}
string fromByte = UTF8Encoding.UTF8.GetString(requestBuffer);
requestPayload += fromByte;
if (requestPayload.EndsWith(EOL + EOL))
{
recvRequest = false;
}
}
Socket destServerSocket = new Socket
(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
destServerSocket.Connect(this._Info.ServerIP,this._Info.ServerPort);
destServerSocket.ReceiveTimeout = 12000;
destServerSocket.SendTimeout = 12000;
if (this._Info.EncryptionEnabled)
{
destServerSocket.Send(ASCIIEncoding.ASCII.GetBytes
(Encryption.Encrypt(requestPayload, this._Info.Key) +
"farhadserverfinish"));
}
else
{
destServerSocket.Send(ASCIIEncoding.ASCII.GetBytes
(requestPayload + "farhadserverfinish"));
}
if (false)
{
while (destServerSocket.Receive(responseBuffer) != 0)
{
int tmp = responseBuffer[0];
tmp -= 2;
if (tmp < 0)
{
tmp += 256;
}
responseBuffer[0] = (byte)tmp;
this.clientSocket.Send(responseBuffer);
}
}
else
{
while (destServerSocket.Receive(responseBuffer) != 0)
{
this.clientSocket.Send(responseBuffer);
}
}
destServerSocket.Disconnect(false);
this.clientSocket.Disconnect(false);
}
catch (Exception ex)
{
this._Info.MessageCenter.setMessage(ex.Message);
}
}
Server Side Handler
private void Handler()
{
bool recvRequest = true;
string EOL = "\r\n";
string requestPayload = "";
string requestTempLine = "";
List<string> requestLines = new List<string>();
byte[] requestBuffer = new byte[1];
byte[] responseBuffer = new byte[1];
requestLines.Clear();
try
{
while (recvRequest)
{
this.clientSocket.Receive(requestBuffer);
string fromByte = UTF8Encoding.UTF8.GetString(requestBuffer);
requestPayload += fromByte;
requestTempLine += fromByte;
if (requestPayload.EndsWith("farhadserverfinish"))
{
recvRequest = false;
}
}
string recivedDec;
if (this._Info.EncryptionEnabled)
{
recivedDec = Encryption.Decrypt(requestPayload.Replace
("farhadserverfinish", ""), this._Info.Key);
}
else
{
recivedDec = requestPayload.Replace("farhadserverfinish", "");
}
requestPayload = "";
requestTempLine = "";
int counter = 0;
while (counter < recivedDec.Length)
{
string fromByte = recivedDec[counter].ToString();
counter++;
requestPayload += fromByte;
requestTempLine += fromByte;
if (requestTempLine.EndsWith(EOL))
{
requestLines.Add(requestTempLine.Trim());
requestTempLine = "";
}
if (requestPayload.EndsWith(EOL + EOL))
{
break;
}
}
if (requestLines.Count == 0)
{
return;
}
string remoteHost = requestLines[0].Split(' ')[1].Replace
("http://", "").Split('/')[0];
string requestFile = requestLines[0].Replace("http://", "").Replace
(remoteHost, "");
requestLines[0] = requestFile;
this._Info.MessageCenter.setMessage(string.Format("Request to {0}",
remoteHost));
requestPayload = "";
foreach (string line in requestLines)
{
requestPayload += line;
requestPayload += EOL;
}
Socket destServerSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
destServerSocket.Connect(remoteHost, 80);
destServerSocket.ReceiveTimeout = 12000;
destServerSocket.SendTimeout = 12000;
destServerSocket.Send(ASCIIEncoding.ASCII.GetBytes(requestPayload));
if(false)
while (destServerSocket.Receive(responseBuffer) != 0)
{
int tmp = responseBuffer[0];
tmp += 2;
if (tmp > 255)
{
tmp -= 256;
}
responseBuffer[0] = (byte)tmp;
this.clientSocket.Send(responseBuffer);
}
while (destServerSocket.Receive(responseBuffer) != 0)
{
this.clientSocket.Send(responseBuffer);
}
destServerSocket.Disconnect(false);
this.clientSocket.Disconnect(false);
}
catch (Exception ex)
{
this._Info.MessageCenter.setMessage(ex.Message);
}
}
Inside the Code
This project uses multiple TCP connections and opens many clients and sends requests to the server. Then the server waits for accepting the connection.
There is a thread in the server side that is ready to accept connection on a specific port. The connection between server and client establishes throw socket connection, sockets send and receive information with binary format. These binaries would be sent byte after byte.
private void startAcc()
{
ConnectionInfo info = new ConnectionInfo
(ServerIP.Text, ServerPort.Text, ListenIP.Text,
ListenPort.Text,KeyInput.Text,this,Encrypt.Checked);
proxyListener = new ProxyTCPListener(info);
proxyListener.StartServer();
while (true)
{
proxyListener.AcceptConnection();
}
}
We can add some delay:
private void startAcc()
{
ConnectionInfo info = new ConnectionInfo
(ServerIP.Text, ServerPort.Text, ListenIP.Text,
ListenPort.Text,KeyInput.Text,this,Encrypt.Checked);
proxyListener = new ProxyTCPListener(info);
proxyListener.StartServer();
while (true)
{
proxyListener.AcceptConnection();
}
Thread.Sleep(50);
}
Sockets
Sockets have a big problem. You don't know when to stop. It has just a reading method. We made a con that after sending information, we send this string
: "farhadserverend
". After that, the client knows that sending information is finished. Now, stop client and dispose it!
destServerSocket.Send(ASCIIEncoding.ASCII.GetBytes
(Encryption.Encrypt(requestPayload, this._Info.Key) + "farhadserverfinish"));
Queue for the Countries that have Low Speed Internet (Like IRAN)
In some countries because of low speed internet, we have network problems. So in this class, we made a queue for browser requests. These requests will be checked every time from client application. They have 5 seconds for response timeout. If the connection does not respond in 5 seconds, it will be disposed!
class ProxyTCPListener : IDisposable
{
private TcpListener _Listener;
private ConnectionInfo _Info;
private List<clientconnection> _Clients;
public ProxyTCPListener(ConnectionInfo info)
{
this._Info = info;
this._Listener = new TcpListener(this._Info.LocalIP, this._Info.LocalPort);
this._Clients = new List<clientconnection>();
}
public void StartServer()
{
this._Listener.Start();
}
public void AcceptConnection()
{
if (this._Listener.Pending())
{
Socket newClient = this._Listener.AcceptSocket();
this._Info.MessageCenter.setMessage("ClientQueued...");
ClientConnection _Client = new ClientConnection(newClient, this._Info);
_Clients.Add(_Client);
}
Thread.Sleep(200);
}
public void QueueCheck()
{
if (_Clients.Count > 0)
{
if (_Clients[0]._ContinueRecive == false &&
_Clients[0]._finished == true)
{
_Clients.RemoveAt(0);
}
}
if (_Clients.Count > 0)
{
if (_Clients[0]._ContinueRecive == false &&
_Clients[0]._finished == false)
{
this._Info.MessageCenter.setMessage("ClientAccepted...");
_Clients[0].StartHandling();
}
}
}
public void Dispose()
{
foreach (ClientConnection client in _Clients)
{
client.StopHandling();
}
_Listener.Stop();
}
Encryption
We encrypt the request with AES algorithm:
public static string Encrypt(string clearText, string Password)
{
byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
byte[] encryptedData = Encrypt(clearBytes,
pdb.GetBytes(32), pdb.GetBytes(16));
return Convert.ToBase64String(encryptedData);
}
Decryption
public static string Decrypt(string cipherText, string Password)
{
byte[] cipherBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65,
0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32),
pdb.GetBytes(16));
return System.Text.Encoding.Unicode.GetString(decryptedData);
}
History
- 15th September, 2011: Initial version