Golabi 代理服务器






4.79/5 (10投票s)
这是一个可以加密您的浏览器请求的代理服务器和客户端。

引言
Golabi 代理是一种保留代理。此代理对浏览器的请求进行加密,以绕过互联网过滤。
背景
在某些国家/地区,该国的互联网服务提供商会对请求进行某种检查,以阻止对网站的未经授权的请求。例如,Facebook 就是被阻止的网站之一!
这些请求检查有一个很大的弱点!它们无法检查加密的请求和响应。因此,我们使用 AES 算法加密请求。AES 算法的密钥可以通过一些秘密 IP 和秘密端口发送。但在本项目中,我们使用一个密钥作为秘密密钥。
Using the Code
在这个项目中,我们有两个方面 - 服务器端和客户端。
首先,我们在客户端计算机上使用客户端 Golabi 代理接收来自浏览器的请求。接下来,我们加密请求。之后,服务器端接收加密的请求并解密它。在服务器端,我们的应用程序(服务器 Golabi 代理)可以无限制地访问互联网。它发出请求并接收响应。然后响应返回给客户端和浏览器。
这个项目只是一个想法的开始!它有一些问题,但可以通过一些改进来解决。
客户端处理程序
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
{
//State 0: Handle Request from Client
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;
}
}
//State 1: Creating a new socket to connect to server
Socket destServerSocket = new Socket
(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
destServerSocket.Connect(this._Info.ServerIP,this._Info.ServerPort);
destServerSocket.ReceiveTimeout = 12000;
destServerSocket.SendTimeout = 12000;
//State 2: Sending New Request Information to Destination Server
//and Relay Response to Client
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);
}
}
服务器端处理程序
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;
//State 0: Handle Request from Client
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;
}
}
//State 1: Rebuilding Request Information and Create Connection
//to Destination Server
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;
//State 2: Sending New Request Information to Destination Server
//and Relay Response to Client
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);
}
}
代码内部
该项目使用多个 TCP 连接,打开许多客户端并向服务器发送请求。然后服务器等待接受连接。
服务器端有一个线程,准备好在特定端口上接受连接。服务器和客户端之间的连接通过套接字连接建立,套接字以二进制格式发送和接收信息。这些二进制数据将逐字节发送。
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();
}
}
我们可以添加一些延迟
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)
套接字有一个很大的问题。你不知道什么时候停止。它只有一个读取方法。我们做了一个约定,在发送信息后,我们会发送这个字符串:“farhadserverend
”。之后,客户端就知道发送信息完成了。现在,停止客户端并释放它!
destServerSocket.Send(ASCIIEncoding.ASCII.GetBytes
(Encryption.Encrypt(requestPayload, this._Info.Key) + "farhadserverfinish"));
为互联网速度较慢的国家/地区(如伊朗)的队列
在某些国家/地区,由于互联网速度较慢,我们存在网络问题。因此,在这个类中,我们为浏览器请求创建了一个队列。这些请求将由客户端应用程序随时检查。它们有 5 秒的响应超时时间。如果 5 秒内没有响应,它将被释放!
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();
}
加密
我们使用 AES 算法加密请求
public static string Encrypt(string clearText, string Password)
{
// First we need to turn the input string into a byte array.
byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
// Then, we need to turn the password into Key and IV
// We are using salt to make it harder to guess our key
// using a dictionary attack -
// trying to guess a password by enumerating all possible words.
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
// Now get the key/IV and do the encryption using the
// function that accepts byte arrays.
// Using PasswordDeriveBytes object we are first getting
// 32 bytes for the Key
// (the default Rijndael key length is 256bit = 32bytes)
// and then 16 bytes for the IV.
// IV should always be the block size, which is by default
// 16 bytes (128 bit) for Rijndael.
// If you are using DES/TripleDES/RC2 the block size is
// 8 bytes and so should be the IV size.
// You can also read KeySize/BlockSize properties off
// the algorithm to find out the sizes.
byte[] encryptedData = Encrypt(clearBytes,
pdb.GetBytes(32), pdb.GetBytes(16));
// Now we need to turn the resulting byte array into a string.
// A common mistake would be to use an Encoding class for that.
//It does not work because not all byte values can be
// represented by characters.
// We are going to be using Base64 encoding that is designed
//exactly for what we are trying to do.
return Convert.ToBase64String(encryptedData);
}
解密
public static string Decrypt(string cipherText, string Password)
{
// First we need to turn the input string into a byte array.
// We presume that Base64 encoding was used
byte[] cipherBytes = Convert.FromBase64String(cipherText);
// Then, we need to turn the password into Key and IV
// We are using salt to make it harder to guess our key
// using a dictionary attack -
// trying to guess a password by enumerating all possible words.
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65,
0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
// Now get the key/IV and do the decryption using
// the function that accepts byte arrays.
// Using PasswordDeriveBytes object we are first
// getting 32 bytes for the Key
// (the default Rijndael key length is 256bit = 32bytes)
// and then 16 bytes for the IV.
// IV should always be the block size, which is by
// default 16 bytes (128 bit) for Rijndael.
// If you are using DES/TripleDES/RC2 the block size is
// 8 bytes and so should be the IV size.
// You can also read KeySize/BlockSize properties off
// the algorithm to find out the sizes.
byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32),
pdb.GetBytes(16));
// Now we need to turn the resulting byte array into a string.
// A common mistake would be to use an Encoding class for that.
// It does not work
// because not all byte values can be represented by characters.
// We are going to be using Base64 encoding that is
// designed exactly for what we are trying to do.
return System.Text.Encoding.Unicode.GetString(decryptedData);
}
历史
- 2011 年 9 月 15 日:初始版本