.oO Phrack Magazine Oo.
Volume Seven, Issue Forty-Nine
File 09 of 16
by Dr.Dimitri Vulis (KOTM)
A Content-Blind Cancelbot for Usenet (CBCB)
Usenet News is a popular system for transmitting articles. Historically it used to propagate over UUCP. However today most of the transmission is done over the Internet TCP/IP connections using the NNTP protocol (RFC 977).
Each article consists of a series of headers of the form Keyword: value followed by a blank line, followed by the body of the message. Some required headers are self-explanatory: From:, Date:, Subject:.
The Newsgroups: header identifies a series of keywords that can be used to search for articles in the newsfeed. For example: Newsgroups: news.admin.policy,comp.lang.c identifies a Usenet article relevant to both Usenet administrative policy and to the C computer language.
The Message-Id: header uniquely identifies each article. For example: Message-Id: 12341223@whitehouse.gov The message-ids are not supposed to be recycled.
The cancelbot program is supposed to search the user-specified newsgroups for
articles whose headers match user-specified regular expressions and to issue
special 'cancel' control articles. It will copy some of the headers from the
original message and add a special header:
Control: cancel
This program is an NNTP client. Much of the processing is offloaded to an NNTP server, to which the cancelbot talks using the Internet sockets protocol.
This cancelbot does not look at article bodies and is therefore content-blind.
Inputs:
argv[1] (required) hosts file
A line that starts with # is a comment. Otherwise, each line contains the following 5 fields:
- hostname (some.domain.com) or ip address (a.b.c.d)
- port (normally 119)
- Y/N - do we ask this host for NEWNEWS/HEADER?
- I/P/N - do we inject cancels to this host with IHAVE, POST, not at all
- Timeout - the number of seconds to wait for a response from this server.
Example of a hosts file:
ask the local server for new news and post back the cancels
127.0.0.1 119 Y P 60
don't get message-ids from remote server, but give it cancels via IHAVE
news.xx.net 119 N I 300
argv[2] (required) target file
A line that starts with # is a comment. Otherwise, each line contains the following 9 fields:
- List of newsgroups to be scanned for new messages. This is not interpreted by the cancelbot, but passed on to the NNTP server. Per RFC 997, multiple groups can be separated by commas. Asterisk "*" may be used to match multiple newsgroup names. The exclamation point "!" (as the first character) may be used to negate a match. Warning: specifying a single * will generate a lot of data.
Example: news.groups,comp.,sci.,!sci.math.*
A watchword (case-sensitive) that needs to be contained in the article headers for the cancel to be issued.
Format of the Subject: header in the cancel article. C - Subject cancel
(same as Control:) O - Subject: header copied from the original article N - none. If N is specified, then Subject: MUST be provided in the file appended to the header, or the cancel won't propagate. cancel message-id prefix normally cancel. or cn.
Most cancellation articles follow the so-called $alz convention:
Control: cancel
- path constant (string to put in path). May be 'none'.
- path copy # (number of elements to copy from the right, may be 0)
Explanation of these two parameters: each Usenet article contains the "Path:" header with a list of hosts separated by explanation marks. For example: Path: ohost1!ohost2!ohost3!ohost4 If you specify path constant of "nhosta!nhostb" and path copy of 2 then the path written by cbcb will be Path: nhosta!nhostb!ohost3!ohost4
- Name of the file appended to the header or 'none'
Examples:
should be supplied as a courtesy
X-Cancelled-By: Cancelbot
if and only if target file field 3 contains 'N':
Subject: Cancelling a Usenet article
only if posting via IHAVE:
NNTP-Posting-Host: usenet.cabal.org
- Name of the file that will become the body of the cancel or 'none'
If 'none' is specified, the default will be "Please cancel this article."
- The string to be prepended to the newsgroups. Normally 'none', but may be set to something like misc.test (or misc.test,alt.test).
Example of a target file:
delete all articles that mention C++ (but not c++)
comp.lang.c.* C++ C cancel. cyberspam 3 can.hdr none none
no sex in the sci hierarchy, and add misc.test to the cancel
sci.* sex C cn. plutonium 2 can1.hdr can.txt misc.test
argv[3] (optional) datestamp, YYMMDD. If not specified, default is 900101. Only articles after this date are examined. This parameter is not processed by the cancelbot, but passed on to the NNTP server. It should normally be specified so as not to look at old Usenet articles.
argv[4] (optional) timestamp, digits HHMMSS, where HH is hours on the 24-hour clock, MM is minutes 00-59, and SS is seconds 00-59. If not specified, default is 000000. Note that both datestamp and timestamp are in Greenwich mean time.
---------------8<-------cut me loose!-------------->8-------------------------- ed-note: To compile, you must define an OS type (under gcc, this is accomplished using the -Dmacro directive). Under Unix, for example: gcc -DCBCB_UNIX -o cancelbot cbcb.c
---------------8<-------cut me loose!-------------->8--------------------------
cbcb.c: /*
Context-blind CancelBot 0.9 04/01/96
Description of operations:
Open socket connections to the hosts listed in the hosts file
loop on targets { loop on servers { if (newnewsflag=='Y') { send NEWNEWS newsgroups datestamp timestamp GMT to this socket receive a list of message-ids and save them in a LIFO linked list loop on message-ids { send HEADER message-id to this server's socket receieve a header if the header contains the watchword { compose a cancel according to the target file specifications loop on servers { if postflag is P or I send the cancel to this server's socket using posting method } } delete this message-id from the linked list } } } }
*/
ifndef CBCB_UNIX
ifndef CBCB_VMS
ifndef CBCB_NT
ifndef CBCB_OS2
error One of (CBCBUNIX, CBCBVMS, CBCBNT, CBCBOS2) must be defined
endif
endif
endif
endif
include
include
include
include
include
/* various flavors of Unix */
ifdef CBCB_UNIX
/* gcc -DCBCB_UNIX cbcb.c -o cbcb */
include
include
include
include
include
include
include
/* perror to be called after failed socket calls */
define perror_sock perror
/* how to close a socket */
define close_sock close
endif
/* Windows NT, /subsystem:console. The executable is supposed to work under NT and Windows 95, but not under Win32s. */
ifdef CBCB_NT
/* important note: when compiling on NT, say something like cl /DCBCB_NT /Ogaityb1 /G5Fs /ML cbcb.c wsock32.lib */
include
/* regular perror doesn't work with WinSock under NT */
define perror_sock(s) fprintf(stderr,"%s : WinSock error %d\n",s,WSAGetLastError())
/* regular close doesn't work with WinSock under NT */
define close_sock closesocket
/* NT doesn't understand unix-style sleep in seconds */
define sleep(n) Sleep(n*1000)
endif
/* DEC VAX/VMS */
ifdef CBCB_VMS
/* important note: when compiling on VAX/VMS, say something like cc/define=CBCB_VMS cbcb/nodebug/optimize=(disjoint,inline) link cbcb/nouserlib/notraceback,sys$library:ucx$ipc.olb/lib,- sys$library:vaxcrtl.olb/lib (to link in shared routines) */
include
include
include
include
include
include
include
define perror_sock perror
define close_sock close
endif
/* IBM OS/2 - link with tcpip.lib */
ifdef CBCB_OS2
define OS2
/* we will use a BSD-like select, not Oleg's hack */
define BSD_SELECT
define INCL_DOSPROCESS
include /* DosSleep */
include
include
include
include
/#include
include
/* perror to be called after failed socket calls */
define perror_sock fprintf(stderr,"%s : tcp error %d\n",s,tcperrno())
/* how to close a socket */
define close_sock soclose
define sleep(n) DosSleep(n/1000)
endif
/*
Future Macintosh notes: Need Apple's MPW (Macintosh Programmer's Workshop). Build CBCB as an MPW tool. Set the Macintosh file type to MPST and the Macintosh creator to MPS, so we can use stdout and stderr.
Sockets are supposed to be available on the Mac.
*/
ifndef FD_ZERO
/* macros for select() not defined on VAX or HPUX However they are defined to be something completely different under NT WinSock, so we must use macros */
define fd_set int
define FD_ZERO(p) {*(p)=0;}
define FD_SET(s,p) {*(p)|=(1<<(s));}
define FD_ISSET(s,p) ((*(p)&(1<<(s)))!=0)
endif
/* file pointers / FILE *sptr, / hosts file / *tptr; / target file*/
/* there's a reason for making all these variables static. If I weren't lazy, I would have put them in their respective functions with 'static' */
define MAXHOSTS 100
struct { int cfd; /* socket handle */ char newnewsflag; char postflag; int timeout; } hosts[MAXHOSTS]; int nhosts;
short int port;
define ASCII_CR 13
define ASCII_LF 10
define BUFFERSIZE 2048
define BUFFERBIGSIZE 20480
char buffer_big[BUFFERBIGSIZE];
struct _msgidq { char *msgid; struct _msgidq *next; };
struct msgidq *msgqueue,*msg_t;
int parsestate, /* for parsing server responses */ hflag,d_flag; /* shortcut for states when parsing headers */
char hostname[BUFFERSIZE]; char buffer[BUFFERSIZE]; char extraheader[BUFFERSIZE]; char extrabody[BUFFERSIZE]; int filerec; char newsgroups[BUFFERSIZE]; /* target field 1 */ char watchword[BUFFERSIZE]; /* target field 2 */ char subjectflag; /* target field 3 / char cmsg_id_prefix[BUFFERSIZE]; / target field 4 / char path_const[BUFFERSIZE]; / target field 5 / int path_num; / target field 6 / char hdr_fname[BUFFERSIZE]; / target field 7 / char txt_fname[BUFFERSIZE]; / target field 8 / char extra_ngrp[BUFFERSIZE]; / target field 9 */
char datestamp,timestamp; /* for the NEWNEWS command */ char *sznone="none"; char *szcabal=" Usenet@Cabal"; char *szsubject="Subject:"; char *szsubjectc="Subject: cmsg"; char *szendl="\r\n"; char *szempty="";
int nretry; /* number of retries in various places / int nbytes; int host1,host2,i,j; / loop indices */
define NOLDHEADERS 8
/* We're interested in 8 original headers :
Path: 0 (requires special handling) From: 1 Sender: 2 Approved: 3 Newsgroups: 4 Date: 5 Subject: 6 Organization: 7
*/
char *hptr[NOLDHEADERS]; char *tptr[3];
/* ANSI function prototypes */ int cbcbparsehosts(void); int cbcbparsetargets(void); int cbcbprocesstarget(void); int cbcbparsemessageids(void); int cbcbprocessarticle(char *); int cbcbgetheaders(void); void cbcbsaveheaders(void); void cbcbsaveheader(int); int cbcbflushsock(int); int cbcbtestsock(int); int cbcbrecvresp(int,char); int cbcbcopy_buffer(char *);
int main(int argc,char*argv[]) {
/* process the arguments */
if (argc<3 || argc>5) { fprintf(stderr,"Usage: cbcb hostfile targetfile [datestamp] [timestamp]\n"); return(1); }
if (argc<4) datestamp="900101"; else datestamp=argv[3];
if (argc<5) timestamp="000000"; else timestamp=argv[4];
/* open the hosts file */
if (NULL==(sptr=fopen(argv[1],"r"))) { perror("open()"); fprintf(stderr,"cbcb cannot open hosts file %s\n",argv[1]); return(0); }
/* open the target file */
if (NULL==(tptr=fopen(argv[2],"r"))) { perror("open()"); fprintf(stderr,"cbcb cannot open target file %s\n",argv[2]); return(0); }
ifdef SIGPIPE
signal(SIGPIPE,SIG_IGN); /* ignore broken pipes if this platform knows them */
endif
/* establish the connections to the NNTP servers */
if (0==cbcbparsehosts()) { fprintf(stderr,"cbcb unable to connect to any NNTP servers\n"); return(1); }
fclose(sptr);
if (!cbcbparsetargets()) { fprintf(stderr,"cbcb encountered an error processing targets\n"); return(1); }
fclose(tptr);
/* final cleanup */
for (i=0; i WSACleanup(); return(0);
} int cbcbparsehosts(void)
{
unsigned long hostip;
struct hostent *hoststruct;
struct inaddr *hostnode;
/*
struct servent *sp;
*/
struct sockaddr_in serverUaddr; WSADATA wsaData; /* needed for WSAStartup */ if (WSAStartup(MAKEWORD(1,1),&wsaData))
{
perror_sock("WSAStartup()");
fprintf(stderr,"couldn't start up WinSock\n");
return(0);
}
fprintf(stderr,"Found WinSock: %s\n",wsaData.szDescription); if (0!=sockinit())
{
perrorsock("sock_init()");
fprintf(stderr,"couldn't start up sockets - is inet.sys running?\n");
return(0);
} /*
if (NULL==(sp=getservbyname("nntp","tcp")))
{
fprintf(stderr,"Can't find the NNTP port\n");
return(0);
}
...
serverUaddr.sinport=(sp->sport);
*/ /* loop on the hosts file /
nhosts=0;
file_rec=0;
while(NULL!=fgets(buffer,sizeof(buffer),sptr))
{
file_rec++;
if (buffer=='#')
continue;
if (nhosts>=MAXHOSTS)
{
fprintf(stderr,"Please increase MAXHOSTS\n");
break;
}
if (5!=sscanf(buffer,"%2048s %hd %c %c %d",
hostname,&port,&hosts[nhosts].newnewsflag,&hosts[nhosts].postflag,
&hosts[nhosts].timeout))
{
fprintf(stderr,"Error parsing host file line %d \"%s\"\n",filerec,buffer);
continue;
}
/* verify that the newnews flag is Y or N */
if (hosts[nhosts].newnewsflag=='n')
hosts[nhosts].newnewsflag='N';
else if (hosts[nhosts].newnewsflag=='y')
hosts[nhosts].newnewsflag='Y';
else if (hosts[nhosts].newnewsflag!='Y'&&hosts[nhosts].newnewsflag!='N')
{
fprintf(stderr,"Newnews flag %c, must be Y or N on line %d\n",
hosts[nhosts].newnewsflag,filerec);
continue;
}
/* verify that the posting flag is P, or I, or N */
if (hosts[nhosts].postflag=='i')
hosts[nhosts].postflag='I';
else if (hosts[nhosts].postflag=='p')
hosts[nhosts].postflag='P';
else if (hosts[nhosts].postflag=='n')
hosts[nhosts].postflag='N';
else if (hosts[nhosts].postflag!='I'&&hosts[nhosts].postflag!='P'&&hosts[nhosts].postflag!='N')
{
fprintf(stderr,"Posting flag %c, must be I, or P, or N on line %d\n",
hosts[nhosts].postflag,filerec);
continue;
}
/* translate the hostname into an ip address. If it starts with a digit,
try to interpret it as a A.B.C.D address /
if (!isdigit(hostname)||(0xFFFFFFFF==(hostip=inetaddr(hostname))))
{
if (NULL==(hoststruct=gethostbyname(hostname)))
{
perror("gethostbyname");
fprintf(stderr,"Can't resolve host name %s to ip on line %d\n",
hostname,filerec);
continue;
}
hostnode=(struct inaddr)host_struct->h_addr;
fprintf(stderr,"Note: Using NNTP server at %s\n",inet_ntoa(hostnode));
hostip=hostnode->saddr;
} /* fill in the address to connect to /
memset(&serverUaddr,0,sizeof(serverUaddr));
serverUaddr.sin_family=PF_INET;
serverUaddr.sin_addr.s_addr=/htonl/(host_ip); / already in net order */
serverUaddr.sin_port=htons(port); /* try to create a socket */
if ((hosts[nhosts].cfd=socket(AFINET,SOCKSTREAM,0))<0)
{
perror_sock("socket()");
continue;
} conn1:
if (0>=connect(hosts[nhosts].cfd,(struct sockaddr)&serverUaddr,sizeof(serverUaddr)))
goto conn2; / we use goto so we can use continue */
if (nretry>10)
{
fprintf(stderr,"give up trying to connect to %s port %hd on line %d\n",
hostname,port,filerec);
closesock(hosts[nhosts].cfd);
hosts[nhosts].newnewsflag=hosts[nhosts].postflag='N';
continue;
}
perrorsock("connect()");
nretry++;
sleep(1);
goto conn1;
conn2:
if (!cbcbrecvresp(nhosts,'2'))
{
fprintf(stderr,"NNTP problem after connecting to %s port %hd on line %d\n",
hostname,port,filerec);
closesock(hosts[nhosts].cfd);
hosts[nhosts].newnewsflag=hosts[nhosts].post_flag='N';
continue;
}
nhosts++;
} return(nhosts);
} int cbcbparsetargets(void)
{ filerec=0;
while(fgets(buffer,sizeof(buffer),tptr)) /* read a target line */
{
filerec++;
if (buffer=='#') / comment /
continue;
/ parse the buffer into the 8 fields */ if (9!=sscanf(buffer,"%2048s %2048s %c %2048s %2048s %d %2048s %2048s %2048s",
newsgroups, watchword, &subjectflag, cmsgidprefix, pathconst,
&pathnum, hdrfname, txtfname, extrangrp))
{
fprintf(stderr,"Error parsing 8 fields on line %d \"%s\"\n",
file_rec,buffer);
continue;
} /* verify that the subject flag is C, O, or N */ if (subjectflag=='c')
subjectflag='C';
else if (subjectflag=='o')
subjectflag='O';
else if (subjectflag=='n')
subjectflag='N';
else if (subjectflag!='C'&&subjectflag!='O'&&subjectflag!='N')
{
fprintf(stderr,"Subject flag %c, must be C, O, or N on line %d\n",
subjectflag,file_rec);
continue;
} if (0==strcmp(pathconst,sznone)) /* if 'none' is specified */
{
if (pathnum==0)
{
fprintf(stderr,"Can't have pathconst none and pathnum 0\n");
continue;
}
pathconst[0]=0;
}
else /* if not none, append bang if needed */
{
i=strlen(pathconst);
if (pathconst[i-1]!='!')
{
pathconst[i]='!';
path_const[i+1]=0;
}
} if (0==strcmp(extrangrp,sznone)) /* if 'none' is specified */
extrangrp[0]=0;
else /* if not none, append comma if needed */
{
i=strlen(extrangrp);
if (extrangrp[i-1]!=',')
{
extrangrp[i]=',';
extrangrp[i+1]=0;
}
} /* read the extra header lines */ if (0==strcmp(hdrfname,sznone)) /* if 'none' is specified */
*extraheader=0;
else
{
/* try to open the specified file */
if (NULL==(sptr=fopen(hdrfname,"r")))
{
perror("open()");
fprintf(stderr,"cbcb cannot open extra-header file %s\n",hdrfname);
continue;
}
nbytes=fread(buffer,1,BUFFERSIZE,sptr);
fclose(sptr);
if (nbytes>=BUFFERSIZE)
fprintf(stderr,"extra-header file %s is too long\n",hdrfname);
if (!cbcbcopybuffer(extraheader))
{
fprintf(stderr,"error in header file\n");
continue;
}
} /* read the body the same way */ if (0==strcmp(txtfname,sznone)) /* if 'none' is specified */
strcpy(extrabody,"Please cancel this article\r\n");
else
{
/* try to open the specified file */
if (NULL==(sptr=fopen(txtfname,"r")))
{
perror("open()");
fprintf(stderr,"cbcb cannot open body file %s\n",txtfname);
continue;
}
nbytes=fread(buffer,1,BUFFERSIZE,sptr);
fclose(sptr);
if (nbytes>=BUFFERSIZE)
fprintf(stderr,"body file %s is too long\n",txtfname);
if (!cbcbcopybuffer(extrabody))
{
fprintf(stderr,"error in body file\n");
continue;
}
} if (!cbcbprocesstarget()) /* process otherwise. warn and go on if error */
fprintf(stderr,"cbcb encountered a problem processing target, line %d\n",
file_rec);
} return(1);
} int cbcbprocesstarget(void)
{ /* loop on hosts /
for (host1=0; host1 /* compose the rfc 977 newnews command. Ansi C would let us write
nbytes=sprintf(..), but gcc has a non-compilant sprintf which return
buffer instead, so we must use strlen /
sprintf(buffer,"NEWNEWS %s %s %s GMT\r\n",
newsgroups,datestamp,timestamp);
nbytes=strlen(buffer);
/ send the command to the server /
if (nbytes!=send(hosts[host1].cfd,buffer,nbytes,0))
{
perror_sock("NEWNEWS send()");
continue;
}
/ the server is supposed to return a list of message-ids now /
if (!cbcb_parse_message_ids())
fprintf(stderr,"Problem parsing message-ids\n");
/ no 'continue': even if we return a partial queue, try to process it */ /* loop through headers, newest first */
while (msgqueue)
{
msgt=msgqueue;
if (!cbcbprocessarticle(msgqueue->msgid))
fprintf(stderr,"Problem processing article <%s>\n",msgqueue->msgid);
msgqueue=msgqueue->next;
free(msgt);
} } return(1);
} int cbcbparsemessage_ids(void)
{ msgqueue=NULL;
parsestate=7; nretry=0;
recvmsgids:
if (!cbcbtestsock(hosts[host1].cfd)) /* nothing to read */
{
if (nretry>hosts[host1].timeout)
{
fprintf(stderr,"timeout waiting to recv message-ids\n");
return(0);
}
fprintf(stderr,".");
nretry++;
sleep(1);
goto recvmsgids;
}
nbytes=recv(hosts[host1].cfd,buffer,sizeof(buffer),0);
if (nbytes<0) /* an error shouldn't happen here */
{
perror_sock("NEWNEWS recv()");
return(0);
} fwrite(buffer,1,nbytes,stdout); /* for debugging only!! */ /* now see if what we received makes sense /
for (i=0; i parsestate=2;
}
else
{
if (j>=BUFFERBIGSIZE)
{
fprintf(stderr,"Please increase BUFFERBIGSIZE\n");
return(0);
}
bufferbig[j]=buffer[i];
j++;
/* parsestate=1; */
}
break;
case 2:
if (buffer[i]==ASCIICR)
parsestate=3;
else
goto recvbadmsgid;
break;
case 3:
if (buffer[i]==ASCIILF)
parsestate=0;
else
goto recvbadmsgid;
break;
case 4:
if (buffer[i]==ASCIICR)
parsestate=5;
else
goto recvbadmsgid;
break;
case 5:
if (buffer[i]==ASCIILF)
parsestate=6;
else
goto recvbadmsgid;
break;
case 6: /* more data after final . */
goto recvbadmsgid;
case 7: /* initial, really */
if (buffer[i]=='2')
parsestate=8;
else
goto recvbadmsgid;
break;
case 8:
if (buffer[i]==ASCIICR)
parsestate=3;
break;
}
} if (parsestate!=6)
goto recvmsgids;
/* normal competion */
return(1); recvbadmsg_id:
fprintf(stderr,"Unexpected response (expected message-ids) ");
if (i)
{
fprintf(stderr,"after \"");
fwrite(buffer,1,i,stderr);
fprintf(stderr,"\" ");
}
if (i int cbcbprocessarticle(char *msgid)
{ /* if there is any leftover data in the socket, get it out */
cbcbflushsock(hosts[host1].cfd); /* compose the rfc 977 head command */
sprintf(buffer,"HEAD <%s>\r\n",msgid); /* send the command to the server */
nbytes=strlen(buffer);
if (nbytes!=send(hosts[host1].cfd,buffer,nbytes,0))
{
perror_sock("HEAD send()");
return(0);
} /* the server is supposed to return the article headers now */ if (!cbcbgetheaders())
{
fprintf(stderr,"Problem retrieving headers\n");
return(0);
} if (!strstr(buffer_big,watchword))
return(1); /* no match, nothing to do */ /* found the watchword: let's cancel */
cbcbsaveheaders();
sprintf(bufferbig,"\
Path: %s%s\r\n\
From:%s\r\n\
Sender:%s\r\n\
Approved:%s\r\n\
Newsgroups: %s%s\r\n\
Date:%s\r\n\
%s%s%s\
Organization:%s\r\n\
Control:%s\r\n\
Message-ID: <%s%s>\r\n\
%s\
\r\n\
%s\
.\r\n",
pathconst,
hptr[0],hptr[1],hptr[2],hptr[3],extrangrp,hptr[4],hptr[5],
tptr[0],hptr[6],tptr[1],hptr[7],tptr[2],
cmsgidprefix,msgid,extraheader,extrabody); fputs(buffer_big,stderr); /* to see what we're posting */ for (host2=0; host2 return(1); /* all's well */
} int cbcbgetheaders(void)
{ hptr[0]=hptr[1]=hptr[2]=hptr[3]=hptr[4]=hptr[5]=hptr[6]=hptr[7]=NULL;
hflag=dflag=parsestate=0;
nretry=0;
j=0;
/* recv */
recvheaders: if (!cbcbtestsock(hosts[host1].cfd)) /* nothing to read */
{
if (nretry>hosts[host1].timeout)
{
fprintf(stderr,"timeout waiting to recv article headers\n");
return(0);
}
fprintf(stderr,".");
nretry++;
sleep(1);
goto recv_headers;
} nbytes=recv(hosts[host1].cfd,buffer,sizeof(buffer),0);
if (nbytes<0) /* an error shouldn't happen here */
{
perror_sock("headers recv()");
return(0);
} fwrite(buffer,1,nbytes,stdout); /* for debugging only!! */ /* see if what we received makes sense /
for (i=0; i return(1);
recvbadheader:
fprintf(stderr,"Unexpected response (expected headers) ");
if (i)
{
fprintf(stderr,"after \"");
fwrite(buffer,1,i,stderr);
fprintf(stderr,"\" ");
}
if (i void cbcbsaveheaders(void)
{
/* now copy old headers to buffer for safekeeping /
/ only if buffer_big matched the pattern */ /* only Path: is special: no initial space /
if (h_ptr[0]==NULL) / no path /
{
j=0;
h_ptr[0]=" ";
}
else
{
i=h_ptr[0]-buffer_big;
j=path_num;
while (buffer_big[i]!=ASCII_LF)
i++;
i--;
/ now go back and look for the last n bang-separated components, or the
beginning of path */
while (bufferbig[i]>' ' && j)
{
i--;
if (bufferbig[i]=='!')
j--;
}
i++;
j=0;
hptr[0]=buffer;
while (bufferbig[i]!=ASCIILF)
buffer[j++]=bufferbig[i++];
buffer[j++]=0;
} tptr[2]=buffer+j;
sprintf(tptr[2]," cancel <%s>",msgqueue->msgid);
j+=strlen(tptr[2])+1; if (hptr[1]==NULL) /* no from? Highly unlikely */
hptr[1]=szcabal;
else
cbcbsaveheader(1);
if (hptr[2]==NULL) /* sender */
hptr[2]=hptr[1];
else
cbcbsaveheader(2);
if (hptr[3]==NULL) /* approved /
h_ptr[3]=h_ptr[2];
else
cbcb_save_header(3);
if (h_ptr[4]==NULL) / no newsgroups? /
h_ptr[4]="control";
else
cbcb_save_header(4);
if (h_ptr[5]==NULL) / no date??? /
h_ptr[5]=" 1 Jan 1990 00:00 GMT";
else
cbcb_save_header(5);
/ subject is special - must use flag /
if (subject_flag=='O')
{
if (h_ptr[6]==NULL)
h_ptr[6]=szcabal; / no subject??? /
else
cbcb_save_header(6);
t_ptr[0]=szsubject;
t_ptr[1]=szendl;
}
else if (subject_flag=='C')
{
h_ptr[6]=t_ptr[2]; / same as the Control: /
t_ptr[0]=szsubjectc;
t_ptr[1]=szendl;
}
else / if (subjectflag=='N') */
{
tptr[0]=tptr[1]=hptr[6]=szempty;
}
if (hptr[7]==NULL) /* organization */
hptr[7]=szcabal;
else
cbcbsaveheader(7); for (i=0; i<8; i++)
if (hptr[i])
printf("%d:%s\n",i,hptr[i]); } void cbcbsaveheader(int k)
{
i=hptr[k]-bufferbig;
hptr[k]=buffer+j;
while (bufferbig[i]!=ASCIILF)
buffer[j++]=bufferbig[i++];
buffer[j++]=0;
} int cbcbflushsock(int sock)
{
/* if there is any leftover data in the socket, get it out /
while (cbcb_test_sock(sock))
{
nbytes=recv(sock,buffer,sizeof(buffer),0);
if (nbytes<0)
perror_sock("flush recv()"); / but don't abort /
else
fwrite(buffer,1,nbytes,stderr); / display it, as it may be informative */
}
return(1);
} /* use select to see if there's data here.
There don't seem to be any unixes left which understand poll and not select.*/
int cbcbtestsock(int sock)
{
fd_set setm;
static struct timeval zerotime={0,0}; FDZERO(&setm);
FDSET(sock,&setm);
if (select(sock+1,&setm,NULL,NULL,&zerotime)<0)
{
perrorsock("select()");
}
if (FDISSET(sock,&setm))
return(1);
else
return(0);
} int cbcbrecvresp(int host,char c)
{ parse_state=0; nretry=0;
recvresp:
if (!cbcbtestsock(hosts[host].cfd)) /* nothing to read */
{
if (nretry>hosts[host].timeout)
{
fprintf(stderr,"timeout waiting to recv response\n");
return(0);
}
fprintf(stderr,".");
nretry++;
sleep(1);
goto recvresp;
}
nbytes=recv(hosts[host].cfd,buffer,sizeof(buffer),0);
if (nbytes<0) /* an error shouldn't happen here /
{
perror_sock("response recv()");
return(0);
}
/ #ifdef DEBUG /
fwrite(buffer,1,nbytes,stdout); / for debugging only!! /
/ #endif /
/ now see if what we received makes sense /
for (i=0; i if (parsestate!=3)
goto recvresp;
/* normal competion */
return(1); recvbadresp:
fprintf(stderr,"Unexpected response (expected %cxx message) ",c);
if (i)
{
fprintf(stderr,"after \"");
fwrite(buffer,1,i,stderr);
fprintf(stderr,"\" ");
}
if (i int cbcbcopybuffer(char *s)
{
i=j=0;
if (nbytes>0&&buffer[nbytes-1]!='\n')
buffer[nbytes++]='\n';
buffer[nbytes]=0; while (buffer[i])
{
if (j>=BUFFERSIZE)
{
fprintf(stderr,"File too big\n");
return(0);
}
if (buffer[i]=='\n')
*(s+(j++))='\r';
*(s+(j++))=buffer[i++];
}
*(s+j)=0;
return(1);
} ---------------8<-------cut me loose!-------------->8--------------------------ifdef CBCB_NT
endif
ifdef CBCB_NT
endif
ifdef CBCB_NT
endif
ifdef CBCB_OS2
endif
ifdef DEBUG
endif
ifdef DEBUG
endif
ifdef DEBUG
endif