[comp.sources.misc] v08i064: bug -- make a transcript of a socket connection

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (10/01/89)

Posting-number: Volume 8, Issue 64
Submitted-by: matthew@CS.UCLA.EDU (Matthew Merzbacher)
Archive-name: bug

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	bug.1
#	bug.c
# This archive created: Fri Sep 29 18:29:52 1989
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
Bug is used to tap a stream socket.  It is useful for tracing communication 
between a server and its clients.  The communication between server and
client is maintained while transcripts of all communication lines are taken.

There is a man page (bug.1), a source file (bug.c) and this file (README)
included in this archive.  No Makefile is necessary, since to "make" it, all
you do is:
    cc -o bug bug.c

You need to be running a BSD or BSD-derived system (since it examines sockets).
It has been tested under SunOS 4.0.3

If you don't like my MARK, you can change it by changing the lines
#define MARK
and
#define MARKLEN
to your own values.

I'd be interested in hearing what people have to say and what they use 'bug'
for.  I hope I haven't re-invented the wheel.  Bugs/Comments/Questions/Hate 
Mail to me,

Matthew Merzbacher	ARPA:	matthew@CS.UCLA.EDU
	  		UUCP:	...!{uunet|rutgers|ucbvax}!cs.ucla.edu!matthew
SHAR_EOF
fi # end of overwriting check
if test -f 'bug.1'
then
	echo shar: will not over-write existing file "'bug.1'"
else
cat << \SHAR_EOF > 'bug.1'
.TH BUG 1
\" Copyright (C) 1989 by Matthew Merzbacher.  All Rights Reserved.
.SH NAME
bug \- listen and transcribe communication on a socket
.SH SYNOPSIS
.B bug
.nh
input_port output_port machine client_file server_file
.hy
.SH DESCRIPTION
.I bug
is used to listen on a specified \fIinput_port\fR and write an exact
duplicate out to the \fIoutput_port\fR on a specified \fImachine\fR.  It 
also writes a transcript of everything that is said on either port
to seperate files.  Each piece of communication is seperated by a MARK, which
is #defined in the code as "<MARK>".
.PP
The naming scheme for the output files is \fIclient_file\fR (for communication
received from the \fIinput_port\fR) or \fIserver_file\fR (for communication 
received from the \fIoutput_port\fR) followed by a period ('.') followed by 
the process id of the bug program followed by a period ('.') followed by the 
process id of the child of bug which actually does the monitoring.  Thus, for 
every connection made to the input_port, there will be two (unique) files 
created.
.PP
The output files are probably best read by using \fIod\fR(1) with the "-c" 
or "-cx" options.
.SH EXAMPLE
\fIbug\fR 3000 2999 moosebreath cl se
.PP
This invocation will create, for every connection to port 3000 on the current 
machine, two files (cl.pid.cpid and se.pid.cpid).  Every communication to port
3000 is repeated verbatim to port 2999 on machine "moosebreath" and written to
the first file.  All responses are written to the second file.
.SH FILES
.br
creates files based on \fIclient_file\fR and \fIserver_file\fR
.SH "SEE ALSO"
od(1), socket(2) [and friends], fork(2).
.SH AUTHOR
Matthew Merzbacher, bugs/comments - matthew@cs.ucla.edu
.SH BUGS
.PP
Goodness knows what systems this will actually run on.  Any BSD or 
BSD-derived system SHOULD work.

There should be an option to change the MARK.
SHAR_EOF
fi # end of overwriting check
if test -f 'bug.c'
then
	echo shar: will not over-write existing file "'bug.c'"
else
cat << \SHAR_EOF > 'bug.c'
/* Copyright (C) 1989 by Matthew Merzbacher.
   All Rights Reserved.
   Bug is provided as is, without express or implied warranty.  In no event
   shall Matthew Merzbacher become liable for any loss or damages, consequential
   or otherwise, arising from the use or possession of this software.
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <fcntl.h>

#define MARK "<MARK>"
#define MARKLEN 6

/*
 * Brief Description:
 * Bug is used to tap a stream socket.  It is useful for tracing communication 
 * between a server and its clients.  The communication between server and
 * client is maintained while transcripts of all communication lines are taken.
 * It has six required (and no optional) arguments.  
 *
 * Usage:
 * bug <in port> <out port> <machine> <client file> <server file>
 *
 * <in port>     - the port (on the machine on which bug runs) to which to
 *				   clients will connect (the pseudo-port)
 * <out port>    - the true server port
 * <machine>     - the server machine
 * <client file> - transcript file of what each client says
 * <server file> - transcript file of what the server responds
 *
 * Example: 
 * Suppose a server listens on port 3000 of machine "xyz".  The user must 
 * artificially make the clients talk to some other port (on any machine -
 * it could be the server machine, the client machine, or a third machine).
 * That port is called the pseudo-port.  Bug runs on the machine of the
 * pseudo-port.  To run it, type:
 * 
 * bug 2999 3000 xyz client server
 *
 * This assumes that the pseudo-port is 2999.  The output files will all start
 * with "client" or "server" followed by the process number of the bug process
 * and the process number of each seperate listening session.  That way, you
 * can follow the connection order.  Typically, the output files created by bug
 * may be read by using 'od -c'.
 *
 */

main(argc, argv)
int argc; char *argv[];
{
    struct sockaddr_in server;
    struct hostent *hp, *gethostbyname();
    char buf[1024];
    int rval;
    int outfile1, outfile2;
    int insock, outsock, msgsock;
    int insocknum, outsocknum;
    fd_set fdset;


    /* Check Arguments */

    if (argc < 6) {
	printf("usage: %s <in port> <out port> <machine> <client file> <server file>\n",argv[0]);
	exit(1);
	}

    insocknum = atoi(argv[1]);
    outsocknum = atoi(argv[2]);


    /* Create listening post on pseudo-port */

    insock = socket(AF_INET, SOCK_STREAM, 0);

    if (insock < 0) {
	perror("opening pseudo-port stream socket");
	exit(1);
	}


    /* Name socket using user supplied port number */

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = insocknum;

    if (bind(insock, &server, sizeof(server))) {
	perror("binding pseudo-port stream socket");
	exit(1);
	}


    /* Start accepting connections */

    listen(insock, SOMAXCONN);


    /* Every connection spawns a child to handle the communication */

    do {
	msgsock = accept(insock, 0, 0);
	if (msgsock == -1)
		perror("accept");
	} while (fork() != 0);


    /* Close listening post - it's now called "msgsock" */

    close(insock);


    /* Create output socket to server */

    outsock = socket(AF_INET, SOCK_STREAM, 0);
    if (outsock < 0) {
	perror("opening server stream socket");
	exit(1);
	}


    /* Connect socket using machine & port specified on command line. */

    server.sin_family = AF_INET;
    hp = gethostbyname(argv[3]);
    if (hp == 0) {
	fprintf(stderr, "%s: unknown machine\n", argv[3]);
	exit(2);
	}

    bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
    server.sin_port = htons(outsocknum);

    if (connect(outsock, &server, sizeof(server)) < 0) {
	perror("connecting server stream socket");
	exit(1);
	}


    /* Open the output files */

    sprintf(buf,"%s.%d.%d",argv[4],getppid(),getpid());
    if ((outfile1 = open(buf, O_TRUNC|O_WRONLY|O_CREAT, 0644)) == -1) {
	fprintf(stderr,"cannot open file: %s\n",buf);
	exit(1);
	}

    sprintf(buf,"%s.%d.%d",argv[5],getppid(),getpid());
    if ((outfile2 = open(buf, O_TRUNC|O_WRONLY|O_CREAT, 0644)) == -1) {
	fprintf(stderr,"cannot open file: %s\n",buf);
	exit(1);
	}

    do {
	
	/* find out who's talking */

	FD_ZERO(&fdset);
	FD_SET(msgsock, &fdset);
	FD_SET(outsock, &fdset);
	if (select(getdtablesize(), &fdset, 0, 0, 0) == -1) {
	    perror("select");
	    exit(1);
	    }
	bzero(buf, 1024);

	if (FD_ISSET(msgsock, &fdset) && FD_ISSET(outsock, &fdset))
	    fprintf(stderr,"Two talkers - no listeners\n");


	/* Client is talking to server */

	if (FD_ISSET(msgsock, &fdset)) { 
	    if ((rval = read(msgsock, buf, 1024)) < 0)
		perror("reading stream message");
	    if (rval == 0)
		fprintf(stderr,"Ending client connection\n");
	    else {
		write(outfile1, buf, rval);
		write(outfile1, MARK, 6);
		if (write(outsock, buf, rval) < 0)
		    perror("writing on output stream socket");
		}
	    }

	/* Server is talking to client */

	else {
	    if (! FD_ISSET(outsock, &fdset)) {
		perror("weird behavior");
		exit(1);
		}
	    if ((rval = read(outsock, buf, 1024)) < 0)
		perror("reading stream message");
	    if (rval == 0)
		fprintf(stderr,"Ending server connection\n");
	    else {
		write(outfile2, buf, rval);
		write(outfile2, MARK, 6);
		if (write(msgsock, buf, rval) < 0)
		    perror("writing on output stream socket");
		}
	    }
	} while (rval != 0);


    /* Close up shop */

    fprintf(stderr, "Closing Connections\n");
    close(msgsock);
    close(outsock);
    close(outfile1);
    close(outfile2);
    }
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0