home *** CD-ROM | disk | FTP | other *** search
Wrap
/* Cypherpunks Type 1 Anonymous Remailer */ /* Code published on http://www.fravia.org on 26 November 98 */ #undef CONFIGURED /* REMAILER INSTALLATION AND CONFIGURATION INSTRUCTIONS First, load the file remailer.c into your favorite editor. You will see a series of #define statements at the beginning. These should be changed to suit your needs. The values are: DIR - The directory path where the remailer is. ANONFROM - This is what is used as the From line when the remailer sends an anonymous message. It should begin with From: and end with a \n ANONREPLYTO - This is used for "Reply-To: wherever \n" to discourage replies. REMAILERFROM - This is what should go in the From line when the remailer returns a help or stats response. It should also begin with From: and end with \n REMAILERADDRESS - The address of the remailer. This should contain the remailer's email address only. RETURN (Optional) - If defined, the remailer calls sendmail with the -f option and uses this as the return address. Generally, errors or bounces will get sent to this address. DISCLAIMER - Any header you want inserted into an anonymous message to identify where to send complaints, etc NONANONDISC - A header to be inserted into messages when sent with the non-anonymous forwarding feature. SPOOL - The file to append non-remailer messages to. Usually /var/spool/mail/remailer if the account is named "remailer". SPAM_THRESHOLD (optional) - If more than this number of messages arrive before they can be processed, the remailer will stop processing messages. This is to prevent spams and mailbombs. If this happens, you will need to remove the extra messages from the in.queue directory before the remailer will function again. WAIT_SEC (optional) - After delivering a message, the remailer will wait this many seconds before delivering another one. Increasing this value can reduce system load, but will make it possible to reach the spam threshold more easily. DEFAULT_LATENCY (optional) - If this is defined, any message which does not have a latent-time header will have a random delay added to it, not exceeding this number of seconds. (1 hour = 3600 seconds) PGP - The pathname of your PGP executable PGPPASS - The password to the remailer's PGP secret key. PGPPATH - Sets the PGPPATH environment variable. Normally the same as DIR INEWS - The pathname to the INEWS executable for usenet posts. If this is not defined, the remailer does not allow posting. Often /usr/lib/news/inews, but may be different depending on your system. NNTPSERVER - The NNTP server to use for posting. If defined, sets the enviornment variable NNTPSERVER when calling INEWS. LS - The ls program. Usually /bin/ls SENDMAIL - Sendmail. Usually /usr/lib/sendmail The rest are the names of the files the remailer uses. They are all relative to DIR, and except for remailer-help, are created by the remailer if they do not exist. You shouldn't need to change them. They are: BLOCKFROM: the name of the source block list (default: source.block) BLOCKTO: the name of the destination block list (default: dest.block) INQUEUE: The directory for incoming messages (default: in.queue) OUTQUEUE: The directory for outgoing messages (default: out.queue) TEMPDIR: Directory for temporary files (default: temp) STATSDATA: File to keep usage statistics in (default: statsdata) HELPFILE: The "remailer-help" file After setting up the remailer, compile it by typing: gcc remailer.c -o remailer Next, create a PGP key for the remailer. Be sure the password matches the one you put in remailer.c, and put the pubring and secring in the directory you set for PGPPATH. Extract the key with ascii armor and put it into the remailer-help file. Be sure to have a randseed.bin file in the directory, or PGP will not run properly. Finally, create a .forward file in your home directory. In it, put a | as the first character, followed by the complete pathname of the remailer executable. For example, if the remailer was in the home directory of the account "remailer", and user directories were under /home, you would put: |/home/remailer/remailer The remailer should now be operational. Send a test message from another account to be sure it works. If you defined a default latency, you will need to put a Latent-Time: +0:00 header to get an immediate response. Address Blocking Two files specify lists of addresses to block. Any message coming from an address specified in the source.block file is discarded. Any message sent to an address in the dest.block file is similarly discarded. The dest.block file can also be used to block posts to newsgroups. The blocking files consist of a list of addresses, one on each line. Addresses will be matched regardless of whether it is upper or lower case. A * may be used as a wildcard. For example, spammer@*.velveeta.com would block the address spammer at any subdomain of velveeta.com. Any line beginning with a ! is an exclusion, and the address following it will be unblocked even if it matched a previous line. An exclusion must come after the line which would block it. Any line beginning with a # is treated as a comment line, and is ignored. Message Delivery and Cron Every time the remailer is run, it also checks the outgoing message queue to see if there are any latent messages to be delivered. Ordinarilly the messages will be delayed until the next time a message comes in, but you can set up a cron job to deliver the waiting messages in a more timely fashion. To get the remailer to deliver queued messages, execute the remailer and send it an empty message with the command: remailer </dev/null /* END OF INSTALLATION INSTRUCTIONS */ /* ----------------------------------------- */ /* Modify these variables to fit your system */ /* ----------------------------------------- */ #ifndef CONFIGURED #define DIR "/home/remailer" #define ANONFROM "From: nobody@foo.com (Anonymous)\n" #define ANONREPLYTO "Reply-To: unsupported@localhost (This remailer doesn't support replies)\n" #define REMAILERFROM "From: remailer@foo.com (Foo Remailer)\n" #define REMAILERADDRESS "remailer@foo.com" #define RETURN "remailer@foo.com" #define DISCLAIMER "Comments: Please report misuse of this automated remailing service to <remailer-admin@foo.com>\nComments: The message sender's identity is unknown, unlogged, and not replyable.\n" #define NONANONDISC "Comments: This message was forwarded by an automated remailing service. No attempt was made to verify the sender's identity, whcihis unknown and not logged. Reply messages will not reach the sender. Please report misuse to <complaints@site>\n" #define SPOOL "/var/spool/mail/remailer" #define SPAM_THRESHOLD 25 #define WAIT_SEC 30 #define DEFAULT_LATENCY 3600 #define PGP "/usr/local/bin/pgp" #define PGPPASS "password" /* Boy is this real secure leaving the password around like this! */ #define PGPPATH DIR #define INEWS "/usr/lib/news/inews" #define NNTPSERVER "127.0.0.1" #define LS "/bin/ls" #define SENDMAIL "/usr/lib/sendmail" #define BLOCKFROM "source.block" #define BLOCKTO "dest.block" #define INQUEUE "in.queue" #define OUTQUEUE "out.queue" #define TEMPDIR "temp" #define HELPFILE "remailer-help" #define STATSDATA "statsdata" #define SPAMWARNFILE "spam-warn" #endif CONFIGURED /* ------------------------------------------------------ */ /* You shouldn't need to modify anything beyond this line */ /* ------------------------------------------------------ */ /* History Matt Ghio version on usura, Bill Stewart modified /* End of history */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <time.h> #include <sys/time.h> /* some os need this one also */ /*File io stuff:*/ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* arrays need to be at least 1001 long to accommodate RFC822 */ #define MAXLEN 1024 /* added stuff to fake out djgpp for testing under DOS */ #ifdef __GO32__ #define SIGCONT 99 #endif FILE *infile; FILE *outfile; FILE *tempfile; FILE *file2; char from[MAXLEN]=""; char from_address[MAXLEN]=""; char cutmarks[MAXLEN]=""; int anon_flag=0; int help_flag=0; int stat_flag=0; int pgp_flag=0; char replykey[80]=""; char idbuf[17]; int idcount=0; struct timeval tp; unsigned long latime; int blockflag; int nospamcheck_flag=0; /* extract name and address from From: line input */ /* ASSERT: Bounds checking not needed because main() does it */ void getfrom(char *input){ int x=0,y=0; while(input[x]!=':'){x=x+1;} x=x+1; while(input[x]<=32){x=x+1;} while(input[x]>32){ from_address[y]=input[x]; x=x+1;y=y+1; } from_address[y]=0; x=0; /* look for <address> */ while(input[x]>31&&input[x]!='<'){x=x+1;} if(input[x]=='<'){ y=0;x=x+1; while(input[x]>32&&input[x]!='>'){ from_address[y]=input[x]; x=x+1;y=y+1; } from_address[y]=0; } } /* block any addresses matching patterns in file */ /* returns result by zeroing address[0] */ /* crude wildcarding (*) case-insensitive matches */ /* ASSERT: Bounds checking not needed because main() does it */ void block_addr(char address[],char *file) { char input[MAXLEN]; int match=0; int x,y,z; int exclude; FILE *killfile; chdir(DIR); if(killfile=fopen(file,"r")){ while(fscanf(killfile,"%s",input)>0) { if (input[0]!='#'&&input[0]>32) { x=0;exclude=0;z=0; if (input[0]=='!') {exclude=1;z++;} while(address[x]!=0) { y=0; while ((address[x+y]|32)==(input[y+z]|32)&&input[y+z]!='*' &&input[y+z]!=0&&address[x+y]!=0) { y++; } if (input[y+z]==0) match=(1^exclude); if (input[y+z]=='*') {z=z+y+1;x=x+y;} else x++; } } } fclose(killfile); } if (match==1) address[0]=0; } /* find str1 in case-insensitive str2 */ /* only used to check for remailer-* strings */ int search(char str1[],char str2[]) { int x=0; int y=0; int match=0; while(str2[x]!=0) { y=0; while ((str2[x+y]==str1[y]||str2[x+y]==(str1[y]-32))&&str2[x+y]!=0) { y++; if (str1[y]==0) match=1; } x++; } return(match); } /* strncmp variant, case-insensitive, returns 1 for match else 0 */ /* compares first strlen(str2) characters (not sure if this is being efficient or just inviting bugs :-) but I want to prevent the programmer from having to count characters, and if I just check for the first end-of-string, it invites problems like match("S","Subject:") */ int match(char *source, char *pattern) { register char *s, *p; s=source; p=pattern; while (*p && ((*s++)|32)==((*p++)|32)) ; if (*p) return 0; return 1; } /* Workhorse subroutine - scans header lines for remailer-related info */ /* used to have lots of complex hand-built string compares; replacing most with match */ /* ASSERT: Bounds checking not needed because main() does it */ void scanline(char input[],char resend_address[]) { register int x,y,z; int resend_flag=0; int cutmarks_flag=0; int post_flag=0; int latent_plusflag; int latent_randflag; int latent_h; int latent_m; int latent_s; int reply_check=0; /* Pass thru Subject, Content-Type, and In-Reply-To lines */ if (match(input,"Subject:")) { /* if the subject line is blank, drop it */ if (input[8]!=0&&input[9]!=0) fprintf(outfile,"%s",input); /* and handle special case subjects for help and stats */ /* but prevent loops (and denial-of-service) */ if (strncmp(input+9,"Re:",3)!=0) { if (search("remailer-stat",input)) { latime=tp.tv_sec; /* No latency */ stat_flag=1; } if (search("remailer-help",input)||search("remailer-info",input) || search("help", input) ) { latime=tp.tv_sec; /* No latency */ help_flag=1; } } } else if (match(input, "Content-")) { fprintf(outfile,"%s",input); } else if (match(input, "In-Reply-To:")) { fprintf(outfile,"%s",input); } /* Save the From: line in case non-anonymous posting is requested */ /* (only From: and not From ) */ else if (match(input, "From:")) { getfrom(input);block_addr(from_address,BLOCKFROM); if(from_address[0]==0) blockflag=1; /* Source block */ block_addr(input,BLOCKTO); strcpy(from,input); } /* Match headers */ else if (match(input,"Request-Remailing-To:")) { resend_flag=1; anon_flag=1; } else if (match(input,"Remail-To:")) { resend_flag=1; anon_flag=1; } else if (match(input,"Anon-To:")) { resend_flag=1; anon_flag=1; } else if (match(input,"Anon-Send-To:")) { resend_flag=1; anon_flag=1; } else if (match(input,"Post-To:")) { resend_flag=1; } /* soda.berkeley style Send-To ? */ else if (match(input,"Send-To:")) { resend_flag=1; } #define HEADERTOOLONG 100 /* Check for PGP... I got a little sloppy here...ohwell*/ else if (match(input,"Encrypted:")) { resend_flag=0; pgp_flag=1; } else if (match(input,"Encrypt-")) { x=7;y=0; while(input[x++]!=':') {if (x>HEADERTOOLONG) goto hosed;} while(input[x]==' '||input[x]=='\t'){x=x+1;} z=x; /* what's this z for??? */ while(input[x]>' ') {replykey[y++]=input[x++];} /* ASSERT: input[1023]=0 */ replykey[y]=0; hosed: ; } else if (match(input,"Cutmarks:")||match(input,"Cutmark:")) { cutmarks_flag=1; } /* process input to get address */ if(resend_flag){ x=2;y=0; /* x=2 in case Extropians-style ::Header */ while(input[x]!=':'){x=x+1;} x=x+1; while(input[x]<=32){x=x+1;} z=x; if (post_flag==0) { while(input[x]>32){ resend_address[y]=input[x]; x=x+1;y=y+1; } resend_address[y]=0; x=0; /* look for <address> */ while(input[x]>31&&input[x]!='<'){x=x+1;} if(input[x]=='<'){ y=0;x=x+1; while(input[x]>32&&input[x]!='>'){ resend_address[y]=input[x]; x=x+1;y=y+1; } resend_address[y]=0; } /* Print out new To: line */ fprintf(outfile,"To: "); while(input[z]>0){ fprintf(outfile,"%c",input[z]); z=z+1; } block_addr(resend_address,BLOCKTO); } if (post_flag) { fprintf(outfile,"Newsgroups: "); while(input[z]>0){ fprintf(outfile,"%c",input[z]); z=z+1; } resend_address[0]='p'; resend_address[1]='o'; resend_address[2]='s'; resend_address[3]='t'; resend_address[4]=0; block_addr(input,BLOCKTO);if (input[0]==0) resend_address[0]=0; } } if(cutmarks_flag){ x=0;y=0; while(input[x]!=':'){x=x+1;} x=x+1; if(input[x]==32){x=x+1;} z=x; while(input[x]>32){ cutmarks[y]=input[x]; x=x+1;y=y+1; } cutmarks[y]=0; } if (match(input,"Latent-Time:")) { x=12; while (input[x]==' '||input[x]=='\t'){x++;} latent_plusflag=0;latent_randflag=0; latent_h=0;latent_m=0;latent_s=0; while((input[x]<'0'||input[x]>'9')&&input[x]>=32) { if (input[x]=='+') latent_plusflag=1; if ((input[x]=='r')||(input[x]=='R')) latent_randflag=1; x++; } while (input[x]>='0'&&input[x]<='9') { latent_h=(latent_h*10)+(input[x]-48); x++; } if(input[x]==':') { x++; while (input[x]>='0'&&input[x]<='9') { latent_m=(latent_m*10)+(input[x]-48); x++; } if(input[x]==':') { x++; while (input[x]>='0'&&input[x]<='9') { latent_s=(latent_s*10)+(input[x]-48); x++; } } } while(input[x]>=32) { if (input[x]=='+') latent_plusflag=1; if ((input[x]=='r')||(input[x]=='R')) latent_randflag=1; x++; } latime=(latent_h*3600+latent_m*60+latent_s); if(latent_plusflag==0) { /* Not Supported - Is this really necessary? */ } if(latent_randflag&&(latime>1)) { /* Simple randomizer */ latime=abs((tp.tv_sec^latime)+tp.tv_usec+(getpid()*latime))%(latime+1); } latime+=tp.tv_sec; } } char* genid() { /* Generate ascii id from process id and time with shuffle */ unsigned long int id1,id2; int x=0; id1=getpid()|(idcount<<16); id2=tp.tv_sec; idcount++; for(x=32;x--;){ id1+=1234567890; id1^=0xABCDEF12; id1=(id1<<1)|(id1>>31); id2^=id1; id2+=0x12345678; id2^=0x9ABCDEF0; id2=(id2<<31)|(id2>>1); id1^=id2; } for(x=0;x<8;x++) { idbuf[x]=65+(id1&15); id1=id1>>4; } for(x=8;x<16;x++) { idbuf[x]=65+(id2&15); id2=id2>>4; } idbuf[16]=0; return(idbuf); } /* Re-encrypt messages for use with reply-blocks */ void reencrypt(){ char input[MAXLEN]; int pipefd[2]; int pipe2fd[2]; input[MAXLEN-1]=0; pipe(pipefd); pipe(pipe2fd); if(!fork()) { dup2(pipefd[0],0); dup2(pipe2fd[1],1); close(pipefd[1]); close(pipe2fd[0]); chdir(DIR); execl(PGP,"pgp","-fcta","+BATCHMODE","+ARMORLINES=0","-z",replykey,(char *)0); } close(pipefd[0]);close(pipe2fd[1]); file2=fdopen(pipefd[1],"w"); while(fgets(input,MAXLEN-1,infile)) { fprintf(file2,"%s",input); } fclose(file2); file2=fdopen(pipe2fd[0],"r"); while(fgets(input,MAXLEN-1,file2)) { fprintf(outfile,"%s",input); } fclose(file2); } void updatestats(int inccnt,int incpgp,int inclat,int incpost) { int m[24]; int ccm=0; int p[24]; int ccpgp=0; int l[24]; int ccl=0; int u[24]; int ccnews=0; char month[24][5]; int date[24]; int hour=0; int currenthour; FILE *datafile; int x; int y; struct tm *timestr; timestr=localtime(&(tp.tv_sec)); if(datafile=fopen(STATSDATA,"r")){ fscanf(datafile,"%d",&hour); fscanf(datafile,"%d %d %d %d",&ccm,&ccpgp,&ccl,&ccnews); for(x=0;x<24;x++) { fscanf(datafile,"%s %d %d %d %d %d", month[x],&date[x],&m[x],&p[x],&l[x],&u[x]); } fclose(datafile); }else{ for(x=0;x<24;x++) { strcpy(month[x],"---"); date[x]=0;m[x]=0;p[x]=0;l[x]=0;u[x]=0; } } currenthour=(*timestr).tm_hour; x=hour%24; while (x!=currenthour) { if (x>0) { strcpy(month[x],month[x-1]); date[x]=date[x-1]; }else{ if((*timestr).tm_mon==0) strcpy(month[0],"Jan"); if((*timestr).tm_mon==1) strcpy(month[0],"Feb"); if((*timestr).tm_mon==2) strcpy(month[0],"Mar"); if((*timestr).tm_mon==3) strcpy(month[0],"Apr"); if((*timestr).tm_mon==4) strcpy(month[0],"May"); if((*timestr).tm_mon==5) strcpy(month[0],"Jun"); if((*timestr).tm_mon==6) strcpy(month[0],"Jul"); if((*timestr).tm_mon==7) strcpy(month[0],"Aug"); if((*timestr).tm_mon==8) strcpy(month[0],"Sep"); if((*timestr).tm_mon==9) strcpy(month[0],"Oct"); if((*timestr).tm_mon==10) strcpy(month[0],"Nov"); if((*timestr).tm_mon==11) strcpy(month[0],"Dec"); date[0]=(*timestr).tm_mday; } m[x]=0; p[x]=0; l[x]=0; u[x]=0; x++;if (x>23) x=0; } if (hour!=currenthour) { m[hour]=ccm; p[hour]=ccpgp; l[hour]=ccl; u[hour]=ccnews; ccm=0; ccpgp=0; ccl=0; ccnews=0; } ccm+=inccnt; ccpgp+=incpgp; ccl+=inclat; ccnews+=incpost; if(datafile=fopen(STATSDATA,"w")){ fprintf(datafile,"%d\n",currenthour); fprintf(datafile,"%d %d %d %d\n",ccm,ccpgp,ccl,ccnews); for(x=0;x<24;x++) { fprintf(datafile,"%s %d %d %d %d %d\n", month[x],date[x],m[x],p[x],l[x],u[x]); } fclose(datafile); } else fprintf(stderr,"remailer: can't write file %s\n",STATSDATA); } void viewstats() { int m[24]; int ccm; int p[24]; int ccpgp; int l[24]; int ccl; int u[24]; int ccnews; char month[24][5]; int date[24]; int hour; int currenthour; FILE *datafile; int x; int y; char buffer[1024]; datafile=fopen(STATSDATA,"r"); fscanf(datafile,"%d",&hour); fscanf(datafile,"%d %d %d %d",&ccm,&ccpgp,&ccl,&ccnews); for(x=0;x<24;x++) { fscanf(datafile,"%s %d %d %d %d %d", month[x],&date[x],&m[x],&p[x],&l[x],&u[x]); } fclose(datafile); fprintf(outfile,"Subject: Re: Remailer Statistics\n"); /* Note - don't even *THINK* about leaving out the Re: ! */ /* Unless somebody's depending on it, I'd prefer to use "Subject: Re: Remailer Statistics from %s\n", REMAILERADDRESS */ fprintf(outfile,"\n"); #ifdef SPAM_THRESHOLD if (access(SPAMWARNFILE,R_OK)==0) /* there's spam! spam! */ { FILE *spamfile; spamfile = fopen(SPAMWARNFILE,"rt"); while(fgets(buffer,1023,spamfile)>0) fputs(buffer,outfile); fclose(spamfile); fprintf(outfile,"\n"); } else fprintf(outfile, "No Spam problems detected at this time\n\n"); #endif fprintf(outfile,"Statistics for last 24 hours from anonymous remailer at\n"); fprintf(outfile,"e-mail address: %s\n",REMAILERADDRESS); fprintf(outfile,"\n"); fprintf(outfile, "Number of messages per hour from %s %d %d:00 to %s %d %d:59\n", month[23],date[23],hour,month[0],date[0],(hour+23)%24); fprintf(outfile,"\n"); for(x=0;x<24;x++) { fprintf(outfile," %2d:00 (%2d) ",x,m[x]); if (m[x]>0) { y=0;while((y<m[x])&&(y<66)) { fprintf(outfile,"*"); y++; } ccm+=m[x]; ccpgp+=p[x]; ccl+=l[x]; ccnews+=u[x]; } fprintf(outfile,"\n"); } fprintf(outfile,"\n"); fprintf(outfile,"Total messages remailed in last 24 hours: %d\n",ccm); #ifdef PGP fprintf(outfile,"Number of messages encrypted with PGP: %d\n",ccpgp); #endif fprintf(outfile,"Number of messages queued with latency: %d\n",ccl); #ifdef INEWS fprintf(outfile,"Number of posts to usenet: %d\n",ccnews); #endif } void main(int argc,char **argv) { char input[MAXLEN]; char resend_address[MAXLEN]=""; int stop; int x; pid_t mypid,otherpid; char filename[MAXLEN]; char filename2[MAXLEN]; int pipefd[2]; int pipe2fd[2]; char envstr[MAXLEN]; int c; while ( (c = getopt(argc, argv, "s")) != EOF ) { switch(c) { case 's': nospamcheck_flag++; break ; default: fprintf(stderr, "Bad Option %c - ignored\n", c); } } /* bounds-checking insurance */ input[MAXLEN-1]='\0'; resend_address[MAXLEN-1]='\0'; filename[MAXLEN-1]='\0'; filename2[MAXLEN-1]='\0'; envstr[MAXLEN-1]='\0'; from[MAXLEN-1]='\0'; from_address[MAXLEN-1]='\0'; cutmarks[MAXLEN-1]='\0'; gettimeofday(&tp,0); if(chdir(DIR)) {fprintf(stderr,"remailer: Fatal Error: can't chdir to %s\n",DIR);exit(1);} mkdir(INQUEUE,0700); /* Create it if it doesn't exist */ mkdir(TEMPDIR,0700); /* And the temp dir */ /* Create a randomly named temporary file in TEMPDIR */ /* note: creating random names and reading the lowest-named file later on creates a slight bias toward LIFO */ strcpy(filename,TEMPDIR); strcat(filename,"/"); strcat(filename,genid()); if((outfile=fopen(filename,"w"))==0){ fprintf(stderr,"remailer: Fatal Error: can't create temporary file\n"); exit(1); } while(fgets(input,MAXLEN-1,stdin)) fprintf(outfile,"%s",input); fclose(outfile); strcpy(filename2,INQUEUE); strcat(filename2,"/"); strcat(filename2,genid()); if(rename(filename,filename2)){ fprintf(stderr,"remailer: Fatal Error: can't move %s to %s\n", filename,filename2); exit(1); } mypid=getpid();otherpid=0; if(infile=fopen("pid","rb")) { fread(&otherpid,sizeof(pid_t),1,infile); fclose(infile); } /* If there is a remailer process already running, leave the message in in.queue and exit */ if(otherpid) { if(kill(otherpid,SIGCONT)==0) exit(0); } if(outfile=fopen("pid","wb")) { fwrite(&mypid,sizeof(pid_t),1,outfile); fclose(outfile); } else fprintf(stderr,"remailer: can't write pid file\n"); in_loop: /* Open an input file from in.queue */ chdir(DIR);chdir(INQUEUE); pipe(pipefd); filename[0]=0; if(!fork()) { dup2(pipefd[1],1); close(pipefd[0]); execl(LS,"ls","-1",(char *)0); } x=0;close(pipefd[1]); infile=fdopen(pipefd[0],"r"); while(fgets(filename,MAXLEN-1,infile)) x++; fclose(infile); if(filename[0]==0) exit(0); #ifdef SPAM_THRESHOLD if(x>SPAM_THRESHOLD && stat_flag==0 || nospamcheck_flag >1) /* original version quit without any notification */ { if (access(SPAMWARNFILE,0)!=0) /* brand new spam! */ { FILE *spamfile; spamfile = fopen(SPAMWARNFILE,"wt"); fprintf(spamfile, "spam x %d filename %s\n", x, filename); fclose(spamfile); } fprintf(stderr, "spam x %d filename %s\n", x, filename); if (nospamcheck_flag!=1) exit(7); } #endif for(x=0;filename[x]>32;x++){} filename[x]=0; if(!(infile=fopen(filename,"r"))){} /* Open the output file */ chdir(DIR); mkdir(OUTQUEUE,0700); /* Create it if it doesn't exist */ if(chdir(OUTQUEUE)) {fprintf(stderr,"remailer: Error - can't chdir to %s\n",OUTQUEUE);exit(1);} if(!(outfile=fopen(filename,"w"))) {fprintf(stderr,"remailer: can't write output file, message left in %s\n",INQUEUE);exit(1);} /* Create blank space for fields in output file */ latime=0;resend_address[0]=0;resend_address[MAXLEN-1]=0; fwrite(&latime,sizeof(long),1,outfile); fwrite(resend_address,MAXLEN,1,outfile); /* Initialize latency time & misc */ latime=tp.tv_sec; from[0]=0;cutmarks[0]=0;replykey[0]=0; anon_flag=0;help_flag=0;stat_flag=0;pgp_flag=0;blockflag=0; #ifdef DEFAULT_LATENCY /* Randomly reorder messages if DEFAULT_LATENCY is set */ if(DEFAULT_LATENCY>1) { latime=tp.tv_sec+abs(tp.tv_sec+tp.tv_usec+getpid())%(DEFAULT_LATENCY+1); } #endif /* Scan headers */ fgets(input,MAXLEN-1,infile); while(input[0]!=10) { scanline(input,resend_address); input[0]=10;input[1]=0; fgets(input,MAXLEN-1,infile); } fgets(input,MAXLEN-1,infile); /* end of headers, skip a line */ /* if first line is blank, skip it and look for a :: on the next line */ if(resend_address[0]==0&&input[0]<32) fgets(input,MAXLEN-1,infile); /* Also skip "blank" lines with a space in them: */ if(resend_address[0]==0){ for(x=0;(input[x]<=32)&&(input[x]);x++){} if(input[x]==0) fgets(input,MAXLEN-1,infile); } /* Scan :: headers, if applicable */ if(input[0]==':'&&input[1]==':') { while(input[0]!=10) { scanline(input,resend_address); input[0]=10;input[1]=0; fgets(input,MAXLEN-1,infile); } fgets(input,MAXLEN-1,infile); } /* or scan for headers anyway for idiots who forget the double colon */ if(resend_address[0]==0) { scanline(input,resend_address); if(resend_address[0]!=0) { fgets(input,MAXLEN-1,infile); while(input[0]!=10) { scanline(input,resend_address); input[0]=10;input[1]=0; fgets(input,MAXLEN-1,infile); } } fgets(input,MAXLEN-1,infile); } /* Exec PGP? */ if (pgp_flag) { fclose(outfile); chdir(DIR);chdir(OUTQUEUE); unlink(filename); pipe(pipefd); pipe(pipe2fd); if(!fork()) { dup2(pipefd[0],0); dup2(pipe2fd[1],1); close(pipefd[1]); close(pipe2fd[0]); chdir(DIR); #ifdef PGPPATH strcpy(envstr,"PGPPATH="); strcat(envstr,PGPPATH); putenv(envstr); #endif execl(PGP,"pgp","-f","-z",PGPPASS,(char *)0); } close(pipefd[0]);close(pipe2fd[1]); fseek(infile,0,0); outfile=fdopen(pipefd[1],"w"); while((fgets(input,MAXLEN-1,infile)>0) &&(strcmp(input,"-----BEGIN PGP MESSAGE-----\n")!=0)) {} fprintf(outfile,"%s",input); while(fgets(input,MAXLEN-1,infile) &&(strcmp(input,"-----END PGP MESSAGE-----\n")!=0)) { fprintf(outfile,"%s",input); } fprintf(outfile,"%s",input); fclose(outfile); file2=fdopen(pipe2fd[0],"r"); chdir(DIR);chdir(INQUEUE); outfile=fopen(genid(),"w"); fprintf(outfile,"\n"); while(fgets(input,MAXLEN-1,file2)) { fprintf(outfile,"%s",input); } fclose(file2); /* Append rest of message to decrypted reply-block */ while(fgets(input,MAXLEN-1,infile)) { fprintf(outfile,"%s",input); } fclose(infile);fclose(outfile); unlink(filename);/* Remove the original message from in.queue */ chdir(DIR); updatestats(0,1,0,0); goto in_loop; } if (from[0]==0) anon_flag=1; if (anon_flag) { fprintf(outfile,ANONFROM); #ifdef ANONREPLYTO fprintf(outfile,ANONREPLYTO); #endif fprintf(outfile,DISCLAIMER); }else{ fprintf(outfile,"%s",from); fprintf(outfile,NONANONDISC); } /* Paste in ## headers if present */ if(input[0]=='#'&&input[1]=='#') { /* Kill Reply-To lines with blocked addresses to prevent mailbombs via alt.test */ while(fgets(input,MAXLEN-1,infile)>0&&input[0]>31) { if ((input[0]=='R'||input[0]=='r')&&input[1]=='e'&&input[2]=='p') { block_addr(input,BLOCKTO);if (input[0]!=0) fprintf(outfile,"%s",input); /* Block ## pasted Newsgroups: */ }else if((input[0]|32=='n')&&input[1]=='e'&&input[2]=='w'&&input[3]=='s') { block_addr(input,BLOCKTO);if (input[0]!=0) fprintf(outfile,"%s",input); }else fprintf(outfile,"%s",input); } fprintf(outfile,"\n"); }else{ fprintf(outfile,"\n%s",input); if(replykey[0]>0&&input[0]=='*'&&input[1]=='*') { reencrypt(); } } /* Copy message */ stop=0; while(fgets(input,MAXLEN-1,infile)>0&&(!stop)) { if (cutmarks[0]!=0) { x=0; while(cutmarks[x]==input[x]&&input[x]!=0&&cutmarks[x]!=0) { x++; } if (cutmarks[x]==0) stop=1; } if (!stop) fprintf(outfile,"%s",input); if(replykey[0]>0&&input[0]=='*'&&input[1]=='*') { reencrypt(); } } /* If help or stats were requested, set destination address to reply to sender */ if((resend_address[0]==0)&&(help_flag||stat_flag)){ strcpy(resend_address,from_address); } else {help_flag=0;stat_flag=0;} /* Save time and destination address in binary data table at begining of file */ if (blockflag) resend_address[0]=0; fseek(outfile,0,0); fwrite(&latime,sizeof(long),1,outfile); fwrite(resend_address,MAXLEN,1,outfile); if(help_flag||stat_flag){ chdir(DIR); fprintf(outfile,"%s",REMAILERFROM); if(help_flag) { if(file2=fopen(HELPFILE,"r")){ while(fgets(input,MAXLEN-1,file2)){ if (match(input,"Subject: remailer-")) /* prevent loops */ {fprintf(outfile, "Subject: Re: remailer-%s",(input+18)); continue; } for(x=0;input[x];x++){ if(input[x]=='['&&input[x+1]=='a'&&input[x+2]=='d' &&input[x+3]=='d'&&input[x+4]=='r'&&input[x+5]==']') { fprintf(outfile,"%s",REMAILERADDRESS);x=x+5; } else { putc(input[x],outfile); } } } fclose(file2); } else resend_address[0]=0; } if(stat_flag) {viewstats();} } fclose(outfile); chdir(DIR);chdir(INQUEUE); /* Second message? Put message following cutmarks into inqueue */ if (stop==1&&input[0]==':'&&input[1]==':') { outfile=fopen(genid(),"w"); fprintf(outfile,"\n::\n"); while(fgets(input,MAXLEN-1,infile)>0) { fprintf(outfile,"%s",input); } fclose(outfile); } /* Write non-remailer messages into operator's mailbox */ if (resend_address[0]==0&&from[0]!=0){ fseek(infile,0,0); outfile=fopen(SPOOL,"a"); while(fgets(input,MAXLEN-1,infile)) { fprintf(outfile,"%s",input); } fclose(infile); fprintf(outfile,"\n"); fclose(outfile); unlink(filename); chdir(DIR);chdir(OUTQUEUE); unlink(filename); }else{ fclose(infile); unlink(filename); if(strcmp(resend_address,"null")==0 ||strcmp(resend_address,"/dev/null")==0) resend_address[0]=0; if(resend_address[0]==0){ /* drop empty messages */ chdir(DIR);chdir(OUTQUEUE); unlink(filename); }else{ chdir(DIR); if((latime-tp.tv_sec)>2) updatestats(0,0,1,0); updatestats(1,0,0,0); /* Add one remailed message to stats */ } } /* Deliver messages in out.queue */ gettimeofday(&tp,0); chdir(DIR);chdir(OUTQUEUE); pipe(pipefd); filename[0]=0; if(!fork()) { dup2(pipefd[1],1); close(pipefd[0]); execl(LS,"ls","-1",(char *)0); } x=0;close(pipefd[1]); file2=fdopen(pipefd[0],"r"); while(fgets(filename,MAXLEN,file2)&&filename[0]!=0) { for(x=0;filename[x]>32;x++){} filename[x]=0; if(infile=fopen(filename,"r")){ fread(&latime,sizeof(long),1,infile); fread(resend_address,MAXLEN,1,infile); if (latime<=tp.tv_sec) { pipe(pipe2fd);/*pipe(pipe3fd);*/ if(!fork()) { /*Child*/ dup2(pipe2fd[0],0);close(pipe2fd[1]); /*dup2(pipe3fd[1],1);close(pipe3fd[0]);*/ if(strcmp(resend_address,"post")){ execl(SENDMAIL,SENDMAIL, #ifdef RETURN "-f",RETURN, #endif resend_address,(char *)0); exit(0); }else{ #ifdef INEWS #ifdef NNTPSERVER strcpy(envstr,"NNTPSERVER="); strcat(envstr,NNTPSERVER); putenv(envstr); #endif execl(INEWS,"inews","-h",(char *)0); #endif exit(0); } }else{ /*Parent*/ close(pipe2fd[0]);/*close(pipe3fd[1]);*/ outfile=fdopen(pipe2fd[1],"w"); if(strcmp(resend_address,"post")){ /* We are talking to sendmail */ while(fgets(input,MAXLEN-1,infile)>0) { fprintf(outfile,"%s",input); } fclose(outfile); /* At this point, it's a safe bet that sendmail will deliver the message, so the remailer can delete its copy. If sendmail execution had failed for some reason, this process would have been killed by a SIGPIPE */ unlink(filename); }else{ /* We are talking to inews */ #ifdef INEWS while(fgets(input,MAXLEN-1,infile)>0) { fprintf(outfile,"%s",input); } /* There should be a way to analyze the response from inews and requeue messages that could not be posted due to server failure. Now, the messages just get deleted :( */ unlink(filename); #else /* If posting is not allowed, delete the failed message */ unlink(filename); #endif } } #ifdef WAIT_SEC sleep(WAIT_SEC); #endif gettimeofday(&tp,0); } fclose(infile); } } fclose(file2); goto in_loop; }