Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Objective : Create a proxy server that can be connected by a single client and w

ID: 668790 • Letter: O

Question

Objective: Create a proxy server that can be connected by a single client and would only allow http requests.

Requirements:

1. Create a C based client-server architecture using sockets

2. The server should be able to accept and service single client’s http requests

3. The server should be run on cse01.cse.unt.edu machine and the client should be run on cse02.cse.unt.edu machine

Procedure:

1. Create a C-based server that can accept single client’s request using sockets

2. The created server should also be able to connect to the client requested website through port 80

3. Make sure the server runs on cse01.cse.unt.edu

4. Create a C-based client that can connect the server using sockets

5. Make sure the client runs on cse02.cse.unt.edu and connects the server

6. When the connection to the server is made the user can enter the web page address on the client side

7. The client requests the server for the web page

8. Capture the requested webpage on the client side and verify to see if the captured page is the same as the browser returned page.

Explanation / Answer

wserver.c

// Include the required header files
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "Wbhelper.h"
#include "wservreq.h"
#define WSERVER_PORT(80)

/* main() method */
int main(int argc, char *argv[])
{
   // Declare the required variables
    int    wblistener, wbcon;
    pid_t wbpid;
    struct sockaddr_in wbservaddr;
  
    /* Create the listening socket */
    if ( (wblistener = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
   WbError_Quit("Not able to create listening socket.");


    /* Socket address structure generation*/
    memset(&wbservaddr, 0, sizeof(wbservaddr));
    wbservaddr.sin_family      = AF_INET;
    wbservaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    wbservaddr.sin_port        = htons(WSERVER_PORT);


    /* Assign socket address */
    if ( bind(wblistener, (struct sockaddr *) &wbservaddr, sizeof(wbservaddr)) < 0 )
   WbError_Quit("Not able to bind socket.");


    /* Create a listening socket */
    if ( listen(wblistener, LISTENQ) < 0 )
   WbError_Quit("Listen failed.");


    /* Infinite loop to accept and service connections */
    while ( 1 )
   {
   /* connection wait*/
   if ( (wbcon = accept(wblistener, NULL, NULL)) < 0 )
        WbError_Quit("Error in the calling of accept()");


   /* Fork child process */
   if ( (wbpid = fork()) == 0 ) {

        /* close listening socket and service request   */
        if ( close(wblistener) < 0 )
       WbError_Quit("Error in closing listening socket.");
        WbService_Request(wbcon);

        /* Close connected socket and exit */
        if ( close(wbcon) < 0 )
       WbError_Quit("Error in closing the connection socket.");
        exit(EXIT_SUCCESS);
   }

   /* Code to close socket, clean the processes,
        and to accept new connection. */
   if ( close(wbcon) < 0 )
        WbError_Quit("Error in closing connection in parent.");
       waitpid(-1, NULL, WNOHANG);
    }
    return EXIT_FAILURE;


wservreq.h

#ifndef WEB_SERVREQ_H
#define WEB_SERVREQ_H

/* Function prototypes */
int WbService_Request(int wbcon);
#endif /* WEB_SERVREQ_H */

wservreq.c

#include <stdio.h>
#include <errno.h>
#include "Wbhelper.h"
#include "wbreqhead.h"
#include "Wbresphead.h"
#include "wbresource.h"

/* HTTP request */
int WbService_Request(int wbcon)
{
    struct WBReqInfo wbreqinfo;
    int wbresource = 0;
    WbInitReqInfo(&wbreqinfo);
  
    /* Get HTTP request */
    if ( WbGet_Request(wbcon, &wbreqinfo) < 0 )
   return -1;
  
    /* Code to check existence of resource, permission
   to access, and update status code. */

    if ( wbreqinfo.status == 200 )
   if ( (wbresource = WbCheck_Resource(&wbreqinfo)) < 0 ) {
        if ( errno == EACCES )
       wbreqinfo.status = 401;
        else
       wbreqinfo.status = 404;
   }
  
    if ( wbreqinfo.type == FULL )
   WbOutput_HTTP_Headers(wbcon, &wbreqinfo);
    if ( wbreqinfo.status == 200 ) {
   if ( WbReturn_Resource (wbcon, wbresource, &wbreqinfo) )
        WbError_Quit("Something is wrong to return resource.");
    }
    else
   WbReturn_Error_Msg(wbcon, &wbreqinfo);

    if ( wbresource > 0 )
   if ( close(wbresource) < 0 )
        WbError_Quit("Error in closing the resource.");
    WbFreeReqInfo(&wbreqinfo);
    return 0;
}

wbreqhead.h

#ifndef WEBPG_REQHEAD_H
#define WEBPG_REQHEAD_H

/* User-defined data types */
enum WBReq_Method { GET, HEAD, UNSUPPORTED };
enum WBReq_Type   { SIMPLE, FULL };
struct WBReqInfo {
    enum WBReq_Method method;
    enum WBReq_Type   type;
    char           *referer;
    char           *useragent;
    char           *wbresource;
    int             status;
};
#define WBMAX_REQ_LINE         (1024)

/* Function prototypes */
int WBParse_HTTP_Header(char * Wbuffer, struct WBReqInfo * wbreqinfo);
int WbGet_Request      (int wbcon, struct WBReqInfo * wbreqinfo);
void WbInitReqInfo      (struct WBReqInfo * wbreqinfo);
void WbFreeReqInfo      (struct WBReqInfo * wbreqinfo);
#endif /* WEBPG_REQHEAD_H */


wbreqhead.c
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "wbreqhead.h"
#include "wservreq.h"
#include "Wbhelper.h"

/* Code to parse the strings and update a request
    information */
int WBParse_HTTP_Header(char * Wbuffer, struct WBReqInfo * wbreqinfo) {

    static int Wbfirst_header = 1;
    char      *Wbtemp;
    char      *endptr;
    int        len;

    if ( Wbfirst_header == 1 )
   {
   if ( !strncmp(Wbuffer, "GET ", 4) ) {
        wbreqinfo->method = GET;
        Wbuffer += 4;
   }
   else if ( !strncmp(Wbuffer, "HEAD ", 5) ) {
        wbreqinfo->method = HEAD;
        Wbuffer += 5;
   }
   else {
        wbreqinfo->method = UNSUPPORTED;
        wbreqinfo->status = 501;
        return -1;
   }

   /* Skip to start */
   while ( *Wbuffer && isspace(*Wbuffer) )
        Wbuffer++;

   /* Calculate string length */
   endptr = strchr(Wbuffer, ' ');
   if ( endptr == NULL )
        len = strlen(Wbuffer);
   else
        len = endptr - Wbuffer;
   if ( len == 0 )
   {
        wbreqinfo->status = 400;
        return -1;
   }

   wbreqinfo->wbresource = calloc(len + 1, sizeof(char));
   strncpy(wbreqinfo->wbresource, Wbuffer, len);

   if ( strstr(Wbuffer, "HTTP/") )
        wbreqinfo->type = FULL;
   else
        wbreqinfo->type = SIMPLE;

   Wbfirst_header = 0;
   return 0;
    }
    endptr = strchr(Wbuffer, ':');
    if ( endptr == NULL ) {
   wbreqinfo->status = 400;
   return -1;
    }

    Wbtemp = calloc( (endptr - Wbuffer) + 1, sizeof(char) );
    strncpy(Wbtemp, Wbuffer, (endptr - Wbuffer));
    WStrUpper(Wbtemp);

    Wbuffer = endptr + 1;
    while ( *Wbuffer && isspace(*Wbuffer) )
   ++Wbuffer;
    if ( *Wbuffer == '' )
        return 0;

    if ( !strcmp(Wbtemp, "USER-AGENT") ) {
        wbreqinfo->useragent = malloc( strlen(Wbuffer) + 1 );
        strcpy(wbreqinfo->useragent, Wbuffer);
    }
    else if ( !strcmp(Wbtemp, "REFERER") ) {
        wbreqinfo->referer = malloc( strlen(Wbuffer) + 1 );
        strcpy(wbreqinfo->referer, Wbuffer);
    }

    free(Wbtemp);
    return 0;
}

int WbGet_Request(int wbcon, struct WBReqInfo * wbreqinfo) {

    char   Wbuffer[WBMAX_REQ_LINE] = {0};
    int    Wbrval;
    fd_set Wbfds;
    struct timeval Wbtv;

    /* Timeout to 5 seconds */
    Wbtv.Wbtv_sec = 5;
    Wbtv.Wbtv_usec = 0;
    do
   {
   FD_ZERO(&Wbfds);
   FD_SET (wbcon, &Wbfds);
   Wbrval = select(wbcon + 1, &Wbfds, NULL, NULL, &Wbtv);

   if ( Wbrval < 0 ) {
        WbError_Quit("Error calling select() in get_request()");
   }
   else if ( Wbrval == 0 ) {
        return -1;

   }
   else {
        WReadline(wbcon, Wbuffer, WBMAX_REQ_LINE - 1);
        WTrim(Wbuffer);
        if ( Wbuffer[0] == '' )
       break;
        if ( WBParse_HTTP_Header(Wbuffer, wbreqinfo) )
       break;
   }
    } while ( wbreqinfo->type != SIMPLE );
    return 0;
}


/* Request information structure initialization */
void WbInitReqInfo(struct WBReqInfo * wbreqinfo) {
    wbreqinfo->useragent = NULL;
    wbreqinfo->referer   = NULL;
    wbreqinfo->wbresource = NULL;
    wbreqinfo->method    = UNSUPPORTED;
    wbreqinfo->status    = 200;        
}

/* Free the allocated memory */
void WbFreeReqInfo(struct WBReqInfo * wbreqinfo)
{
    if ( wbreqinfo->useragent )
   free(wbreqinfo->useragent);
    if ( wbreqinfo->referer )
   free(wbreqinfo->referer);
    if ( wbreqinfo->wbresource )
   free(wbreqinfo->wbresource);
}


Wbresphead.h

#ifndef WbPG_RESPHEAD_H
#define WbPG_RESPHEAD_H
#include "wbreqhead.h"
/* Function prototypes */
int WbOutput_HTTP_Headers(int wbcon, struct WBReqInfo * wbreqinfo);
#endif /* WbPG_RESPHEAD_H */

Wbresphead.c

#include <unistd.h>
#include <stdio.h>
#include "Wbresphead.h"
#include "Wbhelper.h"

/* Output of HTTP response headers */
int WbOutput_HTTP_Headers(int wbcon, struct WBReqInfo * wbreqinfo) {
    char Wbuffer[100];
    sprintf(Wbuffer, "HTTP/1.0 %d OK ", wbreqinfo->status);
    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));
    WWriteline(wbcon, "Server: PGWebServ v0.1 ", 24);
    WWriteline(wbcon, "Content-Type: text/html ", 25);
    WWriteline(wbcon, " ", 2);
    return 0;
}

wbresource.h

#ifndef WbPG_RESOURCE_H
#define WbPG_RESOURCE_H
#include "wbreqhead.h"
/* Function prototypes */
int WbReturn_Resource (int wbcon, int wbresource, struct WBReqInfo * wbreqinfo);
int WbCheck_Resource (struct WBReqInfo * wbreqinfo);
int WbReturn_Error_Msg(int wbcon, struct WBReqInfo * wbreqinfo);
#endif /* WbPG_RESOURCE_H */

wbresource.c
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include "wbresource.h"
#include "wbreqhead.h"
#include "Wbhelper.h"
// server root
static char server_root[1000] = "/home/httpd/html";

/* Return a resource */
int WbReturn_Resource (int wbcon, int wbresource, struct WBReqInfo * wbreqinfo)
{
    char Wc;
    int Wi;
    while ( (Wi = read(wbresource, &Wc, 1)) ) {
   if ( Wi < 0 )
        WbError_Quit("Error reading from file.");
   if ( write(wbcon, &Wc, 1) < 1 )
        WbError_Quit("Error sending file.");
    }
    return 0;
}

/* Method to open a resource and determines the cause of failure    */
int WbCheck_Resource(struct WBReqInfo * wbreqinfo)
{
    WCleanURL(wbreqinfo->wbresource);
    strcat(server_root, wbreqinfo->wbresource);
    return open(server_root, O_RDONLY);
}

/* Method to return an error message */
int WbReturn_Error_Msg(int wbcon, struct WBReqInfo * wbreqinfo)
{
    char Wbuffer[100];
    sprintf(Wbuffer, "<HTML> <HEAD> <TITLE>Server Error %d</TITLE> "
                "</HEAD> ", wbreqinfo->status);
    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));
    sprintf(Wbuffer, "<BODY> <H1>Server Error %d</H1> ", wbreqinfo->status);
    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));
    sprintf(Wbuffer, "<P>The request could not be completed.</P> "
                "</BODY> </HTML> ");
    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));
    return 0;
}

Wbhelper.h
#ifndef WBPG_HELPER_H
#define WBPG_HELPER_H
#include <unistd.h>

/* Function prototypes */
void    WbError_Quit(char const * msg);
int     WTrim      (char * Wbuffer);
int     WStrUpper (char * Wbuffer);
void    WCleanURL (char * Wbuffer);
ssize_t WReadline (int Wsockd, void *Wvptr, size_t maxlen);
ssize_t WWriteline (int Wsockd, const void *Wvptr, size_t n);

#define LISTENQ          (1024)

#endif /* WBPG_HELPER_H */

Wbhelper.c

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include "Wbhelper.h"

/* Method to print an error message and quit */
void WbError_Quit(char const * msg)
{
    fprintf(stderr, "WEBSERV: %s ", msg);
    exit(EXIT_FAILURE);
}

/* Method to read a line from a socket */
ssize_t WReadline(int Wsockd, void *Wvptr, size_t maxlen)
{
    ssize_t n, wrc;
    char    Wc, *Wbuffer;

    Wbuffer = Wvptr;

    for ( n = 1; n < maxlen; n++ ) {
  
   if ( (wrc = read(Wsockd, &Wc, 1)) == 1 ) {
        *Wbuffer++ = Wc;
        if ( Wc == ' ' )
       break;
   }
   else if ( wrc == 0 ) {
        if ( n == 1 )
       return 0;
        else
       break;
   }
   else {
        if ( errno == EINTR )
       continue;
        WbError_Quit("Error in WReadline()");
   }
    }

    *Wbuffer = 0;
    return n;
}

/* Method to write a line to a socket */
ssize_t WWriteline(int Wsockd, const void *Wvptr, size_t n) {
    size_t      wnleft;
    ssize_t     wnwritten;
    const char *Wbuffer;

    Wbuffer = Wvptr;
    wnleft = n;

    while ( wnleft > 0 ) {
   if ( (wnwritten = write(Wsockd, Wbuffer, wnleft)) <= 0 ) {
        if ( errno == EINTR )
       wnwritten = 0;
        else
       WbError_Quit("Error in WWriteline()");
   }
   wnleft -= wnwritten;
   Wbuffer += wnwritten;
    }

    return n;
}


/* Method to remove white space in a string */
int WTrim(char * Wbuffer)
{
    int n = strlen(Wbuffer) - 1;
    while ( !isalnum(Wbuffer[n]) && n >= 0 )
   Wbuffer[n--] = '';
    return 0;
}

/* Method to convert a string into uppercase */
int WStrUpper(char * Wbuffer)
{
    while ( *Wbuffer )
   {
   *Wbuffer = toupper(*Wbuffer);
   ++Wbuffer;
    }
    return 0;
}

/* Method to cleans url encoded string */
void WCleanURL(char * Wbuffer) {
    char asciinum[3] = {0};
    int Wi = 0, Wc;
  
    while ( Wbuffer[Wi] ) {
   if ( Wbuffer[Wi] == '+' )
        Wbuffer[Wi] = ' ';
   else if ( Wbuffer[Wi] == '%' ) {
        asciinum[0] = Wbuffer[Wi+1];
        asciinum[1] = Wbuffer[Wi+2];
        Wbuffer[Wi] = strtol(asciinum, NULL, 16);
        Wc = Wi+1;
        do {
       Wbuffer[Wc] = Wbuffer[Wc+2];
        } while ( Wbuffer[2+(Wc++)] );
   }
   ++Wi;
    }
}