/* HTTP PROXYSERVER */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "Filter.h"

#define QLEN 100    /* maximum connection queue length      */
#define BUFSIZE 8192
#define LINELEN 128
#define TIMEOUT 10
#define PORT "80"

#define CACHE_SIZE 200


extern int      errno;
u_short     portbase = 0;    
void    reaper(int);
int     get_url(int fd);
void    process_url(const char *host, const char *url ,
        int retsocket, const char* cacheurl);
int     cached(const char* cacheurl, char *cachefileptr);
int     errexit(const char *format, ...);
int     listenTCP(const char *service, int qlen);
int     connectTCP(const char *host,const char *service);
int     isblocked(const char *url);


Filter filter;                   /* global content filter */
Filter current;                  /* global files currently used */

int main(int argc, char *argv[])
{
  char    *service = "8080";      /* port number to connect with client  */
  struct  sockaddr_in fsin;       /* the address of a client             */
  int     alen;                   /* length of client's address          */
  int     listensock;             /* server's listening socket           */
  int     outsock;                /* socket that returns to client       */
  FILE    *fp, *ap;               /* used to process files               */   
  int     i, j;                   /* temporary int                       */
  char    a[BUFSIZE], b[BUFSIZE]; /* temporary string                    */

  printf("\nHTTP PROXY/CONTENT FILTER\nby Edison Ch and Jason O'Brien\n ");
  switch (argc)
    {
    case    1:
      break;
    case    2:
      service = argv[1];
      break;
    case    3:
      if( argv[2]="-k")
      if( (fp=fopen("hashtbl", "r+"))!=NULL) 
      {
        printf("\nerasing previous cache...");
       
        while  ((i=fscanf(fp, "%s %s",  a,  b)) != EOF)
          {
            unlink(b);
          }      
        fclose(fp);
        unlink("hashtbl");
        if( (fp=fopen("blocked", "r+"))!=NULL) 
          unlink("blocked");
        printf("done\n");
      }


        break;
      default:
        errexit("usage:proxy [port] -k\n");
    }
 
  printf("\nThese words will trigger the content filter:\n");
 if( (ap=fopen("filter", "r+"))!=NULL)
     while ((i=fscanf(ap, "%s",  a)) != EOF)
       {
       printf("%s\n",a);
       filter.setConstraint(a);
       }
 printf("\nThe proxy is now active...\n");
 /*initialize listener socket*/
  listensock = listenTCP(service, QLEN);

  (void) signal(SIGCHLD, reaper);
 


  /*accept socket*/
  while (1) {
    alen = sizeof(fsin);
    outsock = accept(listensock, (struct sockaddr *)&fsin,(socklen_t *) &alen);
 
      if (outsock < 0) {
      if (errno == EINTR)
        continue;
      errexit("accept: %s\n", strerror(errno));
     }

    switch (fork()) {
    case 0:         /* child */
      (void) close(listensock);
     get_url(outsock);
     exit(1);
    default:        /* parent */
      (void) close(outsock);
      break;
    case -1:
      errexit("fork: %s\n", strerror(errno));
    }
  }

}

/*gets request from socket
 *parses it into url and file
 *sends the url and file to process_url to be proxyd
 */


int get_url(int ssock)
{
  char cacheurl[BUFSIZE]; 
  char *tmp_ptr;
  char  httpcommand[BUFSIZE+1];

  char  received_request[BUFSIZE+1];
  char  targetserver[LINELEN];
  char  targeturl[BUFSIZE];
  char *clientcommand;
  char *clienturl;
  char *clientversion;
 
/* read request and parse it */

  read(ssock, received_request, sizeof(received_request));

  clientcommand = strtok(received_request, " ");
  clienturl     = strtok(NULL, " ");
  clientversion = strtok(NULL, "\n");

/**********************************************************/
/*  retrieve name of targethost and target file from url  */
/**********************************************************/

  strcpy( cacheurl, clienturl );

  tmp_ptr = strstr( clienturl, "/");
  tmp_ptr = strstr( ++tmp_ptr, "/");
  strcpy( targeturl, strstr( ++tmp_ptr, "/") );

  tmp_ptr = strstr( clienturl, "/");
  tmp_ptr = strstr( ++tmp_ptr, "/");
  strcpy( targetserver, ++tmp_ptr);
  (void) strtok(targetserver, "/");
  (targetserver);
  process_url(targetserver, targeturl, ssock, cacheurl);
  close(ssock);

}

/*
 * checks the URL against filter
 * then checks if it is in cache
 * if not in cache
 *  buffer URL into file
 *    check buffer against filter
 *  if buffers pass,
 *    send file to client
 *  else put URL on blocked list
 *  and send notification to client
 */
void process_url(const char *host, const char *url,
int retsocket, const char *cacheurl)
{
  //
  int cachekey = 0;
  char cachekeystr[BUFSIZE];
  char cachefile[BUFSIZE];
  int cacheflag = 0;
  int filterflag = -1;
  int htmflag =0;
  int blockedflag =0;
  int cwfd;
  FILE *fp, *ap;
  int i, j;
  char a[LINELEN];
  char    buf[BUFSIZE+1];         /* buffer for one line of text  */
  char    buf2[BUFSIZE+1];
  int     fwdsocket, cshsocket;
  int     n;                      /* socket, read count           */
  char    *tmpcachefile;
  char    *httphead="GET ";
  char    *httpversion=" HTTP/1.0";
  char    httpcommand[LINELEN+1];
  int key;

  strcpy(httpcommand, httphead);
  if (url == NULL) {
    strcat(httpcommand, "/");
  }
  else {
    strcat(httpcommand, url);
  }

  strcat(httpcommand, httpversion);
  strcat(httpcommand, "\n\n");

 /* check if URL triggers filter */
 if( !(blockedflag = isblocked(cacheurl))  &&
     ((filterflag = filter.search(cacheurl)) < 0) &&
     ((cacheflag = cached(cacheurl, cachefile)) < 0) )
   {
     /* get URL */
     fwdsocket = connectTCP(host, PORT);
     write(fwdsocket, httpcommand, sizeof(httpcommand));
    
     /* create file and write URL to file */    
     cwfd = creat (cachefile, S_IRUSR | S_IWUSR | S_IXUSR);
    
     /* read block of data
      * if data is an .htm file, filter it
      * if block triggers filter, break
      * else write block to file
      */
    
     while( (n = read(fwdsocket, buf, LINELEN)) > 0 )
      
       if(( filterflag= filter.search(buf))>0)
       break;
       else
       write(cwfd, buf, n);        
   }

 close(cwfd);
 close(fwdsocket);
 
 /*if filter is triggered
  * notify client
  *put url on banned list
  *erase file
  */
 

 if((filterflag >= 0) || blockedflag)
   {
     current.deleteConstraint(current.searchConstraint(cachefile));
     printf(":: WEB PAGE BLOCKED!:%s : %s",
          cacheurl, filter.showConstraint(filterflag));
     cshsocket =open("WARNING.html", O_RDONLY);
     while( (n = read(cshsocket, buf2, LINELEN)) > 0 )
       write(retsocket, buf2, n);
     close(cshsocket);
     if(!blockedflag)
       {
       unlink(cachefile);    
       fp=fopen("blocked", "a+");
       fprintf(fp, "%s\n", cacheurl);
       fclose(fp);
       }
   }
 else  
    {

      if (cacheflag >= 0)
      printf(":: hashtable accessed: %s= %s\n",cacheurl, cachefile);
      else
      {
        printf(":: hashtbl updated: %s= %s\n",cacheurl, cachefile);
        if ( (fp=fopen("hashtbl", "a")) == NULL )
          printf(":: file open error!\n");
        fprintf(fp, "%s\t%s\n", cacheurl, cachefile);
        fclose(fp);
      }
      cshsocket = open(cachefile,O_RDONLY);
      while( (n = read(cshsocket, buf, LINELEN)) > 0 )
      write(retsocket, buf, n);
      close(retsocket);
      current.deleteConstraint(current.searchConstraint(cachefile));
    }
} // end of function


/* checks blocked file for url
 * returns 1 if found, 0 if not
 */
int isblocked(const char *url)
{
  int i;
  FILE *fp;
  char tmpurl[BUFSIZE];
  if( (fp=fopen("blocked", "r+"))==NULL)
    return 0;
  while  ((i=fscanf(fp, "%s", tmpurl)) != EOF)
    if( (strcmp( url, tmpurl )) == 0)
      return 1;
  return 0;
}//end of function


/* checks hashtable file for cache url
 * if found it set cachefileptr to the file
 *   and returns the line it was found on 
 * if not found it creates a file for the url in hashtable
 *   sets cachefileptr to that file
 *   and returns the negative number ofthe line it was stored on
 */   
int cached(const char* cacheurl, char *cachefileptr)
{
  int i=0;
  char tbl_url[BUFSIZE];
  char tbl_filename[BUFSIZE];
  FILE *fp;
  int c;

  if ( (fp = fopen("hashtbl", "r+")) == NULL )
    fp = fopen("hashtbl", "w+");

  else
    while  ((c=fscanf(fp, "%s %s", tbl_url, tbl_filename)) != EOF)
      if( (strcmp( tbl_url, cacheurl ) == 0))
      {
        strcpy(cachefileptr, tbl_filename);/* if found copy location */
        fclose(fp);
        return i;
      }
     
  /*  if file is currently in use but not in the hashtable, get another one */

  while (c=current.searchConstraint(tmpnam(cachefileptr))>0);

  current.setConstraint(cachefileptr);
  return -1;
}//end of function




/*
 * reaper - clean up zombie children
 *
 */

void reaper(int sig)
{
  int     status;
 
  while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)
          /* empty */;
}



int
connectTCP(const char *host, const char *service )

/*
 * Arguments:
 *      host      - name of host to which connection is desired
 *      service   - service associated with the desired port
 */
{
      struct hostent    *phe; /* pointer to host information entry      */
      struct servent    *pse; /* pointer to service information entry   */
      struct protoent *ppe;   /* pointer to protocol information entry*/
      struct sockaddr_in sin; /* an Internet endpoint address           */
      int   s, type;    /* socket descriptor and socket type      */


      memset(&sin, 0, sizeof(sin));
      sin.sin_family = AF_INET;

    /* Map service name to port number */
      if ( (pse = getservbyname(service, "tcp")) )
            sin.sin_port = pse->s_port;
      else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
            errexit("can't get \"%s\" service entry\n", service);

    /* Map host name to IP address, allowing for dotted decimal */
      if ( (phe = gethostbyname(host)) )
            memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
      else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )
            errexit("can't get \"%s\" host entry\n", host);

    /* Map transport protocol name to protocol number */
      if ( (ppe = getprotobyname("tcp")) == 0)
            errexit("can't get TCP protocol entry\n");


    /* Allocate a socket */
      s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
      if (s < 0)
            errexit("can't create socket: %s\n", strerror(errno));

    /* Connect the socket */
      if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
            errexit("can't connect to %s.%s: %s\n", host, service,
                  strerror(errno));
      return s;
}




/*------------------------------------------------------------------------
 * passiveTCP - allocate & bind a server socket using TCP or UDP
 *------------------------------------------------------------------------
 */
int
listenTCP(const char *service, int qlen)

/*
 * Arguments:
 *      service   - service associated with the desired port
 *      qlen      - maximum server request queue length
 */
{
        u_short   portbase = 0;
      struct servent    *pse; /* pointer to service information entry   */
      struct protoent *ppe;   /* pointer to protocol information entry*/
      struct sockaddr_in sin; /* an Internet endpoint address           */
      int   s, type, yes=1;   /* socket descriptor and socket type      */

      memset(&sin, 0, sizeof(sin));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = INADDR_ANY;

    /* Map service name to port number */
      if ( (pse = getservbyname(service, "tcp")) )
            sin.sin_port = htons(ntohs((u_short)pse->s_port)
                  + portbase);
      else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
            errexit("can't get \"%s\" service entry\n", service);

    /* Map protocol name to protocol number */
      if ( (ppe = getprotobyname("tcp")) == 0)
            errexit("can't get TCP protocol entry\n");


    /* Allocate a socket */
      s = socket(PF_INET,  SOCK_STREAM, ppe->p_proto);
      if (s < 0)
        errexit("can't create socket: %s\n", strerror(errno));
  /* set socket to non blocking */ 
      if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes,
                  
                   sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
      }

      /* Bind the socket */
      if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        errexit("can't bind to %s port: %s\n", service,
              strerror(errno));
      if (listen(s, qlen) < 0)
        errexit("can't listen on %s port: %s\n", service,
              strerror(errno));
      return s;
}


int
errexit(const char *format, ...)
{
  va_list args;

  va_start(args, format);
  vfprintf(stderr, format, args);
  va_end(args);
  exit(1);
}


 

1