/* 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);
}