Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C# 4.0

Golabi Proxy Server

Rate me:
Please Sign up or sign in to vote.
4.79/5 (10 votes)
20 Sep 2011CPOL2 min read 65.4K   3.7K   21   28
This is a proxy server with a client that can encrypt your browser request.
softwareuut

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.png

Server.png

Client Side Handler

C#
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);
            }
        }

Server Side Handler

C#
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);
            }
        }

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.

C#
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:

C#
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!

C#
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!

C#
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:

C#
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);
        }

Decryption

C#
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);
        }

History

  • 15th September, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer رایان پایا داده محاسب
Iran (Islamic Republic of) Iran (Islamic Republic of)
Bachelor of computer software engineer at Urmia University of Technology,

Masters of computer software engineer at Shahid Beheshti University,

Microsoft Certified Solution Developer : Web Applications,

Microsoft Specialist : HTML5 , CSS3 , javascript,

MCTS at Mojtame Fanni Tehran (MFT)


My Server Solution Blog

Comments and Discussions

 
Questionquestion regarding IDE to use Pin
Member 1369234822-Feb-18 23:39
Member 1369234822-Feb-18 23:39 
QuestionWhat is if(false) Pin
Brackets2-Aug-16 8:05
Brackets2-Aug-16 8:05 
AnswerRe: What is if(false) Pin
Farhad Hazraty Eini5-Aug-16 5:20
professionalFarhad Hazraty Eini5-Aug-16 5:20 
QuestionSpecification Pin
Brackets2-Aug-16 8:03
Brackets2-Aug-16 8:03 
Questionسوال Pin
omid-hh6-Jun-13 4:54
omid-hh6-Jun-13 4:54 
AnswerRe: سوال Pin
Farhad Hazraty Eini6-Jun-13 17:33
professionalFarhad Hazraty Eini6-Jun-13 17:33 
GeneralMessage Closed Pin
6-Jun-13 21:00
omid-hh6-Jun-13 21:00 
GeneralRe: سوال Pin
Farhad Hazraty Eini6-Jun-13 22:05
professionalFarhad Hazraty Eini6-Jun-13 22:05 
Questionquestion2 Pin
Member 100617653-Jun-13 1:48
Member 100617653-Jun-13 1:48 
AnswerRe: question2 Pin
Farhad Hazraty Eini3-Jun-13 18:12
professionalFarhad Hazraty Eini3-Jun-13 18:12 
Questionquestion Pin
Member 1006176531-May-13 22:31
Member 1006176531-May-13 22:31 
AnswerRe: question Pin
Farhad Hazraty Eini1-Jun-13 0:49
professionalFarhad Hazraty Eini1-Jun-13 0:49 
GeneralRe: question Pin
Member 100617651-Jun-13 19:31
Member 100617651-Jun-13 19:31 
QuestionHow to implement for https Pin
Member 877285016-Apr-13 23:20
Member 877285016-Apr-13 23:20 
AnswerRe: How to implement for https Pin
Farhad Hazraty Eini17-Apr-13 5:16
professionalFarhad Hazraty Eini17-Apr-13 5:16 
Questionnasb dar server Pin
Member 99632363-Apr-13 9:37
Member 99632363-Apr-13 9:37 
AnswerRe: nasb dar server Pin
Farhad Hazraty Eini3-Apr-13 17:59
professionalFarhad Hazraty Eini3-Apr-13 17:59 
GeneralRe: nasb dar server Pin
Member 99632364-Apr-13 21:55
Member 99632364-Apr-13 21:55 
GeneralRe: nasb dar server Pin
Member 99632364-Apr-13 22:32
Member 99632364-Apr-13 22:32 
GeneralRe: nasb dar server Pin
Farhad Hazraty Eini4-Apr-13 22:59
professionalFarhad Hazraty Eini4-Apr-13 22:59 
QuestionServer Installation Pin
hadi_aghasi23-Dec-11 22:46
hadi_aghasi23-Dec-11 22:46 
AnswerRe: Server Installation Pin
Farhad Hazraty Eini24-Dec-11 2:25
professionalFarhad Hazraty Eini24-Dec-11 2:25 
This application need a dedicated server or Virtual Private Server known as (VPS)

but if you want to have an proxy website i sugget u to take a look at this :

ASProxy: Surf in the web invisibly using ASP.NET power[^]
GeneralMy vote of 5 Pin
Member 432084427-Sep-11 7:56
Member 432084427-Sep-11 7:56 
SuggestionWhat about content Encrypting ? Pin
RabinDl17-Sep-11 3:34
RabinDl17-Sep-11 3:34 
GeneralRe: What about content Encrypting ? Pin
Farhad Hazraty Eini17-Sep-11 18:27
professionalFarhad Hazraty Eini17-Sep-11 18:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.