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, ¤t->chance, ¤t->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"