.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: 1. hostname (some.domain.com) or ip address (a.b.c.d) 2. port (normally 119) 3. Y/N - do we ask this host for NEWNEWS/HEADER? 4. I/P/N - do we inject cancels to this host with IHAVE, POST, not at all 5. 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: 1. 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.* 2. A watchword (case-sensitive) that needs to be contained in the article headers for the cancel to be issued. 3. 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. 4. cancel message-id prefix normally cancel. or cn. Most cancellation articles follow the so-called $alz convention: Control: cancel Message-id: However this is not a requirement. 5. path constant (string to put in path). May be 'none'. 6. 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 7. 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 8. 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." 9. 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 (newnews_flag=='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 post_flag 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 (CBCB_UNIX, CBCB_VMS, CBCB_NT, CBCB_OS2) 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 newnews_flag; char post_flag; 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 *msg_queue,*msg_t; int parse_state, /* for parsing server responses */ h_flag,d_flag; /* shortcut for states when parsing headers */ char hostname[BUFFERSIZE]; char buffer[BUFFERSIZE]; char extra_header[BUFFERSIZE]; char extra_body[BUFFERSIZE]; int file_rec; char newsgroups[BUFFERSIZE]; /* target field 1 */ char watchword[BUFFERSIZE]; /* target field 2 */ char subject_flag; /* 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 *h_ptr[NOLDHEADERS]; char *t_ptr[3]; /* ANSI function prototypes */ int cbcb_parse_hosts(void); int cbcb_parse_targets(void); int cbcb_process_target(void); int cbcb_parse_message_ids(void); int cbcb_process_article(char *); int cbcb_get_headers(void); void cbcb_save_headers(void); void cbcb_save_header(int); int cbcb_flush_sock(int); int cbcb_test_sock(int); int cbcb_recv_resp(int,char); int cbcb_copy_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==cbcb_parse_hosts()) { fprintf(stderr,"cbcb unable to connect to any NNTP servers\n"); return(1); } fclose(sptr); if (!cbcb_parse_targets()) { fprintf(stderr,"cbcb encountered an error processing targets\n"); return(1); } fclose(tptr); /* final cleanup */ for (i=0; is_port); */ /* 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].newnews_flag,&hosts[nhosts].post_flag, &hosts[nhosts].timeout)) { fprintf(stderr,"Error parsing host file line %d \"%s\"\n",file_rec,buffer); continue; } /* verify that the newnews flag is Y or N */ if (hosts[nhosts].newnews_flag=='n') hosts[nhosts].newnews_flag='N'; else if (hosts[nhosts].newnews_flag=='y') hosts[nhosts].newnews_flag='Y'; else if (hosts[nhosts].newnews_flag!='Y'&&hosts[nhosts].newnews_flag!='N') { fprintf(stderr,"Newnews flag %c, must be Y or N on line %d\n", hosts[nhosts].newnews_flag,file_rec); continue; } /* verify that the posting flag is P, or I, or N */ if (hosts[nhosts].post_flag=='i') hosts[nhosts].post_flag='I'; else if (hosts[nhosts].post_flag=='p') hosts[nhosts].post_flag='P'; else if (hosts[nhosts].post_flag=='n') hosts[nhosts].post_flag='N'; else if (hosts[nhosts].post_flag!='I'&&hosts[nhosts].post_flag!='P'&&hosts[nhosts].post_flag!='N') { fprintf(stderr,"Posting flag %c, must be I, or P, or N on line %d\n", hosts[nhosts].post_flag,file_rec); 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==(host_ip=inet_addr(hostname)))) { if (NULL==(host_struct=gethostbyname(hostname))) { perror("gethostbyname"); fprintf(stderr,"Can't resolve host name %s to ip on line %d\n", hostname,file_rec); continue; } host_node=(struct in_addr*)host_struct->h_addr; fprintf(stderr,"Note: Using NNTP server at %s\n",inet_ntoa(*host_node)); host_ip=host_node->s_addr; } /* 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(AF_INET,SOCK_STREAM,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,file_rec); close_sock(hosts[nhosts].cfd); hosts[nhosts].newnews_flag=hosts[nhosts].post_flag='N'; continue; } perror_sock("connect()"); nretry++; sleep(1); goto conn1; conn2: if (!cbcb_recv_resp(nhosts,'2')) { fprintf(stderr,"NNTP problem after connecting to %s port %hd on line %d\n", hostname,port,file_rec); close_sock(hosts[nhosts].cfd); hosts[nhosts].newnews_flag=hosts[nhosts].post_flag='N'; continue; } nhosts++; } return(nhosts); } int cbcb_parse_targets(void) { file_rec=0; while(fgets(buffer,sizeof(buffer),tptr)) /* read a target line */ { file_rec++; 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, &subject_flag, cmsg_id_prefix, path_const, &path_num, hdr_fname, txt_fname, extra_ngrp)) { 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 (subject_flag=='c') subject_flag='C'; else if (subject_flag=='o') subject_flag='O'; else if (subject_flag=='n') subject_flag='N'; else if (subject_flag!='C'&&subject_flag!='O'&&subject_flag!='N') { fprintf(stderr,"Subject flag %c, must be C, O, or N on line %d\n", subject_flag,file_rec); continue; } if (0==strcmp(path_const,sznone)) /* if 'none' is specified */ { if (path_num==0) { fprintf(stderr,"Can't have path_const none and path_num 0\n"); continue; } path_const[0]=0; } else /* if not none, append bang if needed */ { i=strlen(path_const); if (path_const[i-1]!='!') { path_const[i]='!'; path_const[i+1]=0; } } if (0==strcmp(extra_ngrp,sznone)) /* if 'none' is specified */ extra_ngrp[0]=0; else /* if not none, append comma if needed */ { i=strlen(extra_ngrp); if (extra_ngrp[i-1]!=',') { extra_ngrp[i]=','; extra_ngrp[i+1]=0; } } /* read the extra header lines */ if (0==strcmp(hdr_fname,sznone)) /* if 'none' is specified */ *extra_header=0; else { /* try to open the specified file */ if (NULL==(sptr=fopen(hdr_fname,"r"))) { perror("open()"); fprintf(stderr,"cbcb cannot open extra-header file %s\n",hdr_fname); continue; } nbytes=fread(buffer,1,BUFFERSIZE,sptr); fclose(sptr); if (nbytes>=BUFFERSIZE) fprintf(stderr,"extra-header file %s is too long\n",hdr_fname); if (!cbcb_copy_buffer(extra_header)) { fprintf(stderr,"error in header file\n"); continue; } } /* read the body the same way */ if (0==strcmp(txt_fname,sznone)) /* if 'none' is specified */ strcpy(extra_body,"Please cancel this article\r\n"); else { /* try to open the specified file */ if (NULL==(sptr=fopen(txt_fname,"r"))) { perror("open()"); fprintf(stderr,"cbcb cannot open body file %s\n",txt_fname); continue; } nbytes=fread(buffer,1,BUFFERSIZE,sptr); fclose(sptr); if (nbytes>=BUFFERSIZE) fprintf(stderr,"body file %s is too long\n",txt_fname); if (!cbcb_copy_buffer(extra_body)) { fprintf(stderr,"error in body file\n"); continue; } } if (!cbcb_process_target()) /* process otherwise. warn and go on if error */ fprintf(stderr,"cbcb encountered a problem processing target, line %d\n", file_rec); } return(1); } int cbcb_process_target(void) { /* loop on hosts */ for (host1=0; host1msgid)) fprintf(stderr,"Problem processing article <%s>\n",msg_queue->msgid); msg_queue=msg_queue->next; free(msg_t); } } return(1); } int cbcb_parse_message_ids(void) { msg_queue=NULL; parse_state=7; nretry=0; recv_msgids: if (!cbcb_test_sock(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 recv_msgids; } nbytes=recv(hosts[host1].cfd,buffer,sizeof(buffer),0); if (nbytes<0) /* an error shouldn't happen here */ { perror_sock("NEWNEWS 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') { /* add to the queue */ msg_t=(struct _msgidq*)malloc(sizeof(struct _msgidq)); if (msg_t==NULL) { fprintf(stderr,"malloc failed\n"); return(0); } msg_t->msgid=(char*)malloc(j+1); if (msg_t->msgid==NULL) { free(msg_t); fprintf(stderr,"malloc failed\n"); return(0); } memcpy(msg_t->msgid,buffer_big,j); *(msg_t->msgid+j)=0; msg_t->next=msg_queue; msg_queue=msg_t; parse_state=2; } else { if (j>=BUFFERBIGSIZE) { fprintf(stderr,"Please increase BUFFERBIGSIZE\n"); return(0); } buffer_big[j]=buffer[i]; j++; /* parse_state=1; */ } break; case 2: if (buffer[i]==ASCII_CR) parse_state=3; else goto recv_bad_msg_id; break; case 3: if (buffer[i]==ASCII_LF) parse_state=0; else goto recv_bad_msg_id; break; case 4: if (buffer[i]==ASCII_CR) parse_state=5; else goto recv_bad_msg_id; break; case 5: if (buffer[i]==ASCII_LF) parse_state=6; else goto recv_bad_msg_id; break; case 6: /* more data after final . */ goto recv_bad_msg_id; case 7: /* initial, really */ if (buffer[i]=='2') parse_state=8; else goto recv_bad_msg_id; break; case 8: if (buffer[i]==ASCII_CR) parse_state=3; break; } } if (parse_state!=6) goto recv_msgids; /* normal competion */ return(1); recv_bad_msg_id: fprintf(stderr,"Unexpected response (expected message-ids) "); if (i) { fprintf(stderr,"after \""); fwrite(buffer,1,i,stderr); fprintf(stderr,"\" "); } if (i\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 (!cbcb_get_headers()) { 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 */ cbcb_save_headers(); sprintf(buffer_big,"\ 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", path_const, h_ptr[0],h_ptr[1],h_ptr[2],h_ptr[3],extra_ngrp,h_ptr[4],h_ptr[5], t_ptr[0],h_ptr[6],t_ptr[1],h_ptr[7],t_ptr[2], cmsg_id_prefix,msgid,extra_header,extra_body); fputs(buffer_big,stderr); /* to see what we're posting */ for (host2=0; host2\r\n",cmsg_id_prefix,msgid); nbytes=strlen(buffer); /* send the command to the server */ if (nbytes!=send(hosts[host2].cfd,buffer,nbytes,0)) { perror_sock("IHAVE send()"); continue; } } if (!cbcb_recv_resp(host2,'3')) { fprintf(stderr,"NNTP problem while trying to post\n"); continue; } nbytes=strlen(buffer_big); if (nbytes!=send(hosts[host2].cfd,buffer_big,nbytes,0)) { perror_sock("article send()"); continue; } if (!cbcb_recv_resp(host2,'2')) { fprintf(stderr,"NNTP problem after posting\n"); continue; } } return(1); /* all's well */ } int cbcb_get_headers(void) { h_ptr[0]=h_ptr[1]=h_ptr[2]=h_ptr[3]=h_ptr[4]=h_ptr[5]=h_ptr[6]=h_ptr[7]=NULL; h_flag=d_flag=parse_state=0; nretry=0; j=0; /* recv */ recv_headers: if (!cbcb_test_sock(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); } #ifdef DEBUG fwrite(buffer,1,nbytes,stdout); /* for debugging only!! */ #endif /* see if what we received makes sense */ for (i=0; i=BUFFERBIGSIZE) { fprintf(stderr,"Please increase BUFFERBIGSIZE\n"); return(0); } buffer_big[j++]=buffer[i]; } /* next i */ if (parse_state!=5) goto recv_headers; return(1); recv_bad_header: fprintf(stderr,"Unexpected response (expected headers) "); if (i) { fprintf(stderr,"after \""); fwrite(buffer,1,i,stderr); fprintf(stderr,"\" "); } if (i' ' && j) { i--; if (buffer_big[i]=='!') j--; } i++; j=0; h_ptr[0]=buffer; while (buffer_big[i]!=ASCII_LF) buffer[j++]=buffer_big[i++]; buffer[j++]=0; } t_ptr[2]=buffer+j; sprintf(t_ptr[2]," cancel <%s>",msg_queue->msgid); j+=strlen(t_ptr[2])+1; if (h_ptr[1]==NULL) /* no from? Highly unlikely */ h_ptr[1]=szcabal; else cbcb_save_header(1); if (h_ptr[2]==NULL) /* sender */ h_ptr[2]=h_ptr[1]; else cbcb_save_header(2); if (h_ptr[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 (subject_flag=='N') */ { t_ptr[0]=t_ptr[1]=h_ptr[6]=szempty; } if (h_ptr[7]==NULL) /* organization */ h_ptr[7]=szcabal; else cbcb_save_header(7); #ifdef DEBUG for (i=0; i<8; i++) if (h_ptr[i]) printf("%d:%s\n",i,h_ptr[i]); #endif } void cbcb_save_header(int k) { i=h_ptr[k]-buffer_big; h_ptr[k]=buffer+j; while (buffer_big[i]!=ASCII_LF) buffer[j++]=buffer_big[i++]; buffer[j++]=0; } int cbcb_flush_sock(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 cbcb_test_sock(int sock) { fd_set setm; static struct timeval zerotime={0,0}; FD_ZERO(&setm); FD_SET(sock,&setm); if (select(sock+1,&setm,NULL,NULL,&zerotime)<0) { perror_sock("select()"); } if (FD_ISSET(sock,&setm)) return(1); else return(0); } int cbcb_recv_resp(int host,char c) { parse_state=0; nretry=0; recv_resp: if (!cbcb_test_sock(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 recv_resp; } 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; i0&&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--------------------------