Sunday, 19 July 2015

Simple Proxy Server in C using Multi-threading


This articles covers a simple proxy server application for multiple clients in C. It is based on Linux based socket programming.

If you are comfortable with basics of socket programming and multithreading in c then this article is for you!

To revise basics of socket programming, please visit my earlier article. Link:


This proxy server application is as follows:
  1. Three programs will run at a time(in separate Linux terminals, of course..!) 
  • One is main server. It acts as a echo server.
  • One is proxy server. It forwards data from client to main server. And server response back to client
  • Last one is a client. 
     2.  It supports multiple clients.
     3.  There will be only one main server and one proxy server
     4.  Proxy server will accept three arguments from command line. These include server IP address   followed by server port address and then proxy's port address. Since proxy is running on a local machine. Its IP address will be 127.0.0.1 by default. No need to mention that in the command line.
     5. Client will accept two arguments. These include proxy's IP address and port.

Now you can directly jump to the code. Copy-paste it and run it for further study.

For newbies, I'm explaining how programs work in brief. The application flow is as follows:
  1. Client will accept proxy details and connect to the specified proxy through socket
  2. Proxy server will accept server details and store the details in variables
  3. Proxy server also create a socket at the mentioned proxy port and start listening for incoming client connections continuously on that port.
  4. For each successfully accepted client connection, proxy server will create a thread and pass client details(client file descriptor) and server details(ip and port) to that newly created thread. Lets call this thread as client thread.
  5. This client thread will create a socket connection and connect to the main server. Now each thread will be responsible for communication between that client and main server.
  6. While main server will accept client connections and create its own thread for each client. Lets call these threads as server threads. This will help main server to differentiate traffic between multiple clients.
  7. Here, main trick is to manage multiple client connections via proxy server. I implemented it using multithreading. 
  8. Instead of multithreading, you can use a 'select command' which can also serve the same purpose. It is used for synchronous IO multiplexing.  Following link covers the details of it with multi chat client-server application using select command:
    http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html
Why multi-threading?
Answer: Codes are modular in multithreading and all accepted client-server connections are independent of each other. When client exits, that particular thread will exit. But the disadvantage of threads is that threads create complexity in the program.


So, Thats all folks! 

Now you are ready to run your own proxy server application with the help of following code. See the results and modify it as per your requirements.

I wrote comments in the codes for clarity of the logic. 

1. Copy the following code and save in a file called "mainserver.c"

 #include <sys/socket.h>  
 #include <sys/types.h>  
 #include <resolv.h>  
 #include <string.h>  
 #include <pthread.h>  
 #include<unistd.h>  
 // A thread function  
 // A thread is created for each accepted client connection  
 void *runSocket(void *vargp)  
 {  
   int c_fd =(int)vargp; // get client fd from arguments passed to the thread  
   char buffer[65535];  
   int bytes = 0;  
      while(1)  
      {  
           //receive data from client  
           memset(&buffer,'\0',sizeof(buffer));  
           bytes = read(c_fd, buffer, sizeof(buffer));  
           if(bytes <0)  
           {  
                //perror("read");  
           }  
           else if(bytes == 0)  
           {  
           }  
           else  
           {  
                //send the same data back to client  
                // similar to echo server  
                write(c_fd, buffer, sizeof(buffer));  
                //printf("client fd is : %d\n",c_fd);                    
                //printf("From client:\n");                    
                fputs(buffer,stdout);       
           }  
             fflush(stdout);  
      };       
   return NULL;  
 }  
 // main entry point  
 int main()  
 {  
      int client_fd;  
      char buffer[100];  
      int fd = 0 ;  
      struct sockaddr_in server_sd;  
 // add this line only if server exits when client exits  
 signal(SIGPIPE,SIG_IGN);  
      // create a socket  
      fd = socket(AF_INET, SOCK_STREAM, 0);  
      printf("Server started\n");  
      memset(&server_sd, 0, sizeof(server_sd));  
      // set socket variables  
      server_sd.sin_family = AF_INET;  
      server_sd.sin_port = htons(5010);  
      server_sd.sin_addr.s_addr = INADDR_ANY;  
      // bind socket to the port  
      bind(fd, (struct sockaddr*)&server_sd,sizeof(server_sd));  
      // start listening at the given port for new connection requests  
      listen(fd, SOMAXCONN);  
      // continuously accept connections in while(1) loop  
      while(1)  
      {  
           // accept any incoming connection  
           client_fd = accept(fd, (struct sockaddr*)NULL ,NULL);  
           //printf("accepted client with id: %d",client_fd);  
           // if true then client request is accpted  
           if(client_fd > 0)  
           {  
                 //multithreading variables    
                printf("proxy connected\n");     
                pthread_t tid;  
                // pass client fd as a thread parameter  
                 pthread_create(&tid, NULL, runSocket, (void *)client_fd);   
           }  
      }  
      close(client_fd);   
      return 0;  
 }  

2. Copy the following code and save it in a file called 'proxy.c'


 #include <sys/socket.h>  
 #include <sys/types.h>  
 #include <resolv.h>  
 #include <string.h>  
 #include <stdlib.h>  
 #include <pthread.h>  
 #include<unistd.h>  
 #include<netdb.h> //hostent  
 #include<arpa/inet.h>  
 int hostname_to_ip(char * , char *);  
 // A structure to maintain client fd, and server ip address and port address  
 // client will establish connection to server using given IP and port   
 struct serverInfo  
 {  
      int client_fd;  
      char ip[100];  
      char port[100];  
 };  
 // A thread function  
 // A thread for each client request  
 void *runSocket(void *vargp)  
 {  
   struct serverInfo *info = (struct serverInfo *)vargp;  
   char buffer[65535];  
   int bytes =0;  
      printf("client:%d\n",info->client_fd);  
      fputs(info->ip,stdout);  
      fputs(info->port,stdout);  
      //code to connect to main server via this proxy server  
      int server_fd =0;  
      struct sockaddr_in server_sd;  
      // create a socket  
      server_fd = socket(AF_INET, SOCK_STREAM, 0);  
      if(server_fd < 0)  
      {  
           printf("server socket not created\n");  
      }  
      printf("server socket created\n");       
      memset(&server_sd, 0, sizeof(server_sd));  
      // set socket variables  
      server_sd.sin_family = AF_INET;  
      server_sd.sin_port = htons(atoi(info->port));  
      server_sd.sin_addr.s_addr = inet_addr(info->ip);  
      //connect to main server from this proxy server  
      if((connect(server_fd, (struct sockaddr *)&server_sd, sizeof(server_sd)))<0)  
      {  
           printf("server connection not established");  
      }  
      printf("server socket connected\n");  
      while(1)  
      {  
           //receive data from client  
           memset(&buffer, '\0', sizeof(buffer));  
           bytes = read(info->client_fd, buffer, sizeof(buffer));  
           if(bytes <= 0)  
           {  
           }  
           else   
           {  
                // send data to main server  
                write(server_fd, buffer, sizeof(buffer));  
                //printf("client fd is : %d\n",c_fd);                    
                printf("From client :\n");                    
                fputs(buffer,stdout);       
                  fflush(stdout);  
           }  
           //recieve response from server  
           memset(&buffer, '\0', sizeof(buffer));  
           bytes = read(server_fd, buffer, sizeof(buffer));  
           if(bytes <= 0)  
           {  
           }            
           else  
           {  
                // send response back to client  
                write(info->client_fd, buffer, sizeof(buffer));  
                printf("From server :\n");                    
                fputs(buffer,stdout);            
           }  
      };       
   return NULL;  
 }  
 // main entry point  
 int main(int argc,char *argv[])  
 {  
     pthread_t tid;  
     char port[100],ip[100];  
     char *hostname = argv[1];  
     char proxy_port[100];  
        // accept arguments from terminal  
        strcpy(ip,argv[1]); // server ip  
        strcpy(port,argv[2]);  // server port  
        strcpy(proxy_port,argv[3]); // proxy port  
        //hostname_to_ip(hostname , ip);  
        printf("server IP : %s and port %s" , ip,port);   
        printf("proxy port is %s",proxy_port);        
        printf("\n");  
      //socket variables  
      int proxy_fd =0, client_fd=0;  
      struct sockaddr_in proxy_sd;  
 // add this line only if server exits when client exits  
 signal(SIGPIPE,SIG_IGN);  
      // create a socket  
      if((proxy_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  
      {  
          printf("\nFailed to create socket");  
      }  
      printf("Proxy created\n");  
      memset(&proxy_sd, 0, sizeof(proxy_sd));  
      // set socket variables  
      proxy_sd.sin_family = AF_INET;  
      proxy_sd.sin_port = htons(atoi(proxy_port));  
      proxy_sd.sin_addr.s_addr = INADDR_ANY;  
      // bind the socket  
      if((bind(proxy_fd, (struct sockaddr*)&proxy_sd,sizeof(proxy_sd))) < 0)  
      {  
           printf("Failed to bind a socket");  
      }  
      // start listening to the port for new connections  
      if((listen(proxy_fd, SOMAXCONN)) < 0)  
      {  
           printf("Failed to listen");  
      }  
      printf("waiting for connection..\n");  
      //accept all client connections continuously  
      while(1)  
      {  
           client_fd = accept(proxy_fd, (struct sockaddr*)NULL ,NULL);  
           printf("client no. %d connected\n",client_fd);  
           if(client_fd > 0)  
           {  
                 //multithreading variables      
                 struct serverInfo *item = malloc(sizeof(struct serverInfo));  
                 item->client_fd = client_fd;  
                 strcpy(item->ip,ip);  
                 strcpy(item->port,port);  
                 pthread_create(&tid, NULL, runSocket, (void *)item);  
                 sleep(1);  
           }  
      }  
      return 0;  
 }  
 int hostname_to_ip(char * hostname , char* ip)  
 {  
   struct hostent *he;  
   struct in_addr **addr_list;  
   int i;  
   if ( (he = gethostbyname( hostname ) ) == NULL)   
   {  
     // get the host info  
     herror("gethostbyname");  
     return 1;  
   }  
   addr_list = (struct in_addr **) he->h_addr_list;  
   for(i = 0; addr_list[i] != NULL; i++)   
   {  
     //Return the first one;  
     strcpy(ip , inet_ntoa(*addr_list[i]) );  
     return 0;  
   }  
   return 1;  
 }  

3. Copy the following code and save it in a file called 'client.c'

 #include <sys/socket.h>  
 #include <sys/types.h>  
 #include <resolv.h>  
 #include <string.h>  
 // main entry point  
 int main(int argc, char* argv[])  
 {  
      //socket variables  
      char IP[200];  
      char port[200];  
      char buffer[65535];  
      int sd;  
      struct sockaddr_in client_sd;  
      printf("\nEnter proxy address:");  
      fgets(IP,sizeof("127.0.01\n")+1,stdin);  
      fputs(IP,stdout);  
      printf("\nEnter a port:");  
      fgets(port,sizeof("5000\n")+1,stdin);  
      fputs(port,stdout);  
      if((strcmp(IP,"127.0.0.1\n"))!=0 || (strcmp(port,"5000\n"))!=0)  
      {  
           printf("Invalida proxy settings. Try again...");  
      }  
      else  
      {  
           // create a socket  
           if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  
           {  
                printf("socket not created\n");  
           }  
           memset(&client_sd, 0, sizeof(client_sd));  
           // set socket variables  
           client_sd.sin_family = AF_INET;  
           client_sd.sin_port = htons(5000);  
           // assign any IP address to the client's socket  
           client_sd.sin_addr.s_addr = INADDR_ANY;   
           // connect to proxy server at mentioned port number  
           connect(sd, (struct sockaddr *)&client_sd, sizeof(client_sd));  
           //send and receive data contunuously  
           while(1)  
            {  
                printf("Type here:");  
                fgets(buffer, sizeof(buffer), stdin);  
                write(sd, buffer, sizeof(buffer));  
                printf("\nServer response:\n\n");  
                read(sd, buffer, sizeof(buffer));  
                fputs(buffer, stdout);  
                  //printf("\n");       
           };  
           //close(sd);  
      }  
      return 0;  
 }  

How to run the programs?

I'm posting snapshots of my program. Please follow steps in sequence:

1. Compile and run main server





2. Compile and run proxy server






3. Compile and run client










Please note that

  1. -lpthread is a command used to compile the code if multi-threading is used. So, commands to compile proxy.c and mainserver.c are little different from client.c
  2. And client.c file accepts 5000 as a port address for proxy server. It is hard coded in the file. you can either comment that line or write other valid port address if you need it that way.



5 comments:

  1. Hey! This program is pretty helpful.
    I had a doubt whether we can convert this to http proxy server?

    ReplyDelete
  2. Thanks for posting these source modules. I've found them to be exactly precisely what I was looking for; which was simply a C-coded example of cli-to-serv/serv-to-cli interactivity framework atop which (and after some degree of re-working) I can begin integrating a new private P2P network protocol which i'd already begun working on;... however I had missing the basic functionality of proxy emulation which will basically make my library about 3X more effective; that is, if I ever get it finished :) I appreciate your work and would like you to know that i'll most likely use these sources (however revised IDK yet) in part of my project. I hope that's OK. If not, let me know @ strawberry.lokote.13@gmail.com --- thankx again!

    ReplyDelete
  3. hello if I want to download a file via proxy from mainserver.c how do I download a file from client what modifications will there be needed also can the code cache the server response at the proxy so that already downloaded file does not get again downloaded.

    ReplyDelete
  4. I have got segmentation fault(core dumped) error when run proxy.c code

    ReplyDelete
  5. King Casino Login | All your games online and - Community Khabar
    Login King Casino, Play, and Win! Login King Casino, Play. Login King Casino, Play. Login febcasino King Casino, Play. Login King https://jancasino.com/review/merit-casino/ Casino, Play. Login King www.jtmhub.com Casino, Play. Login deccasino King communitykhabar Casino,

    ReplyDelete