#include "smtprelay.h" #include "sys/time.h" #include "signal.h" /* * Switch for EndOfLine processing * 0 Accept \r \n \r\n \n\r * 1 Accept \n only, ignore all \r's * 2 Accept \r\n only, ignore lone \r's * We see a lot of lines with imbedded \r in porn postings */ #define NG_EOL 1 #if NG_EOL == 0 # define NG_ISTERM(x) ((x) == '\n' || (x) == '\r') #else #if NG_EOL == 1 # define NG_ISTERM(x) ((x) == '\n') #else #if NG_EOL == 2 # define NG_ISTERM(x) ((x) == '\n' && lastchar == '\r') #endif #endif #endif char hostName[MAXHOSTNAMELEN]; static void NewsAlarm(int); /* * Initialize an incomming news connection */ NEWS * NewsInit( int ns, /* news stream file descriptor */ int blocking /* NI_BLOCK, NI_NONBLOCK */ ) { NEWS *nss; /* news stream struct */ int flags; /* socket mode flags */ dprintf(DEBUG_FUN,(debugout, "NewsInit(ns=%d,blocking=%d(%s))\n", ns, blocking, blocking == NI_BLOCK ? "BLOCKING" : blocking == NI_NONBLOCK ? "NON-BLOCKING" : "???" )); /* * Allocate and initialize news stream structure * Returns null value if calloc fails */ if( (nss = (NEWS *)calloc(1,sizeof(NEWS))) == NULL) { LogError("Out memory allocating newstream"); } else { nss->fd = ns; nss->nsbp = nss->nsb; nss->nsbs = 0; } /* * Setup Blocking/Non-Blocking */ if ( (flags = fcntl(nss->fd,F_GETFL,0)) != -1) { /* * Set or clear blocking mode */ if (blocking == NI_NONBLOCK) { flags |= O_NONBLOCK; } else { flags &= (~O_NONBLOCK); } if (fcntl(nss->fd,F_SETFL,flags) == -1) { sprintf( logBuf, "Couldn't set connection mode to %s", blocking == NI_NONBLOCK ? "NON-BLOCKING" : "BLOCKING" ); LogError(logBuf); } } else { LogError("Couldn't get connection mode"); } return(nss); } /* * Close a news connection */ void NewsClose( NEWS *N /* Stream to news server */ ) { dprintf(DEBUG_FUN,(debugout, "NewsClose(N==%p)\n", N )); if(N) { if(shutdown(N->fd,0)) LogError("Shutdown news socket"); close(N->fd); free(N); } } /* * Send a news command * * If command is null, no command is sent but a response is expected. * This is how we handle the original connection message from the server. * * The returned message points to a static location * Returns -1 for an internal error otherwise, the command response number */ int NewsCommand( NEWS *N, /* News server stream */ char *command, /* Command to send */ char **message /* Returned message (static) */ ) { char *wbp; /* Write buffer pointer */ int wbs; /* Write buffer size */ int ws; /* Write size */ static char mbuf[512]; /* returned message buffer */ char tmbuf[sizeof(mbuf)+16]; /* tmp debug buffer */ dprintf(DEBUG_FUN,(debugout, "NewsCommand(N=%p,command='%s',message=%p)\n", N, command, message )); if(message) *message = ""; /* * Send command (if any) to news server */ if(command) { if(strlen(command)) { dprintf(DEBUG_DETAIL,(debugout, "NEWS -> %s\n", command )); if(NewsPuts(N,command,-1,NEWSPUTS_CRLF) < 0) { LogMsg("Write to news server"); return(-1); } } } /* * Get response from the server */ if(NewsGets(N,mbuf,sizeof(mbuf),(int *)0,NEWSGETS_LINE)) { return(-1); } dprintf(DEBUG_DETAIL,(debugout, "NEWS <- %s", mbuf )); if(message) *message = mbuf; return(atoi(mbuf)); } /* * Write a string to the news server * Appends CR+LF to string if NEWSPUTS_CRLF * Returns 0 if ok, -1 if error */ int NewsPuts( NEWS *N, /* News stream */ char *mp, /* Message string */ int msize, /* Message size (-1 = use strlen()) */ int crlf /* NEWSPUTS_CRLF, NEWSPUTS_NONE */ ) { int ml; /* Message length */ char *wbp; /* Write buffer pointer */ int wbs; /* Write buffer size */ int ws; /* Write size */ int nnbs; /* New output buffer size */ static char *nbp; /* Static output buffer */ static int nbs; /* Output buffer size */ dprintf(DEBUG_FUN,(debugout, "NewsPuts(N=%p,mp=%p,msize=%d,crlf=%d)\n", N, mp, msize, crlf )); ml = msize >= 0 ? msize : strlen(mp); if(debugNetDump == TRUE) { LogMsg("NewsPuts"); LogDump((u_char *)mp,msize,LD_HEX|LD_ASCII); } /* * If we are adding the CRLF then transfer to the internal buffer first * Otherwise, write directly from the callers buffer */ if(crlf == NEWSPUTS_CRLF) { /* * Build output buffer. * Expand if necessary. * Concatonate message and CRLF */ nnbs = ml+2; if(nnbs > nbs) { if(nbp) { nbp = (char *)realloc(nbp,nnbs); } else { nbp = (char *)malloc(nnbs); } if(!nbp) { LogError("Out of memory in NewsPuts()"); return(-1); } nbs = nnbs; } memcpy(nbp,mp,ml); nbp[ml++] = '\r'; nbp[ml++] = '\n'; wbp = nbp; } else { wbp = mp; } /* * Write message to news server */ wbs = ml; signal(SIGALRM,NewsAlarm); while(wbs > 0) { alarm(SELECT_TIMEOUT); if((ws = write(N->fd,wbp,wbs)) < 0) { LogError("Write to news stream"); alarm(0); signal(SIGALRM,SIG_DFL); return(-1); } wbp += ws; wbs -= ws; } alarm(0); signal(SIGALRM,SIG_DFL); return(0); } static void NewsAlarm( int sig ) { if (sig == SIGALRM) { LogMsg("NEWSALARM"); alarm(SELECT_TIMEOUT); } else { sprintf(logBuf,"NEWSALARM: signal %d",sig); LogMsg(logBuf); } } /* * Read a line or an article from the news server * * Returns 0 if ok, 1 if EOF else -1 * For NEWSGETS_ARTICLE EOF is the normal return. 0 means the buffer * filled up without finding the end of the article * */ #define NG_IGNORECHAR (mp--, msize++, trs--) int NewsGets( NEWS *N, /* News stream */ char *mbuf, /* Message buffer */ int msize, /* Size of mbuf */ int *gsize, /* Size read (if not NULL) */ int gtype /* NEWSGETS_LINE, NEWSGETS_ARTICLE */ ) { char *mp; /* Pointer into buffer */ char *mpc; /* Pointer for copy */ int rs; /* Read size */ int trs; /* Total read size */ int goteof; /* EOF flag */ char *nbp; /* News buffer pointer */ char *nbs; /* News buffer size */ char c; /* Current char */ char lastchar; /* Last character */ static char nextterm; /* Next terminator char to expect */ fd_set rmask; /* select mask */ struct timeval tval; /* select timeout */ int selres; /* select result */ dprintf(DEBUG_FUN,(debugout, "NewsGets(N=%p,mbuf=%p,msize=%d,gsize=%p,gtype=%d(%s))\n", N, mbuf, msize, gsize, gtype, gtype==NEWSGETS_LINE ? "line" : gtype==NEWSGETS_ARTICLE ? "article" : "?" )); FD_ZERO(&rmask); FD_SET(N->fd,&rmask); /* * Get characters from the news stream till we hit a * or we fill the buffer (leaving room for terminating null) * We leave one extra space for expanding single character line * terminators. */ goteof = trs = 0; c = lastchar = 0; for(mp = mbuf;msize > 2;) { /* * Read from stream if buffer empty */ if(N->nsbs == 0) { tval.tv_sec = SELECT_TIMEOUT; tval.tv_usec = 0; selres = select(N->fd+1,&rmask,0,0,&tval); /* * Socket timeout. */ if (selres == 0) { dprintf(DEBUG_NONE,(debugout, "TIMEOUT: Connection inactive %d seconds\n", SELECT_TIMEOUT )); return(-1); } /* * Error from select */ if (selres < 0) { if(errno != EINTR) { LogError("select()"); } return(-1); } /* * We're only waiting on one thing... */ rs = read(N->fd,N->nsb,sizeof(N->nsb)); if(rs < 0) { LogError("Error reading the news stream"); return(-1); } if(rs == 0) { dlogmsg(DEBUG_ACTIVE,"EOF from news stream"); return(-1); } if(debugNetDump == TRUE) { LogMsg("NewsGets:read"); LogDump((u_char *)N->nsb,rs,LD_HEX|LD_ASCII); } N->nsbp = N->nsb; N->nsbs = rs; } /* * Fullfill request from buffer */ N->nsbs--; msize--; trs++; lastchar = c; c = (*mp++ = *N->nsbp++); if (c == 0) { /* * Ignore nulls */ NG_IGNORECHAR; continue; } #if NG_EOL == 1 /* * Ignore all \r's if EOL type 1 processing */ if ( c == '\r' ) { NG_IGNORECHAR; continue; } #else #if NG_EOL == 0 /* * Type 0 processing (anything goes) * Ignore the matched pair (\r or \n) if combination * terminator. */ if (nextterm) { if( c == nextterm ) { /* * Ignore the second char of a two char terminator */ nextterm = 0; NG_IGNORECHAR; continue; } /* * Single character terminator. Just keep going */ nextterm = 0; } #endif #endif if( NG_ISTERM(c) ) { #if NG_EOL == 0 /* * Remember the next character to ignore * (Not always used depending on EOL processing type) */ if (c == '\n') { nextterm = '\r'; } else { nextterm = '\n'; } #endif if(gtype == NEWSGETS_LINE) { /* * Strip the terminator if getting a line */ NG_IGNORECHAR; *mp = '\0'; break; } /* * Force in the standard EOL (\r\n) */ mp[-1] = '\r'; mp[0] = '\n'; trs++; mp++; msize--; /* * Set flag and break if . found * otherwise, break on \n if getting just one line */ if( trs >= 3 && mp[-3] == '.' && ( trs == 3 || mp[-4] == '\n' ) ) { goteof = 1; break; } continue; } #if NG_EOL == 2 if (c == '\n' || c == '\r') { /* * Ignore any lone terminators if type 2 */ NG_IGNORECHAR; } #endif } /* * Terminate the buffer and * return the total size (not including the null) */ *mp = '\0'; if(gsize) *gsize = mp-mbuf; if(debugNetDump == TRUE) { LogMsg("NewsGets"); LogDump((u_char *)mbuf,mp-mbuf,LD_HEX|LD_ASCII); } /* * Return EOF if terminator */ return(goteof); } /* * Open up an outgoing connection */ NEWS * NewsOpen( char *newshost, /* hostname */ int port /* host port */ ) { struct hostent *h; /* host entry */ char *hp; /* current addr from h */ int hn; /* current addr num in h */ int ns; /* news socket */ NEWS *nss; /* news stream struct */ struct sockaddr_in sin; /* * Lookup host ip address */ if(!(h = gethostbyname(newshost))) { sprintf(logBuf,"Address lookup failed for %s",newshost); LogMsg(logBuf); return((NEWS *)0); } /* * Open connection to the news server */ ns = -1; for(hn=0; hp=h->h_addr_list[hn]; hn++) { /* * Create socket--Fatal error if failure */ if((ns = socket(AF_INET, SOCK_STREAM, 0))<0) { LogMsg("Couldn't create socket to news server"); return((NEWS *)0); } /* * Build socket name */ memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = h->h_addrtype; memcpy((char *)&sin.sin_addr.s_addr, hp, h->h_length); sin.sin_port = htons(port); /* * Connect to socket */ if(connect(ns,(struct sockaddr *)&sin, sizeof(sin))) { sprintf( logBuf, "Couldn't connect to port %d of %s (%s)", ntohs(sin.sin_port), h->h_name, inet_ntoa(sin.sin_addr) ); LogError(logBuf); close(ns); ns = -1; continue; } /* * Success--Break out of loop */ #ifdef DEBUG sprintf( logBuf, "News connection to port %d of %s (%s)", ntohs(sin.sin_port), h->h_name, inet_ntoa(sin.sin_addr) ); LogMsg(logBuf); #endif break; } if(ns < 0) { LogMsg("Couldn't connect to news server"); return((NEWS *)0); } /* * Allocate and initialize news stream structure * Returns null value if calloc fails */ if( (nss = (NEWS *)calloc(1,sizeof(NEWS))) == NULL) { LogMsg("Out memory allocating newstream"); } else { nss->fd = ns; nss->nsbp = nss->nsb; nss->nsbs = 0; } return(nss); } /* * Get our host name */ void HostName(void) { strcpy(hostName,"news"); gethostname(hostName,sizeof(hostName)); hostName[sizeof(hostName)-1] = '\0'; dprintf(DEBUG_STATE,(debugout,"Our hostname is %s\n",hostName)); }