[alt.sources] AutoBounce the automatic mail bouncer

shipley@e260-4g.berkeley.edu (Pete Shipley) (02/25/89)

Here it is, a automatic mail bouncer, the local postmater has asked
me to stop using this. So I am posting it to the world....



Pete Shipley: 
email: shipley@berkeley.edu		Flames:  cimarron@postgres.berkeley.edu 
       uunet!lurnix!shipley or ucbvax!shipley or pyramid!hippo!{ root peter }
Spelling corections: /dev/null                    Quote: "Anger is an energy"



		    -=-=-=-=- cut here -=-=-=-=-
#! /bin/sh
mkdir Autobounce
cd Autobounce
echo x - Makefile
cat >Makefile <<'!E!O!F!'
DEST	      = .

EXTHDRS	      = /usr/include/ctype.h \
		/usr/include/machine/param.h \
		/usr/include/machine/param.h \
		/usr/include/netdb.h \
		/usr/include/netinet/in.h \
		/usr/include/pwd.h \
		/usr/include/signal.h \
		/usr/include/stdio.h \
		/usr/include/sys/fcntl.h \
		/usr/include/sys/file.h \
		/usr/include/sys/ioctl.h \
		/usr/include/sys/param.h \
		/usr/include/sys/socket.h \
		/usr/include/sys/sysmacros.h \
		/usr/include/sys/time.h \
		/usr/include/sys/ttychars.h \
		/usr/include/sys/ttydev.h \
		/usr/include/sys/types.h \
		/usr/include/time.h

HDRS	      =

#	STRNCASECMP define if strcasecmp() does not exist (eg: non 4.3 systems)
#
CFLAGS        = -g -pipe -I. -DSTRNCASECMP #-DDEBUG

LDFLAGS	      = $(CFLAGS)

LIBS	      =

LINKER	      = $(CC)

MAKEFILE      = Makefile

OBJS	      = mbounce.o

PRINT	      = psgrind

PROGRAM	      = autobounce

SRCS	      = mbounce.c

all:		$(PROGRAM) manpage

$(PROGRAM):     $(OBJS)
		$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)

clean:;		@rm -f $(OBJS) autobounce.man tags

depend:;	@mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)

index:;		@ctags -wx $(HDRS) $(SRCS)

manpage:	autobounce.1
		tbl autobounce.1 | nroff -man > autobounce.man

install:	$(PROGRAM)
		@echo Installing $(PROGRAM) in $(DEST)
		@install -s $(PROGRAM) $(DEST)

print:;		@$(PRINT) $(HDRS) $(SRCS)

program:        $(PROGRAM)

lint:;		lint $(SRCS)

tags:           $(HDRS) $(SRCS) 
		ctags -tw  $(HDRS) $(SRCS)

update:		$(DEST)/$(PROGRAM)

$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
		@make -f $(MAKEFILE) DEST=$(DEST) install
###
!E!O!F!
echo x - autobounce.1
cat >autobounce.1 <<'!E!O!F!'
.TH MAN 1 "24 Feb 1989"
.SH NAME
autobounce \- bounced mail impersonator
.SH SYNOPSIS
.B man
.RB "[\|" \-b "\|]"
.RB "[\|" \-d "\|]"
.RB "[\|" \-o "\|]"
.RB "[\|" \-f \0\fIfile\fP "\|]"
.RB "[\|" \-h \0\fImailerhost\fP "\|]"
.SH DESCRIPTION
.I autobounce
can be used to forge bounced mail messages.
It is normally used by placing in one's \*Q.forward\*U file
so it can be used to reject unwanted mail.
It can also be use to form the command line to previously received
bounce mail.
.SH OPTIONS
.PP
.TP
.B \-b
Force the sending of a forged bounce. (even is user does not appear
in the \*Q.shitlist\*U config file).
.TP
.B -d
turn on debugging infomation.
.TP
.B \-o
Send output to stdout instead of the smtp port of the mailerhost.
.TP
.B \-f
Take input from specified file instead of stdin.
.TP
.B \-h
specify the host to deliver the bounced mail to.
.SH USAGE
Autobounce is normally run from within your .forward file.
To run create a
.I \&.forward
file in your home directory containing a line of the form:
.IP
\e\fIname\fP, "|/the/full/path/to/autobounce"
.LP
where
.I name
is your login name.
.SH SHITLIST FILE FORMAT
The default file \*Q$HOST/.shitlist\*U has the following format:
.sp 1
.ti 0.5i
bounce user \fI%_chance_of_bounce\fP \fInumber_of_bounces\fP
.LP
Where: user if the user name you wish to bounce.
%_chance_of_bounce is a optional percent chance of bouncing the letter
(default is 100%).
number_of_bounces if the number of copies to send (default is one).
.LP
Other options that can be set in the .shitlist file
are the mailerhost and debug.
Mailerhost sets the system to connect and send the forged mail to.
Debug activates the debugging option.
.LP
Here is an example default file:
.in +0.5i
.na
.nf
bounce	nory	100	40
bounce	muir	10	100
bounce	andy	50
bounce	leo
mailerhost	jade.berkeley.edu
.fi
.ad
.in -0.5i
.LP
.SH FILES
.TP
.I $HOST/.shitlist
default config file.
.SH "SEE ALSO"
vacation(1), RFC788, RFC821.
.SH BUGS
Lots.  The reason list for the bounce looks fishy.
This badly written manpage.
!E!O!F!
echo x - mbounce.c
cat >mbounce.c <<'!E!O!F!'
#include <sys/param.h>
#include <sys/file.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <stdio.h>
#include <pwd.h>
#include <ctype.h>

/* mail data string to be feed to mailer */
static char header_string[] = "\
helo: %s\n\
mail from: %s<%s>\n\
rcpt to: %s<%s>\n\
data\n\
Subject: Returned mail: User unknown\n\
\n\
   ----- Transcript of session follows -----\n\
>>> RCPT To:<%s>\n\
<<< 550 <%s>... User unknown\n\
550 %s... User unknown\n\n\
   ----- Unsent message follows -----\n";

/* max taken from mail header configs */
#ifndef MAXLINE
#define MAXLINE 500
#endif

/* other random definitions */
#ifndef FALSE
#define FALSE 0
#endif	FALSE

#ifndef TRUE
#define TRUE 1
#endif	TRUE

/* this make coding more fun */
#ifndef EVER
#define	EVER	;;
#endif	EVER

/* thsi is a fun one */
#ifdef	DEBUG
#define	exit(X)	abort(X);
#endif	DEBUG

#ifndef	ToLower
#define ToLower(X)	(isupper(X) != 0 ? tolower(X) : X) 
#endif	ToLower

#ifndef StrMake
#define	StrMake(X)	strcpy( malloc((unsigned) strlen(X)+1), X)
#endif	StrMake

/* linked list struct def */
typedef struct _Shitlist {
	char    name[MAXLINE];
	int	chance;
	int	count;
	struct	_Shitlist *next;
} Shitlist;

/* the struct is to hold the information on the current victim */
typedef struct user {
	char	to[MAXLINE];
	char	from[MAXLINE];
	char	cc[MAXLINE];
	char	id[MAXLINE];
	char	subject[MAXLINE];
	char	name[16];
	u_int	count;
} mailer;

/* Guess */
typedef char Boolean;

Shitlist *start_shit;
Boolean	debug;
Boolean	bounceall;
char *mailerhost = "localhost.berkeley.edu";
char *homedir;

#ifdef	STRNCASECMP
Boolean	strncasecmp();
#else
int	strncasecmp();
#endif	STRNCASECMP


static mailer *readheaders();

static Boolean checkbounce(),
	    nsearch();

static void sendmessage(),
	    logbounce(),
	    ReadDefaults(),
	    copycont();

static Shitlist *CreateLink();

static short    servtoport();

static long	hosttoaddr();

int	getuid();
char    *strcpy(),
	*malloc(),
	*tempnam(),
	*index(),
	*sprintf(),
	*getenv(),
	*strcat(),
	*rindex();

/* here is where is all begins */
/* ARGUSED */
main(ac, av)
	int ac;
	char **av;
{
register int	ch;
char	*tempfile;
mailer	*header;
FILE	*tempfd, *infile;
Boolean use_stdout = FALSE;
extern	int optind;
extern	char *optarg;
extern	int errno;
extern	Boolean debug;
extern	char *homedir;
extern	Boolean	bounceall;

    /* init a few vars */
    errno = 0;
    infile = (FILE *) NULL;
    debug = FALSE;
    bounceall = FALSE;

    /* load in the default file */
    ReadDefaults();

#ifdef	DEBUG
    /* So core files can be found */
    (void) chdir(homedir);
#endif	DEBUG

    /* Parse the arguments */
    while((ch = getopt(ac, av, "dh:?obf:")) != EOF)
	switch( (char) ch) {
	    case 'h':	/* define new mailer host */
		mailerhost = optarg;
		break;
	    case 'd':	/* activate debuging */
		debug = TRUE;
		break;
	    case 'b':	/* force the bounce */
		bounceall = TRUE;
		break;
	    case 'f':	/* take input from following file */
		infile = fopen(optarg, "r");
		if(infile == (FILE *) NULL) {
		    perror(optarg);
		    exit(1);
		}
		break;
	    case 'o':	/* send output to stdout */
		use_stdout = TRUE; 
		break;
	    case '?':
	    default:
		(void) fprintf(stderr,
		    "Usage: %s [-o] [-f file] [-h hostname] [-b] [-d]\n",
		    av[0]);
		(void) fputs(
		    "-o\tUse stdout instead of connecting to malerhost\n",
		    stderr);
		(void) fputs("-f file\tuse file for input [stdin default]\n",
		stderr);
		(void) fputs(
		"-h hostname\thost to connect and send mail to\n", stderr);
		(void) fputs(
	"-b\tforce bounce even if user does not appear in configurtion file\n",
		stderr);
	    exit(1);
	}

    /* get a unique temp file name */
    tempfile = tempnam((char *) NULL, "mboun");
    if(debug)
	(void) fprintf(stderr, "tempfile = '%s'\n", tempfile);

    /* open a temporary file */
    tempfd = fopen(tempfile, "w+");
    if(tempfd == (FILE *) NULL) {
	perror(tempfile);
	exit(1);
    }


    /* free unneeded memory */
    (void) free(tempfile);

    /* unlink the tempory file to make it really temporary */
    if(unlink(tempfile) == -1)
	perror("unlink");

    /* copy the incoming letter into the temp file */
    if(infile) {
	copycont(infile, tempfd);
	(void) fclose(infile);
    } else
	copycont(stdin, tempfd);

    /* parse the mail header */
    header = readheaders(tempfd);

    /* if bounce is not being forced, test for it */
    if(!bounceall) 
	bounceall = checkbounce(header);

    /* here we send it off */
    if(bounceall)
	for( ch = header->count; ch > 0; ch--)
	    /* Transmit the message */
	    sendmessage(header, tempfd, use_stdout);

    /* bye bye */
    exit(0);
}

/* this function checks to see it show bounce the letter */
static Boolean
checkbounce(hdata)
mailer *hdata;
{
Shitlist	*current;
Boolean	send;


    /* set current to start of linked list */
    current = start_shit;

    /* don't bounce authors mail */
    if (nsearch("shipley", hdata->from)) {
	return(FALSE);
    }

    /* loop untill end of list or victem is found  */
    for (EVER) {
	Shitlist	*prev;

	/* check for match */
	if (nsearch(current->name, hdata->from)) {
	    send = TRUE;
	    hdata->count = current->count;
	    break;
	}

	/* look for end of list */
	if(current->next == NULL)
	    break;

	/* free unused date */
	prev = current;
	current = current->next;
	if(!debug)
	    (void) free((char *) prev);
    }


    /* check if random bounce */
    if(send && current->chance) {
	(void) srand(getpid());
	if( current->chance < (rand()%100) )
	    send = FALSE;
    }

    /* log the bouncing */
    if(send)
	logbounce(hdata);
	
    /* and go home */
    return(send);
}

/* the function sends the mail */
static void
sendmessage(hdata, datafd, use_stdout)
mailer	*hdata;
FILE	*datafd;
Boolean use_stdout;
{
char	hostn[MAXLINE];
char	from_addr[MAXLINE];
struct	servent  *sp;
FILE	*fout;

    /* get our hostname */
    (void) gethostname(hostn, MAXLINE);

    /* if we are not using stdout set up a connection */
    if(!use_stdout) {
	int sock;
	struct sockaddr_in server;
	struct hostent *hp, *gethostbyname();

	/* Create socket */
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		perror("opening stream socket");
		exit(1);
	}

	/* Connect socket using name specified by command line. */
	server.sin_family = AF_INET;
	hp = gethostbyname(mailerhost);
	if (hp == 0) {
		(void) fprintf(stderr, "%s: unknown host\n", mailerhost);
		exit(2);
	}

	/* get ugly */
	bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length);
	sp = getservbyname("smtp", "tcp");
	server.sin_port = sp->s_port;
	if(server.sin_port == htons(0)) {
	    (void) fputs("Unknown service: smtp\n", stderr);
	    exit(1);
	}

	/* test connection */
	if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
		perror("connecting stream socket");
		exit(1);
	}

	/* turn file des. into FILE* */
	fout = fdopen(sock, "w");
    } else
	fout = stdout;

    /* assemble maler header */
    (void) sprintf(from_addr, "%s@%s", "MAILER-DAEMON", hostn);

    /* check if we have to me verbose about life */
    if(debug) {
	(void) fprintf(stderr,
	    "From = '%s'\nTo = '%s'\nCc = '%s'\nsubject = '%s'\nId = '%s'\n",
	    hdata->from, hdata->to, hdata->cc, hdata->subject, hdata->id);

	(void) fprintf(stderr, "hostname = '%s'\n", hostn);
	(void) fflush(stderr);
    }

    /* start Xmiting */
    (void) fprintf(fout, header_string,
	hostn,
	from_addr, from_addr,
	hdata->from, hdata->from,
	hdata->to,
	hdata->to,
	hdata->to);

    /* start at the begining of the letter */
    rewind(datafd);

    /* copy the leter to output */
    copycont(datafd, fout);

    /* say good buy to the mailer [or stdout] */
    (void) fputs("\n\n.\nquit\n", fout);

    /* close the socket */
    if (!use_stdout)
	(void) fclose(fout);

    /* bye bye*/
    return;
}



/* the function parces the mailer header */
static mailer *
readheaders(in)
FILE *in;
{
	register char *p;
	int n;
	char buf[MAXLINE];
	mailer *mdata;
	extern int errno;

	/* allocate and init return data */
	mdata = (mailer *)malloc(sizeof(mailer));
	bzero((char *)mdata, sizeof(mailer));

	/* init buffer since local Vars are not automaticly nulled */
	bzero(buf, MAXLINE);

	/* rewind the temp file so we can read the full contance */
	(void) rewind(in);
	if(errno) {
	    if(debug)
		perror("rewind");
	    exit(1);
	}

	while (fgets(buf, sizeof(buf), in) && *buf != '\n') {
		switch(*buf) {
		case 'F':		/* "From " */
			if (!strncmp(buf, "From:", 5)) {
			    (void)strcpy(mdata->from, &buf[6]);

			    if( (n = strlen(mdata->from)) != 0)
				mdata->from[n-1] = NULL;
			}
			break;
		case 'C':		/* "Cc:" */
			if (!strncmp(buf, "Cc:", 3))
			    (void)strcpy(mdata->cc, &buf[4]);

			if( (n = strlen(mdata->cc)) != 0)
			    mdata->cc[n-1] = NULL;

			break;
		case 'S':		/* "Subject:" */
			if (!strncmp(buf, "Subject:", 8))
			    (void)strcpy(mdata->subject, &buf[9]);

			if( (n = strlen(mdata->subject)) != 0)
			    mdata->subject[n-1] = NULL;

			break;
		case 'T':		/* "To:" */
			if (!strncmp(buf, "To:", 3)) {
			    (void)strcpy(mdata->to, &buf[4]);

			    if( (p = index(mdata->to, ',')) != NULL)
				 *p = NULL;
			    else
				if( (n = strlen(mdata->to)) != 0)
				    mdata->to[n-1] = NULL;
			}
			break;
		case 'M':		/* "Message-Id:" */
			if (!strncmp(buf, "Message-Id:", 10))
			    (void)strcpy(mdata->id, &buf[11]);

			if( (n = strlen(mdata->id)) != 0)
			    mdata->id[n-1] = NULL;

			break;
		}
	}

	/* set bounce count to 1 [may be reset in checkbounce] */ 
	mdata->count = 1;

	/* done here so return */
	return mdata;
}

/* search a string for a substring.  */
static Boolean
nsearch(name, string)
register char *name;
register char *string;
{
	register int i;

	for (i = strlen(name); *string; ++string)
	    if (*string == *name && !strncasecmp(name, string, i))
		return(TRUE);

	return(FALSE);
}

/* copy one FILE* into another FILE* */
static void
copycont(from, to)
FILE *from, *to;
{
register n;
char	buf[BUFSIZ];
extern	int	errno;
extern	Boolean	debug;

    /* reset error flag so we can test it later */
    errno = 0;

    /* loop untill out of data */
    while(n = fread(buf, sizeof(char), BUFSIZ, from))
	if(fwrite(buf,  sizeof(char), n, to) != n) {
	    if(debug)
		(void) perror("copycont");
	}
    
    /*
     *if there is an error and debuging info is requested
     * call perror  & continue
     */
    if(errno != 0 && debug)
	(void) perror("Copycont");

    return;
}

/* log bounce if log file exists */
static void
logbounce(hdata)
mailer	*hdata;
{
char	tbuf[MAXPATHLEN];
FILE	*logfd;
extern	char	*homedir;
extern	Boolean	debug;

    /* build path to log file */
    (void) strcpy(tbuf, homedir);
    (void) strcat(tbuf, "/.bouncelog");

    /* open logfile */
    logfd = fopen(tbuf, "a+");

    /* test if we opened log file */
    if(logfd == (FILE *) NULL) {

	/* test if we report failer */
	if(debug) perror(tbuf);

	/* bye again */
	return;
    }

    /* write log entery */
    (void) fprintf(logfd,
   "From = '%s'\nTo = '%s'\nCc = '%s'\nsubject = '%s'\nId = '%s'\ncount =%d\n",
	hdata->from, hdata->to, hdata->cc, hdata->subject, hdata->id,
	hdata->count);

    /* close log file */
    (void) fclose(logfd);

    return;
}

/* create a link section */
static Shitlist *
CreateLink()
{

    register Shitlist *newlink;

    /* allocate mem for the new link */
    newlink = (Shitlist *) malloc( sizeof(Shitlist) );

    /* clean out the newly alloced memory */
    bzero((char *) newlink, sizeof(Shitlist) );

    /* return our new creation */
    return(newlink);
}

/* read in our defaults file*/
static void
ReadDefaults()
{
char    buf[BUFSIZ];
FILE	*bfd;
int	uid = getuid();
struct passwd *pw;
Shitlist *current;

extern char *homedir;
extern Boolean debug;
extern Boolean bounceall;

    /* get teh passwd entery of user [needed for home directory] */
    pw = getpwuid(uid);

    /* test to see it we get what we want */
    if(pw == (struct passwd *) NULL) {
	(void) fprintf(stderr, "Can't get home directory for %d\n", uid);
	exit(1);
    } else
	(void) strcpy(buf, pw->pw_dir);

    /* copy the path to home directory into a safe place */
    homedir = StrMake(pw->pw_dir);

    /* build path to defuat file */
    (void) strcat(buf, "/.shitlist");

    /* be loud if necceary */
    if(debug)
	(void) fprintf(stderr, "<bounce> buf = '%s'\n", buf);

    /* open the default file */
    bfd = fopen(buf, "r");

    /* check if we cant open default file */ 
    if(bfd == (FILE *) NULL) {
	if(debug)
	    perror(buf);
	return;
    }


    /* start the list */
    current = start_shit = CreateLink();

    /* assemble the list */
    while ( fgets(buf, BUFSIZ, bfd) != NULL)
	switch(*buf) {
	    case 'd':		/* "mailerhost" */
	    case 'D':		/* "Mailerhost" */
		    if (!strncasecmp(buf, "debug", 5))
			debug = TRUE;
		    break;
	    case 'm':		/* "mailerhost" */
	    case 'M':		/* "Mailerhost" */
		    if (!strncasecmp(buf, "mailerhost", 10)) {
			char *tp;

			/* skip the while space */
			if( (tp = index(buf, ' ')) == NULL)
			    break;
			else
			    tp++;

			if(*tp != NULL) {
			    mailerhost = StrMake(tp);
			    mailerhost[strlen(mailerhost) -1] = '\0';
			}

		    }
		    if(debug)
			(void) fprintf(stderr, "mailerhost = '%s'\n",
				    mailerhost);
		    break;

	    case 'b':		/* "bounce" */
	    case 'B':		/* "Bounce" */
		    if(bounceall)
			break;

		    if (!strncasecmp(buf, "bounce", 6)) {
			char *tp;

			if( (tp = index(buf, ' ')) == NULL)
			    break;
			else
			    tp++;

			if(*tp == NULL)
			    break;

			if (nsearch("!any", tp)) {
			    bounceall = TRUE;
			    break;
			}

			/* Check if the is the first link */
			if(current->name[0] != '\0') {
			    current->next = CreateLink();
			    current = current->next;
			}

			(void) sscanf(tp, "%s %d %d",
			    current->name, &current->chance, &current->count);
			
			if(current->count == 0) { 
			    current->count = 1; 
			}

			if(debug)
			    (void) fprintf(stderr,
				"buf = '%s'\tchance = %d\tcount = %d\n",
			    current->name, current->chance, current->count);

		    }
	}

    /* be neet and clean */
    (void) fclose(bfd);

    /* go home */
    return;
}


#ifdef STRNCASECMP
Boolean 
strncasecmp(s1, s2, n)
char *s1, *s2;
register int n;
{
register u_char	*r1 = (u_char *)s1;
register u_char	*r2 = (u_char *)s2;

    while (--n >= 0 && ToLower(*s1) == ToLower(*s2)) {
	*s2++;
	if (*s1++ == '\0')
		return(FALSE);
    }

    return (n<0 ? 0 : *s1 - *--s2);
}
#endif STRNCASECMP

/*	END OF TEXT	*/
!E!O!F!
echo x - sample.shitlist
cat >sample.shitlist <<'!E!O!F!'
bounce phr	50	2
bounce stephan	100	40
bounce c60a-4fl	33	2
bounce adamj	20
bounce muir	25
bounce hans	75	1
bounce andy
bounce cimarron	20	1
bounce shipley	50	2
bounce psb	30	1
mailerhost widow.berkeley.edu
!E!O!F!
cd ..
Pete Shipley: 
email: shipley@berkeley.edu		Flames:  cimarron@postgres.berkeley.edu 
       uunet!lurnix!shipley or ucbvax!shipley or pyramid!hippo!{ root peter }
Spelling corections: /dev/null                    Quote: "Anger is an energy"