#include #include #include #include /* not needed on IRIX */ #include /* #include */ /* not needed on IRIX */ #define TRUE 1 #define FALSE 0 #define NAMEBUF 256 /* buffer for local hostname information */ /* proxy.c -- a trivial proxy forwarder. This program "listens" at a defined port, and then forwards all data from this port to a port on a remote server, and also forwards all data returned from the remote server to the client connecting at the defined port. All data is also copied to a cache file, so that the transactions can be saved and monitored. */ main(argc, argv) int argc; char **argv; { int verbose; int lfd, sfd; int remote_server_fd, remote_client_fd; struct sockaddr_in my_addr, remote_server_addr, client_addr; struct hostent *remote_server_ent; int my_inane; int server_port, local_port; int pid; FILE *sfp, *remote_server_fp; FILE *dump_file; char *remoteServerName = NULL; char *dumpFileName = NULL; int one = 1; int lhostlen = NAMEBUF; char localHostName[NAMEBUF]; /* following are for the getopt() function */ extern char *optarg; extern int optint; int errflg=0; int sflag=0; int oflag=0; int c; char *ofile=NULL; verbose = 0; server_port = 80; local_port = 0; while(( c = getopt(argc, argv, "o:l:p:s:v")) != -1 ) { switch(c) { case 'o': dumpFileName = strdup(optarg); break; case 'l': local_port = atoi(optarg); break; case 'p': server_port = atoi(optarg); break; case 's': remoteServerName = strdup(optarg); sflag++; break; case 'v': verbose = 1; break; case '?': errflg++; break; } } if( errflg || !sflag ) { fprintf(stderr,"usage: monitor (-o) (-l) -s (-p ) (-v)\n"); exit(1); } if(dumpFileName == NULL ) { dump_file = stderr; } else { dump_file = fopen(dumpFileName, "w+"); } if(gethostname(localHostName, lhostlen) != 0 ) { perror("Cannot get local host name: try using localhost\n"); strncpy(localHostName, "localhost",9); } if(verbose) { fprintf(stderr, "Filename: --%s--\n",dumpFileName); fprintf(stderr, "Local port: --%i--\n",local_port); fprintf(stderr, "Server port: --%i--\n",server_port); fprintf(stderr, "Remote server: --%s--\n",remoteServerName); fprintf(stderr, "verbose: --%i--\n",verbose); fprintf(stderr, "Local Hostname:--%s--\n",localHostName); } /* * A) Create and bind a socket that will connect to the remote client (essentially, a local server socket). We use accept() to create a file descriptor that references this remote resource--and redo the accept every time we start a new client-server transaction. */ if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket 1"); exit(1); } if((setsockopt(lfd, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one))) == -1) { perror("setsockopt(SO_REUSEADDR)"); fprintf(stderr,"httpd: could not set socket option SO_REUSEADDR\n"); exit(1); } my_addr.sin_family = AF_INET; my_addr.sin_port = local_port; my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(lfd, &my_addr, sizeof(my_addr)) < 0) { perror("Proxy server unable to create socket for client connection.\nMost likely the port is not free -- \n"); exit(1); } my_inane = sizeof(my_addr); if (getsockname(lfd, &my_addr, &my_inane) < 0) { perror("Proxy server unable to getsockname() for client connection\n"); exit(1); } /* make this socket listen for incoming data */ if (listen(lfd, 5) < 0) { perror("Proxy server unable to listen on designated socket\n"); exit(1); } /* * B). Ok, now create a socket structure for communicating with the * remote server. This is, essentially, a local client socket. * This will need to open and close for each transaction. */ if( (remote_server_ent = gethostbyname(remoteServerName)) == NULL ) { perror("gethostbyname 2"); exit(1); } bcopy( (char *) remote_server_ent->h_addr, (char *) &remote_server_addr.sin_addr, remote_server_ent->h_length ); remote_server_addr.sin_family = remote_server_ent->h_addrtype; remote_server_addr.sin_port = server_port; fprintf(stderr, "Proxy server at host %s on port %d\n", localHostName, ntohs(my_addr.sin_port)); fprintf(stderr,"Proxy connection to host %s on port %d\n", remoteServerName,ntohs(remote_server_addr.sin_port)); /* Now loop infinitelly, opening to accept messages from browser, and then creating an outgoing connection to the remote server. */ while ( my_inane = sizeof(client_addr), (sfd = accept(lfd, &client_addr, &my_inane)) >= 0 ) { /* Preceding line sets up the program to accept data from the remote browser. We now need to create a new socket that will connect to the remote server. */ if ((remote_server_fd = socket(remote_server_ent->h_addrtype, SOCK_STREAM, 0)) < 0) { perror("Proxy server unable to create socket for server connection\n"); exit(1); } if ((connect( remote_server_fd, &remote_server_addr, sizeof(remote_server_addr))) < 0 ) { perror("Proxy server unable to connect to remote server\n "); exit(1); } /* Server socket is created. Now fork to create parent and child processes. Parent process will read data from the remote server, and write it to the remote client. The child will read data from teh remote client (browser), and forward it to the remote server. */ if ((pid = fork()) < 0) { perror("fork"); continue; } if (pid == 0) { /* * IN CHILD -- read data sent by browser; forward it to the server */ if(verbose) {fprintf(stderr, "Forked and in CHILD\n"); } if ((sfp = fdopen(sfd, "r")) == NULL) { fprintf(stderr, "CHILD can't create file pointer for reading from browser\n"); exit(1); } if ((remote_server_fp = fdopen(remote_server_fd, "w")) == NULL) { fprintf(stderr, "CHILD can't create file pointer for writing to server.\n"); exit(1); } if(verbose) {fprintf(stderr, "CHILD: start data from client\n");} fputs("--- BEGIN DATA FROM CLIENT ---- \r\n",dump_file); copydata(sfp, sfd, pid, remote_server_fp, verbose, dump_file); fputs("--- END DATA FROM CLIENT ---- \r\n",dump_file); if(verbose) {fprintf(stderr, "CHILD: end data from client.EXIT\n");} exit(0); } else { /* * IN PARENT -- read data sent by server; return it to the browser. */ if (verbose) { fprintf(stderr, "FORKED, in PARENT: accept %s:%d, pid %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), pid); } if ((remote_server_fp = fdopen(remote_server_fd, "r")) == NULL ) { fprintf(stderr, "PARENT can't create file pointer for reading from server.\n"); continue; /* try again */ } if ((sfp = fdopen(sfd, "w")) == NULL ) { fprintf(stderr, "PARENT can't create file pointer for writng to browser\n"); continue; /* try again */ } if(verbose) {fprintf(stderr, "PARENT: copy server to browser\n");} fputs("--- BEGIN DATA FROM SERVER ---- \r\n",dump_file); copydata(remote_server_fp, remote_server_fd, pid, sfp, verbose, dump_file); fputs("--- END DATA FROM SERVER ---- \r\n",dump_file); fclose(sfp); fclose(remote_server_fp); kill(pid, SIGTERM); /* kill child, to close browser connection*/ clearerr(stderr); /* clear EOF flag -- not needed */ } close(sfd); /* close connection to browser */ close(remote_server_fd); } exit(0); } /* copydata() function copies data from input file descriptor to output file descriptor, with a copy sent to the dump_file. */ copydata(from_fp, from_fd, pid, to_fp, verbose, dump_file) FILE *from_fp, *to_fp, *dump_file; int from_fd; int verbose, pid; #define BUFSIZE 1024 /* size for read/write buffer */ { char buf[BUFSIZE]; /* read/write data buffer */ int i, isHeader; /* isHeader flag for HTTP header */ static char *pattern = "content-length:"; /* string for content-length header */ long chunks, nbytes, remainder, len; /* For calcualting length of message body */ isHeader = TRUE; nbytes = -1; if (verbose) { fprintf(stderr, "PID %d -- Begin copydata().\n", pid); } while(isHeader) { if( len = fgets(buf, sizeof(buf), from_fp) > 0 ) { fputs(buf, to_fp); fputs(buf, dump_file); if (verbose) { fprintf(stderr, "PID %d -- buffer:%s\n",pid,buf); } if(strncasecmp(buf, pattern, 15) == 0 ) { nbytes = atol(&buf[15]); } else if( strcmp(buf, "\r\n") == 0 || strcmp(buf, "\r") == 0 || strcmp(buf, "\n") == 0 ) { isHeader = FALSE; fflush(dump_file); fflush(to_fp); break; } } else { if (verbose) { fprintf(stderr, "PID %d -- error: len=fgtets()=%i\n",len); } } } if (verbose) { fprintf(stderr, "PID %d -- finished with header \n"); } if( nbytes > 0 ) { /* calculate how many full buffers we can read, and then calculate the size of the remainder we'll need to copy after all full buffers ahve been transferred. */ chunks = nbytes / BUFSIZE; remainder = nbytes % BUFSIZE; if(verbose) { fprintf(stderr," *** Bytes=%i, BUFSIZE=%i, Chunks=%i, remainder=%i\n", nbytes, (long) BUFSIZE, chunks, remainder); } for(i=0; i 0 ) { fwrite(&buf,sizeof(char), BUFSIZE, dump_file); if(verbose) { fprintf(stderr, "**** PID %d: buffer: %s",pid,buf); } fflush(dump_file); if (fwrite(&buf, sizeof(char), BUFSIZE, to_fp) == 0 || fflush(to_fp) == EOF) { perror("fwrite1 failed... continue "); } } else { break; } } /* Done buffer chunks; now copy over the remainder */ if( fread(&buf, sizeof(char), remainder, from_fp) > 0 ) { fwrite(&buf,sizeof(char), remainder, dump_file); if(verbose) { fprintf(stderr, "*** PID %d: REMAINDER buffer: %s", pid, buf); } fflush(dump_file); if (fwrite(&buf, sizeof(char), remainder, to_fp) == 0 || fflush(to_fp) == EOF) { perror("fwrite2 -- break"); } } if(verbose) { fprintf(stderr, "PID %d: finished data transfer;\n"); } fflush(to_fp); } else { /* don't know message length because there was no content-length header, so go into an infinite loop copying single bytes. This is slow and ineffecient, but that doesn't matter for this demo. */ while ( fread(&buf, sizeof(char), 1, from_fp) > 0 ) { fwrite(&buf,sizeof(char), 1, dump_file); fflush(dump_file); if (fwrite(&buf, sizeof(char), 1, to_fp) == 0 || fflush(to_fp) == EOF) { perror("fwrite"); break; } } } }