#include <ctype.h>
/*
Daily stop and start of slave processes  slave_restart h

xactive (number)
xdslave setcached (group)
xnewgroup xxx ... 
xdslave nnn shutdown
fcmd nnn reload
fcmd nnn killslaves



Write Instructions
------------------

	xdmulti (add to dnews, tells it not to timeout, and to send shutdown)
	(stop slave channels from timing out on master and slave);
	test: slave_feed *,*,*
*/

#define MAX_CHAN 4096

/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#ifdef OS2
#define BSD_SELECT   TRUE
#endif


#ifdef WIN32
#define WIN_SOCK TRUE
#endif


#ifdef WIN_SOCK
#define FD_SETSIZE MAX_CHAN /* This should let us have this many connectins ??*/


#include <winsock.h>
#define socket_close closesocket
#define socket_ioctl ioctlsocket
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)
#define GOT_SOCKADDR TRUE
#define NOTUNIX TRUE
#endif

#ifdef NETWARE
#define NOTUNIX TRUE
#define GOT_SOCKADDR TRUE
#define NEED_DTABLE TRUE
#endif

#ifdef __MWERKS__ /*RHP we will take this to mean we are making a Mac version*/
#include "Mac.h"
#include "sys/types.h"
#include "netinet/in.h"
#include "sys/socket.h"
#include "sys/ioctl.h"
#include "netdb.h"
#include "sys/time.h"
#include "socket.ext.h"
#include "MacSockDefs.h"
#endif

#ifdef unix
#ifndef NOTUNIX
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifdef SCO
#include "myhostdb.h"
#else
#include <netdb.h>
#endif
#include <sys/time.h>
#ifndef OS2
#include <unistd.h>
#endif
#ifdef LINUX
#include <linux/in.h>
#else /* This one works for solaris , why the hell isn't it standardized */
#ifdef SCO
#include <sys/netinet/in.h>
#else
#include <netinet/in.h>
#ifndef OS2
#include <arpa/inet.h>
#include <unistd.h>
#endif
#endif
#endif
#define SOCKET_DEF TRUE
#endif
#endif

#ifdef OS2
#include <sys/select.h>
#include <nerrno.h>
#include <utils.h>
#undef SOCKET_DEF
#define socket_ioctl ioctl
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)
#define socket_close(s) soclose(s)
#endif


#ifdef SOCKET_DEF
#define socket_close close
#define socket_open open
#define socket_read read
#define socket_ioctl ioctl
#define socket_write write
#define NEED_BCOPY TRUE

#endif
#ifdef NEED_DECLARE
int socket(unsigned int address,unsigned int type,unsigned int protocol);
void socket_perror(char *s);
int socket_read(int channel, char *buffer, int size);
int socket_write(int channel, char *buffer, int size);
int connect();
int select();
int socket_close();
int setsockopt();
int bind();
int listen();
int accept();
char *bset();
char *bzero();
int htons();
int sleep();
char *inet_ntoa();
int getdtablesize();
#endif


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <signal.h>

#define VERSION_ONLY 1
#include "dnews.h"
#ifdef WIN32
#include <process.h>
#else
#include <unistd.h>
#endif
#ifdef NETWARE
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)
#define socket_close close
#define socket_ioctl ioctl
NETDB_DEFINE_CONTEXT
NETINET_DEFINE_CONTEXT
#endif



int log_bin(char *head, char *msg);
char *cvt_bin_string(unsigned char *bin_string, int size);
static int nolog;


#ifdef WIN_SOCK
static SOCKET x_listen = INVALID_SOCKET;
static SOCKET chan[MAX_CHAN]; /*Should keep list of inuse ones.*/
#else
static short x_listen = -1;
static short chan[MAX_CHAN]; /*Should keep list of inuse ones.*/
#define SOCKET short
#endif

static SOCKET chan_out[MAX_CHAN]; /* Outgoing leg (to news server) */
static char *chan_ipnum[MAX_CHAN];
static int sent_ip[MAX_CHAN];

static struct sockaddr_in chan_sin[MAX_CHAN];
static int chan_closetime[MAX_CHAN];
static int chan_bsy1[MAX_CHAN];
static int chan_bsy2[MAX_CHAN];
static int chan_data1_len[MAX_CHAN];
static int chan_data2_len[MAX_CHAN];
static char *chan_data1[MAX_CHAN];
static char *chan_data2[MAX_CHAN];

int nchan;
static  struct sockaddr_in xsin;
static  struct servent *sp,fakesp;

static int isblock;
static char zzzz[100];

void dmsg(char * arg_list, ...);
char *mystrupr(char *s);
char *mystrlwr(char *s);
void nntp_perror(char *s);
int nntp_errbusy(void);
char *chan_getipnum(int c);
void slave_restart(void);
int chan_isfree(int c);
void chan_newchan(int c);
void nntp_addidle(int x);
void dmulti_exit(char *s);
void nntp_writerest1(int c);
void nntp_writerest2(int c);
char *zemsg(char *fmt, int n, char *bf);


void log_socket_write(int chan, char *bf, int sz);
void nntp_doclose(int c);
int ihavein_clear(int i);
void nntp_lasterror(void);

int dns_ageok(int i, int isreverse);
int dns_add(char *name, struct sockaddr_in *sin, int isok);
int dns_find_name(char *name);
int dns_find_addr(struct sockaddr_in *sin);
int nntp_getaddr(char *name, struct sockaddr_in *sin);
int nntp_getname(char *name, struct sockaddr_in *sin);
void nntp_setblockraw(SOCKET c, int t);
void nntp_newchan_all(void);
int nntp_newchan_waiting(void);
int nntp_busywrite(void);


#ifndef WIN_SOCK
#define INVALID_SOCKET  -1
#endif
#define check(x) if (chan[x]==INVALID_SOCKET) {emsg("nntp: channel not open %d ",x); dnews_ex("nntp");} if (x<0) {emsg("nntp: bad channel %d ",x); dnews_ex("channel");}


#ifdef NEED_BCOPY
#define bcopy(src,dst,len) memmove(dst,src,len)
#endif

int nntp_except(int c);
int nntp_errclosed(void);
static int werrno;
static int max_xx;

#define BIGSZ 8000
#define BIGSZBIN 68000
#define BFSZ 1000
#define mymalloc malloc
#define myfree free
#define filemsg emsg
#define imsgf imsg
#define wmsg imsg
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
char *ncpy(char *dst, char *src, int len);
char *config_get(int c);
int config_true(int c);
void nntp_closebad(void);
void m_idle(void);
void nntp_readwrite1(int c);
int lib_gmtime(void);
void nntp_readwrite2(int c);
int nntp_open(char *dst_name, int n, int xblock);
void nntp_newchan(void);
char *choose_server(char *ipnum);
int emsg(char * arg_list, ...);
int imsg(const char * arg_list, ...);
void emsg_do(char *in);
void emsg_flush(void);
void emsg_open(void);
void do_restart(void);
static int isbinary = FALSE;
#ifdef NOUPPER
#define _tolower(_c)    ( (_c)-'A'+'a' )
#define _toupper(_c)    ( (_c)-'a'+'A' )
#endif


enum {
	C_TCP_NODELAY,C_PORT,C_OPEN_RETRY
	,C_SLAVE_PORTS,C_SLAVE_N, C_MYNAME, C_CONFIG
	,C_SLAVE_FEED,C_WORKAREA, C_SLAVE_RESTART
};
static char *conf_name[]={
	"TCP_NODELAY","PORT","OPEN_RETRY"
	, "SLAVE_PORTS", "SLAVE_N", "MYNAME", "CONFIG"
	, "SLAVE_FEED", "WORKAREA", "SLAVE_RESTART"
, NULL};


static char real_host[BFSZ];
static char lcl_port[BFSZ];

int check_sock(int c, char *s);
int check_sock(int c, char *s)
{
	if (c<0) {
		emsg("%s: Invalid socket number %d\n",s,c);
		return FALSE;
	} 
	if (chan[c]==INVALID_SOCKET) {
	 	emsg("%s: Invalid Socket [%d] probably it closed while we were writing to it\n",s,c);
		return FALSE;
	}
	return TRUE;
}
void nntp_setblock(int c, int t)
{
	nntp_setblockraw(chan[c],t);
}
void nntp_setblockraw(SOCKET c, int t)
{
#ifdef SET_LINGER
	struct linger ling;
#endif
        int w;
        if (t) w=0;
        else w=1;
#ifndef VMS
#ifdef OS2
        if (socket_ioctl(c,FIONBIO,&w,sizeof(w))!=0) filemsg("setblock failed\n");
#else
        if (socket_ioctl(c,FIONBIO,&w)!=0) filemsg("setblock failed\n");
#endif
#endif

#ifdef SET_LINGER
	ling.l_onoff = 1;
	ling.l_linger = 20; 
        setsockopt(c, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));   
#endif

}
#ifdef WIN32
#define HAS_NODELAY TRUE
#else 
#ifdef TCP_NODELAY
#define HAS_NODELAY
#endif
#endif
int chan_init(int i)
{
	chan[i] = INVALID_SOCKET;
	chan_out[i] = INVALID_SOCKET;
	chan_bsy1[i] = FALSE;
	chan_bsy2[i] = FALSE;
	chan_data1[i] = NULL;
	chan_data2[i] = NULL;
	if (chan_ipnum[i]==NULL) chan_ipnum[i] = malloc(40);
	return TRUE;
}
int chan_init2(int i);
int chan_init2(int i)
{
	int on= -1;
       	if (check_sock(i,"setblock")) nntp_setblock(i,FALSE);
	
#ifdef HAS_NODELAY
        	imsg("nntp: Setting TCP_NODELAY for socket [%d]\n",i);
        	setsockopt(chan[i], IPPROTO_TCP, TCP_NODELAY,(char *) &on, sizeof(int));
#endif 
        return TRUE;
}
int nntp_chan(int i)
{
        return (int) chan[i];
}
#ifdef UCX
#define DOCAST
#endif
#ifdef WIN_SOCK
#define DOCAST
#endif
#ifdef NETWARE
#define DOCAST
#endif

#ifdef DOCAST
#define ONCAST (char *)
#else
#define ONCAST
#endif


void nntp_startup(void)
{
        int i;
#ifdef OS2
	if (sock_init()!=0) {
		emsg("FATAL: TCPIP sockets are not installed on this machine\n");
		return;
	}
#endif
#ifdef WIN_SOCK
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
        wVersionRequested = MAKEWORD( 1, 1 );

        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) { emsg("No winsock found of correct version\n"); return;}
        if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
                emsg("No winsock found of correct version, trying to continue\n");
                goto ret;
        }
	imsg("Socket library found, %d sockets available\n",wsaData.iMaxSockets);

/* wmsg("Socket library found and is version 1.1, Great!\n"); */
/* The Windows Sockets DLL is acceptable. Proceed. */
ret: ;
#endif
        for (i=0; i<MAX_CHAN; i++) {
                chan[i] = INVALID_SOCKET;
                chan_out[i] = INVALID_SOCKET;
        }

}
#ifdef WIN32
int getdtablesize(void);
#endif
void nntp_init(int pchan)
{
        int on=1;
        char *s;
        int i;
        int r;
	int quelen;

        imsg("nntp: DTableSize = %d \n",getdtablesize());
        for (i=0; i<MAX_CHAN; i++) {
                chan[i] = INVALID_SOCKET;
                chan_out[i] = INVALID_SOCKET;
        }
        /*
         *  Create an IP-family socket on which to listen for connections
         */

        if (pchan<0) {
                x_listen = socket(AF_INET, SOCK_STREAM, 0);
               	nntp_lasterror();		
                if (x_listen == INVALID_SOCKET) {
                        nntp_perror("nntp: Can't open socket");
                        exit(0x10000000);
                }

                        emsg("nntp: Can't getservbyname \n");
                        sp = &fakesp;
                        fakesp.s_port = htons(atoi(lcl_port));

                r = setsockopt(x_listen, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));
               	nntp_lasterror();		

                if (r < 0) {
                        filemsg("nntp: Can't setsockopt  %d \n",r);
                        nntp_perror("tcpnntpserver: setsockopt");
                        exit(0x10000000);
                }

                xsin.sin_family = AF_INET;
                xsin.sin_addr.s_addr = INADDR_ANY;
                xsin.sin_port = sp->s_port;
                if (xsin.sin_port == 0) xsin.sin_port = htons(119);
 

#ifdef GOT_SOCKADDR
                if (bind(x_listen,(struct sockaddr *)  &xsin, sizeof (xsin)) < 0) {
#else
                if (bind(x_listen,&xsin, sizeof (xsin)) < 0) {
#endif
	              	nntp_lasterror();		
                        filemsg("nntp: Can't bind \n");
                        nntp_perror("tcpnntpserver: bind");
                        exit(0x10000000);
                }
        } else x_listen = pchan;
	nntp_setblockraw(x_listen,FALSE);
 	
	quelen = 25;
#ifdef WIN32
	quelen = 200;
#endif        
        if (listen(x_listen, quelen) < 0) {
              	nntp_lasterror();		
                filemsg("nntp: Can't set listen \n");
                nntp_perror("tcpnntpserver: listen");
                exit(0x10000000);
        }
}
static struct timeval timeout;
static struct timeval timecopy;
void nntp_wait(void)
{
	static int last_give;
        int i,nc;
	static int xlast;
        fd_set readfd,writefd,exceptfd;
	static int curtime=0;
	
        timeout.tv_sec = 1; /*Wake up once a second*/
        timeout.tv_usec = 0;
again:
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
        for (i=0; i<nchan; i++) if ((chan[i]!=INVALID_SOCKET) && (chan_out[i]!=INVALID_SOCKET)) {
		FD_SET(chan[i],&writefd);
		FD_SET(chan[i],&exceptfd);
		FD_SET(chan_out[i],&writefd);
		FD_SET(chan_out[i],&exceptfd);
	}
        timecopy = timeout; /* Because linux clears the value each call */
        if (x_listen!=INVALID_SOCKET) FD_SET(x_listen,&readfd);
	nc = select(getdtablesize(), &readfd, &writefd, &exceptfd, &timecopy);
	if (nc<0) nntp_closebad();
	if (nc==0) {m_idle(); goto again;}
        timecopy = timeout; /* Because linux clears the value each call */
        FD_ZERO(&readfd);
        for (i=0; i<nchan; i++) if ((chan[i]!=INVALID_SOCKET) && (chan_out[i]!=INVALID_SOCKET)) {
		FD_SET(chan[i],&exceptfd);
//imsg("in  isbsy2 %d, writefd %d\n",!chan_bsy2[i],FD_ISSET(chan[i],&writefd));
//imsg("out isbsy1 %d, writefd %d\n",!chan_bsy1[i],FD_ISSET(chan_out[i],&writefd));
		if (!chan_bsy2[i]) if (FD_ISSET(chan[i],&writefd)) FD_SET(chan_out[i],&readfd);
		if (!chan_bsy1[i]) if (FD_ISSET(chan_out[i],&writefd)) FD_SET(chan[i],&readfd);
	}
        if (x_listen!=INVALID_SOCKET) FD_SET(x_listen,&readfd);
        FD_ZERO(&writefd);
	nc = select(getdtablesize(), &readfd, &writefd, &exceptfd, &timecopy);
      
	for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
		if (chan[i]==INVALID_SOCKET) continue;
		if (chan_out[i]==INVALID_SOCKET) continue;
		if (chan_bsy1[i]) nntp_writerest1(i);
		if (chan[i]==INVALID_SOCKET) continue;
		if (chan_out[i]==INVALID_SOCKET) continue;
		if (chan_bsy2[i]) nntp_writerest2(i);
		if (chan[i]==INVALID_SOCKET) continue;
		if (chan_out[i]==INVALID_SOCKET) continue;
                if ((FD_ISSET(chan[i],&exceptfd)) || 
			(FD_ISSET(chan_out[i],&exceptfd))) {
                        emsg("nntp: Channel closed or didn't open [%d] %d\n",i,chan[i]);
			nntp_except(i);
                } else {
			if (chan[i]==INVALID_SOCKET) continue;
			if (chan_out[i]==INVALID_SOCKET) continue;
			if (FD_ISSET(chan[i],&readfd)) nntp_readwrite1(i);
			if (chan[i]==INVALID_SOCKET) continue;
			if (chan_out[i]==INVALID_SOCKET) continue;
			if (FD_ISSET(chan_out[i],&readfd)) nntp_readwrite2(i);
		}
	}
	if (FD_ISSET(x_listen,&readfd)) nntp_newchan();
	if (xlast!=lib_gmtime()) {
		xlast = lib_gmtime();
		m_idle();
	}
	goto again;
}
void nntp_closebad(void)
{
        int i,nc;
        fd_set readfd,writefd,exceptfd;

	imsg("Select returned error, scanning for bad channels\n");
        if (x_listen!=INVALID_SOCKET) FD_SET(x_listen,&readfd);
        for (i=0; i<nchan; i++) if ((chan[i]!=INVALID_SOCKET) && (chan_out[i]!=INVALID_SOCKET)) {
	        FD_ZERO(&readfd);
	        FD_ZERO(&writefd);
	        FD_ZERO(&exceptfd);
		FD_SET(chan[i],&writefd);
		FD_SET(chan[i],&exceptfd);
		FD_SET(chan_out[i],&writefd);
		FD_SET(chan_out[i],&exceptfd);
	        timecopy.tv_sec = 0; /*Wake up once a second*/
	        timecopy.tv_usec = 0;
		nc = select(getdtablesize(), &readfd, &writefd, &exceptfd, &timecopy);
		if (nc<0) {
			imsg("Found bad channel [%d] \n",i);
			nntp_doclose(i);
		}
	}
}
int nntp_except(int c)
{
        nntp_doclose(c);
        return FALSE;
}
int nntp_getchan(void)
{
        /* should search thru list for unallocated one.*/
        int i;
        for (i=0; i<nchan ; i++) {
                if ((chan[i]==INVALID_SOCKET) ) {
                        chan_init(i);
                        return i;
                }
        }
        chan_init(nchan);
    
        if ((nchan+4)>MAX_CHAN) emsg("Channels exhausted, panic %d \n",nchan);
        return nchan++;
}
void nntp_doclose(int c)
{
        if (chan[c]!=INVALID_SOCKET) {
		socket_close(chan[c]);	
	}
        if (chan_out[c]!=INVALID_SOCKET) {
		socket_close(chan_out[c]);	
	}
        chan[c] = INVALID_SOCKET;
        chan_out[c] = INVALID_SOCKET;
	chan_bsy1[c] = FALSE;
	if (chan_data1[c]!=NULL) free(chan_data1[c]);
	chan_data1[c] = NULL;
	chan_bsy2[c] = FALSE;
	if (chan_data2[c]!=NULL) free(chan_data2[c]);
	chan_data2[c] = NULL;
        if (c==nchan-1) nchan--; /* should search backwards too, but doesn' matter much*/
}

#ifdef NEED_NTOA
char *myinet_ntoa(struct in_addr in)
{
	union {unsigned char b[4]; long all;} both;
	static char bf[100];
	both.all = (long) in.s_addr;
 	sprintf(bf,"%d.%d.%d.%d",both.b[0],both.b[1],both.b[2],both.b[3]);
	return bf;
}
#define inet_ntoa myinet_ntoa
#endif


void nntp_newchan(void)
{
        int n=nntp_getchan();
        int sizesin=sizeof(chan_sin[0]);
	char ipnum[200];
	char xhost[BFSZ];
        char bf[BFSZ];
        strcpy(bf,"");

#ifdef GOT_SOCKADDR
        chan[n] = accept(x_listen,(struct sockaddr *) &chan_sin[n], &sizesin);
#else
        chan[n] = accept(x_listen,&chan_sin[n], &sizesin);
#endif
      	nntp_lasterror();		
#ifndef WIN32
	if (((int)chan[n])>getdtablesize()) emsg("FATAL, channel is > than dtablesize %d %d\n",chan[n],getdtablesize());
#endif

	
        if (chan[n] == INVALID_SOCKET) {
          nntp_perror("Socket newchan accept() call failed");
          filemsg("Socket newchan accept() call failed\n");
          return;
        }
	sent_ip[n] = FALSE;
        strcpy(ipnum,inet_ntoa(chan_sin[n].sin_addr));
	strcpy(chan_ipnum[n],ipnum);
	strcpy(xhost,real_host);
	if (n>1950) {
		emsg("Too many connections - disconnecting\n");
		nntp_doclose(n);
		return;		
	}
	nntp_open(xhost,n,FALSE);
	if (chan_out[n]==INVALID_SOCKET) return;
	imsg("Connection from %s %d\n",ipnum,n);
}

int nntp_open(char *dst_name, int n, int xblock)
{
        int dst_port=119;
        struct sockaddr_in xsin;
        int st;
        int gotaddr;
        char bf[BFSZ],*s;
        int dontblock = FALSE;

        memset(&xsin,0,sizeof(xsin));
        xsin.sin_family = AF_INET;

        if (strchr(dst_name,':')!=NULL) {
                ncpy(bf,dst_name,BFSZ-1);
                s = strchr(bf,':');
                *s = 0;
                dst_name = bf;
                if (s==NULL) emsg("nntp: Thats very strange\n");
                dst_port = atoi(s+1);
                if (dst_port==0) dst_port = 119;
        }
        mystrlwr(dst_name); /* Seems to be required by NIS */
        gotaddr = nntp_getaddr(dst_name,&xsin);
	if (!gotaddr) {
                emsg("Host Lookup failed {%s} \n",dst_name);
                chan_out[n] = INVALID_SOCKET;
		nntp_doclose(n);
                return -1;
        }

        chan_out[n] = socket(xsin.sin_family,SOCK_STREAM, 0);
       	nntp_lasterror();		
        if (chan_out[n]==INVALID_SOCKET) {
                nntp_perror("Socket");
                emsg("Can't connect to {%s} \n",dst_name);
                nntp_doclose(n);
                return -1;
        }
        xsin.sin_port = htons((unsigned short) dst_port);

        if (!xblock) nntp_setblockraw(chan_out[n],FALSE);

        st = connect(chan_out[n],(struct sockaddr *)   &xsin, sizeof(xsin));
	nntp_lasterror();		
     	if (st!=0) {
                if (!nntp_errbusy()) {
                        nntp_perror(dst_name);
                        nntp_doclose(n);
                        return -1;
                }
        }
        return n;
}
char *zemsg(char *fmt, int n, char *bf)
{
	char xbf[20000];
	ncpy(xbf,bf,n);
	emsg(fmt,n,xbf);
	return "";
}
void nntp_readwrite1(int c)
{
	int r,w;
	char bf[BIGSZBIN];

	errno = 0;
	r =  socket_read(chan[c],bf,BIGSZ);
       	nntp_lasterror();
	if (r<=0) imsg("Socket read from reader[%d] %d bytes e=%d\n",c,r,errno);
	if (r<=0) {
		if (nntp_errbusy() ) {
			return;
		}

		if (r!=0) nntp_perror("Socket closed on read from reader");
		else imsg("Socket closed normally [%d] \n",c);
		nntp_doclose(c); 
		return;
	}
	errno = 0;
	

	if (!isbinary) zemsg("In  %d {%s}\n",r,bf);
	else log_bin("In:",cvt_bin_string(bf,r));
	
	w = socket_write(chan_out[c],bf,r);
	if (isblock) {printf("Got %d bytes",r); gets(zzzz);}
       	nntp_lasterror();
	if (w<r) imsg("Socket write to news [%d] %d bytes e=%d\n",c,w,errno);
	if (w<r) {
		if (nntp_busywrite()) {
			imsg("Dnews [%d] busy bufferring %d bytes\n",c,r-w);
			if (chan_data1[c]!=NULL) emsg("Channel already busy, REPORT THIS BUG [%d]\n",c);
			chan_bsy1[c] = TRUE;
			chan_data1[c] = malloc(BIGSZ+1);
			if (chan_data1[c]==NULL) {emsg("Memory allocation failed\n"); exit(1);}
			if (w<0) w = 0;
			memcpy(chan_data1[c],bf+w,r-w);
			chan_data1_len[c] = r-w;
			return;
		}
		nntp_perror("Socket write to dnews failed - closing");
		nntp_doclose(c); 
	}
}
void nntp_readwrite2(int c)
{
	int r,w;
	char bf[BIGSZBIN];

	errno = 0;
	r =  socket_read(chan_out[c],bf,BIGSZ);
	if (isblock) {printf("out %d bytes",r); gets(zzzz);}
       	nntp_lasterror();
	if (r<=0) imsg("Socket read from news[%d] %d bytes e=%d\n",c,r,errno);
	if (r<=0) {
		if (nntp_errbusy() ) {
			return;
		}
		if (r!=0) nntp_perror("Socket closed on read from dnews");
		else imsg("Socket closed normally [%d] \n",c);
		nntp_doclose(c); 
		return;
	}
	errno = 0;
	if (!isbinary) zemsg("Out  %d {%s}\n",r,bf);
	else log_bin("Out:",cvt_bin_string(bf,r));

	w = socket_write(chan[c],bf,r);
       	nntp_lasterror();
	if (w<r) imsg("Socket write to reader [%d] %d bytes e=%d\n",c,w,errno);
	if (w<r) {
		if (nntp_busywrite()) {
			imsg("To Reader [%d] bufferring %d bytes\n",c,r-w);
			if (chan_data2[c]!=NULL) emsg("Channel already busy, REPORT THIS BUG [%d]\n",c);
			chan_bsy2[c] = TRUE;
			chan_data2[c] = malloc(BIGSZ+1);
			if (chan_data2[c]==NULL) {emsg("Memory allocation failed\n"); exit(1);}
			if (w<0) w = 0;
			memcpy(chan_data2[c],bf+w,r-w);
			chan_data2_len[c] = r-w;
			return;
		}
		nntp_perror("Socket write to reader failed - closing");
		nntp_doclose(c); 
	}
}
void nntp_writerest1(int c)
{
	int n,w;
	errno = 0;
	n = chan_data1_len[c];
	zemsg("x1  %d {%s}\n",n,chan_data1[c]);
	w = socket_write(chan_out[c],chan_data1[c],n);
       	nntp_lasterror();
	if (w<n) {
	  if (nntp_busywrite()) {
		dmsg("Dnews [%d] still busy bufferring %d bytes\n",c,n-w);
		if (w<0) w = 0;
		memmove(chan_data1[c],chan_data1[c]+w,n-w);
		chan_data1_len[c] = n-w;
		return;
	  }
	  imsg("writerest to dnews [%d] failed\n",c);
	  nntp_perror("Socket writerest to dnews failed - closing");
	  nntp_doclose(c); 
	  return;
	} 
	chan_bsy1[c] = FALSE;
	free(chan_data1[c]);
	chan_data1[c] = NULL;
}
void nntp_writerest2(int c)
{
	int n,w;
	errno = 0;
	n = chan_data2_len[c];
	zemsg("x2  %d {%s}\n",n,chan_data2[c]);
	w = socket_write(chan[c],chan_data2[c],n);
       	nntp_lasterror();
	if (w<n) {
	  if (nntp_busywrite()) {
		dmsg("Reader [%d] still busy bufferring %d bytes\n",c,n-w);
		if (w<0) w = 0;
		memmove(chan_data2[c],chan_data2[c]+w,n-w);
		chan_data2_len[c] = n-w;
		return;
	  }
	  imsg("writerest to reader [%d] failed\n",c);
	  nntp_perror("Socket writerest to reader failed - closing");
	  nntp_doclose(c); 
	  return;
	}
	chan_bsy2[c] = FALSE;
	free(chan_data2[c]);
	chan_data2[c] = NULL;
}

char *nntp_hostname(void)
{
   static char host[200];
   if (gethostname(host,190)!=0) emsg("nntp: Can't get host name\n");
   return host;
}

#ifdef OS2
#define NEED_DTABLE TRUE
#endif

#ifdef WIN_SOCK
#define NEED_DTABLE TRUE
#endif

#ifdef USE_RLIMIT
#include <sys/resource.h>
int getdtablesize(void)
{
        static int min;
        struct rlimit lim;
	static int max;
        if (min>0) return min;

        getrlimit(RLIMIT_NOFILE,&lim);
	max = lim.rlim_max;
	if (max>8000) max = 8000;
#ifdef FD_SETSIZE
	if (max>FD_SETSIZE) max = FD_SETSIZE;
#endif
	lim.rlim_cur = max;
	if (setrlimit(RLIMIT_NOFILE,&lim)!=0) filemsg("Set rlimit failed\n");

        min = 5000;
#ifdef FD_SETSIZE
        imsg("nntp: FD_SETSIZE %d, rlim %d\n",FD_SETSIZE,lim.rlim_cur);
        if (min>FD_SETSIZE) min = FD_SETSIZE;
#endif
        if (min>lim.rlim_cur) min = lim.rlim_cur;
        return min;
}
#endif

#ifdef NEED_DTABLE
int getdtablesize(void)
{
#ifdef WIN_SOCK
        return FD_SETSIZE;
#else
#ifdef unix
#ifdef FD_SETSIZE
        return FD_SETSIZE;
#else
        return 64;
#endif
#endif
#endif
#ifdef VMS
#ifdef MULTINET
	return 32;
#else
        return FD_SETSIZE;
#endif
#endif
}
#endif

void wsock_error(char *s);
void nntp_perror(char *s)
{
#ifdef OS2
	psock_errno(s);
#else
#ifdef WIN_SOCK
        wsock_error(s);
#else
#ifdef unix
        emsg("nntp_perror: %s %s\n",strerror(errno),s);
#else
        emsg("nntp_perror: %s %s \n",strerror(errno),s);
        /*
	socket_perror(s);
	*/
#endif
#endif
#endif
}

#ifdef WIN_SOCK
void wsock_error(char *s)
{
        int err = werrno;
        if (err==WSANOTINITIALISED) emsg("wsock: (%s) A successful WSAStartup must occur before using this API.\n",s);
        else if (err==WSAENETDOWN)      emsg("wsock: (%s) The Windows Sockets implementation has detected that the network subsystem has failed.\n",s);
        else if (err==WSAEACCES)        emsg("wsock: (%s) The requested address is a broadcast address, but the appropriate flag was not set.\n",s);
        else if (err==WSAEINTR) emsg("wsock: (%s) The (blocking) call was cancelled via WSACancelBlockingCall.\n",s);
        else if (err==WSAEINPROGRESS)   emsg("wsock: (%s) A blocking Windows Sockets operation is in progress.\n",s);
        else if (err==WSAEFAULT)        emsg("wsock: (%s) The buf argument is not in a valid part of the user address space.\n",s);
        else if (err==WSAENETRESET)     emsg("wsock: (%s) The connection must be reset because the Windows Sockets implementation dropped it.\n",s);
        else if (err==WSAENOBUFS)       emsg("wsock: (%s) The Windows Sockets implementation reports a buffer deadlock.\n",s);
        else if (err==WSAENOTCONN)      emsg("wsock: (%s) The socket is not connected.\n",s);
        else if (err==WSAENOTSOCK)      emsg("wsock: (%s) The descriptor is not a socket.\n",s);
        else if (err==WSAEOPNOTSUPP)    emsg("wsock: (%s) MSG_OOB was specified, but the socket is not of type SOCK_STREAM.\n",s);
        else if (err==WSAESHUTDOWN)     emsg("wsock: (%s) The socket has been shutdown; it is not possible to send on a socket after shutdown has been invoked with how set to 1 or 2.\n",s);
        else if (err==WSAEWOULDBLOCK)   emsg("wsock: (%s) The socket is marked as non-blocking and the requested operation would block.\n",s);
        else if (err==WSAEMSGSIZE)      emsg("wsock: (%s) The socket is of type SOCK_DGRAM, and the datagram is larger than the maximum supported by the Windows Sockets implementation.\n",s);
        else if (err==WSAEINVAL)        emsg("wsock: (%s) The socket has not been bound with bind.\n",s);
        else if (err==WSAECONNABORTED)  emsg("wsock: (%s) The virtual circuit was aborted due to timeout or other failure.\n",s);
        else if (err==WSAECONNRESET)    emsg("wsock: (%s) The virtual circuit was reset by the remote side.\n",s);
        else if (err==WSAETOOMANYREFS)  emsg("wsock: (%s) Too many refs.\n",s);
        else if (err==WSAETIMEDOUT)     emsg("wsock: (%s) Timedout\n",s);
        else if (err==WSAECONNREFUSED)  emsg("wsock: (%s) Connection Refused\n",s);
        else if (err==WSAEDESTADDRREQ)  emsg("wsock: (%s) DestAddrReq\n",s);
        else if (err==WSAEMSGSIZE)      emsg("wsock: (%s) Message Size\n",s);
        else if (err==WSAEADDRINUSE)    emsg("wsock: (%s) Address in use\n",s);
        else if (err==WSAEADDRNOTAVAIL) emsg("wsock: (%s) Address not avail\n",s);
        else if (err==WSAENETUNREACH)   emsg("wsock: (%s) Network Unreachable\n",s);
        else if (err==WSAENETRESET)     emsg("wsock: (%s) Network Reset\n",s);
        else if (err==WSAENOBUFS)       emsg("wsock: (%s) No bufs\n",s);
        else if (err==WSAESHUTDOWN)     emsg("wsock: (%s) Shutdown\n",s);
        else if (err==WSAENAMETOOLONG)  emsg("wsock: (%s) Name too long\n",s);
        else if (err==WSAEHOSTDOWN)     emsg("wsock: (%s) Host down\n",s);
        else if (err==WSAEHOSTUNREACH)  emsg("wsock: (%s) Host Unreachable\n",s);
        else if (err==WSAENOTEMPTY)     emsg("wsock: (%s) Not Empty\n",s);
        else emsg("wsock: (%s) Socket Unknown error code %d \n",s,err);
}
#endif

#ifdef WIN_SOCK
int nntp_errbusy(void)
{
        int err = werrno;

	if (err==WSAEWOULDBLOCK) {
                return TRUE;
        }
	return FALSE;
}
#else
int nntp_errbusy(void)
{
        if (werrno==EWOULDBLOCK) {
                dmsg("socket: EWOULDBLOCK, The socket is marked as non-blocking and the requested operation would block.\n");
                return TRUE;
        }
        if (werrno==EINPROGRESS) {
                dmsg("socket: EINPROGRESS The open is pending \n");
                return TRUE;
        }
        return FALSE;
}
#endif



#ifdef WIN_SOCK
int nntp_busywrite(void)
{
        int err = werrno;

	if (err==WSAEWOULDBLOCK) {
                return TRUE;
        }
	return FALSE;
}
#else
int nntp_busywrite(void)
{
        if (werrno==EAGAIN) {
                dmsg("socket: EAGAIN, Resource temporarily unavailable\n");
                return TRUE;
        }
        if (werrno==EWOULDBLOCK) {
                dmsg("socket: EWOULDBLOCK, The socket is marked as non-blocking and the requested operation would block.\n");
                return TRUE;
        }
        if (werrno==EINPROGRESS) {
                dmsg("socket: EINPROGRESS The open is pending \n");
                return TRUE;
        }
	if (werrno==0) return TRUE;
        return FALSE;
}
#endif


int nntp_errclosed(void)
{
#ifdef badidea
#ifdef WIN32
        int err = werrno;
	if (err==WSAECONNABORTED) return TRUE;
	if (err==WSAECONNRESET) return TRUE;
	if (err==WSAESHUTDOWN) return TRUE;
#else
#ifdef OS2
	if (werrno==SOCEPIPE) return TRUE;
#else
	if (werrno==EPIPE) return TRUE;
#endif
#endif	
#endif
	return FALSE;
}



/*
struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};
*/

/* Forward and reverse name lookup cache */


#define DSIZE 400  /* Increase to 500 when testing done*/
static struct sockaddr_in dns_sin[DSIZE];
static char *dns_name[DSIZE];
static long dns_time[DSIZE];
static int dns_ok[DSIZE];
static int ndns,dns_next;

void dns_clear(void)
{
        int i;
        for (i=0; i<ndns; i++) {
                if (dns_name[i]!=NULL) strcpy(dns_name[i],"");
                dns_sin[i].sin_addr.s_addr = 0;
        }
}
int dns_ageok(int i, int isreverse)
{
        int retry;
        int age = lib_gmtime()-dns_time[i];
        retry = 1;
        if (retry==0) retry = 60;
        if (retry<=0) retry = 1;

        if (dns_ok[i]) {
                if (age<24*60*60*7) return TRUE;  /* Less than a week old */
        } else {
                if (isreverse) {
                        if (age<60*60*12) return TRUE;  /* Less than 1/2 days old, reverse  */
                } else {
                        if (age<60*retry) return TRUE;  /* Less than 60 minutes old open */
                }
        }
        return FALSE;
}
int dns_add(char *name, struct sockaddr_in *sin, int isok)
{
        int n;

        if (strlen(name)!=0) n = dns_find_name(name);
        else n = dns_find_addr(sin);
        if (n<0) {
                if (ndns<DSIZE) {
                        n = ndns++;
                } else {
                        n = dns_next++;
                        if (dns_next>=DSIZE) dns_next = 0;
                }
        }
        if (dns_name[n]==NULL) dns_name[n] = mymalloc(100);
        ncpy(dns_name[n],name,99);
        dns_sin[n].sin_addr.s_addr = sin->sin_addr.s_addr;
        dns_sin[n].sin_family = sin->sin_family;
        dns_ok[n] = isok;
        dns_time[n] = lib_gmtime();
        return 0;
}
int dns_find_name(char *name)
{
        int i;
        for (i=0; i<ndns; i++) {
                if (dns_name[i]!=NULL) if (strcmp(name,dns_name[i])==0) {
                        return i;
                }
        }
        return -1;
}
int dns_find_addr(struct sockaddr_in *sin)
{
        int i;
        for (i=0; i<ndns; i++) {
                if (dns_sin[i].sin_addr.s_addr == sin->sin_addr.s_addr) {
                        return i;
                }
        }
        return -1;
}
int nntp_getaddr(char *name, struct sockaddr_in *sin)
{
	static int badloop;
        struct hostent *hp;
        int i;
	badloop = 0;

	if (name==NULL) {emsg("nntp_getaddr: Can't lookup NULL string\n"); return FALSE;}
	if (strlen(name)==0) {emsg("nntp_getaddr: Can't lookup empty string\n"); return FALSE;}
again:
	badloop++;
	if (badloop>4) {
	 	emsg("nntp_getaddr() failed for name {%s} \n",name);
		return FALSE;
	}
        i = dns_find_name(name);
        if (i>=0) if (dns_ageok(i,FALSE)) {
                sin->sin_addr.s_addr = dns_sin[i].sin_addr.s_addr;
                sin->sin_family = dns_sin[i].sin_family;
                return dns_ok[i];
        }

        if (isdigit(*name)) {
                sin->sin_addr.s_addr = inet_addr(name);
                sin->sin_family = AF_INET;
                dns_add(name,sin,TRUE);
                goto again;
        } else {
                sin->sin_addr.s_addr = 0;
		imsgf("nntp: Doing forward DNS lookup\n");
                hp = gethostbyname(name);
		imsgf("nntp: forward DNS completed\n");
                if (hp==NULL) {dns_add(name,sin,FALSE); goto again; }
                memcpy(&(sin->sin_addr),hp->h_addr,  hp->h_length);
                sin->sin_family = hp->h_addrtype;
                dns_add(name,sin,TRUE);
                goto again;
        }

}
int nntp_getname(char *name, struct sockaddr_in *sin)
{
	return FALSE;
}

void nntp_lasterror(void)
{
	werrno = errno;
#ifdef WIN32
	werrno = WSAGetLastError();
#endif
#ifdef OS2
	werrno = sock_errno();
#endif	
}
/*-------------- dmulti, multi processor version of dnews --------*/
void start_servers(void);
void config_read(void);
void sig_init(void);
void dnews_shutdown(void);
void exit_handler(void);
char *main_msg(void)
{
	return "DMULTI successfully running!";
}		
static int doexit=FALSE;
void suck_setexit(void)
{
	doexit = TRUE;
}
static int isrestart;
int main(int argc, char *argv[])
{
	int i;
	int pchan = -1;
	emsg_open();
#ifdef USEONEXIT
        on_exit(exit_handler); 
#else
        atexit(exit_handler); 
#endif

	strcpy(lcl_port,"80");
	if (argc==1) {printf("dspy -port 119 -to x.y.z -binary\n"); return 0;}
        for (i=1; i<argc; i++) {
           if (strncmp(argv[i],"-restart",4)==0)  {
		isrestart = TRUE;
           }
           if (strncmp(argv[i],"-to",2)==0)  {
                   ncpy(real_host,argv[++i],BFSZ-1);
           }
           if (strncmp(argv[i],"-bin",4)==0)  {
		   isbinary = TRUE;
           }
           if (strncmp(argv[i],"-block",6)==0)  {
		isblock = TRUE;
           }
           if (strncmp(argv[i],"-port",2)==0)  {
                   ncpy(lcl_port,argv[++i],BFSZ-1);
           }
           if (strncmp(argv[i],"-nolog",6)==0)  {
		   nolog = TRUE;
           }
        }
	imsg("DMULTI, Version %s Starting\n",DNEWS_VERSION);
	for (i=0; i<argc; i++) imsg("argv[%d] = %s\n",i,argv[i]);
	sig_init();
	nntp_startup();
	nntp_init(pchan);
	/* start n dnews processes */
	nntp_wait();
	return 0;
}
static int nslaves;
static int slave_port;
FILE *slave_pipe[300];
FILE *myspawn(char *cmd, char *mde);
void myspawnx(FILE *x);
#ifdef WIN32
#define popen myspawn
#define pclose myspawnx
#endif
static int hmaster = -1;
char *dnews_bin(void);
void exit_handler(void)
{
	dmulti_exit("Exit handler");
}
void dmulti_exit(char *s)
{
	static int done;
	int i;
	if (done) return;

	done = TRUE;
	i = 0;
	emsg("Exit requested (%s)\n",s);
#ifdef WIN32
#else
	for (i=0; i<nslaves; i++) {
		if (slave_pipe[i]!=NULL) pclose(slave_pipe[i]);
		slave_pipe[i] = NULL;
	}
#endif
	exit(1);
}

int match_wild(char *s, char *ip);
static SOCKET msock = INVALID_SOCKET;
static int ntried;
void m_idle(void)
{
	int r;
	int n;
	char bf[BFSZ];
	static int start;

	if (doexit) {
		dnews_shutdown();
		dmulti_exit("Service shutdown requested");
	}
	if (start==0) start = lib_gmtime();
	if ((lib_gmtime() % 20) == 0) emsg_flush();
}
void dnews_shutdown(void)
{
	char bf[BFSZ];
	if (msock==INVALID_SOCKET) return;
}
char *ncpy(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}
int lib_gmtime(void)
{
        time_t t;
        time(&t);
        return (int) t;
}


static char output[40000];
int emsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;

	if (nolog) return;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        emsg_do(output);
}
void dmsg(char * arg_list, ...)
{
}
int imsg(const char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	if (nolog) return;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        emsg_do(output);
	return 1;
}
static FILE *flog;
int log_bin(char *head, char *msg)
{
	if (nolog) return 1;
	if (flog==NULL) return 1;
	fprintf(flog,"%s (%s)\n",head,msg);
	fflush(flog);
	return 1;
}

void emsg_do(char *in)
{
        time_t t;
        char tstr[30],bf[35000];
	char *ccc;
	struct tm *tt; 
	static int nlines;

	if (nolog) return;
	nlines++;
	if (nlines>7000) {
		nlines = 0;
		fclose(flog);
		emsg_open();
	}
		
        t = time(NULL);
        tt = localtime(&t);
	ccc = ctime(&t); if (ccc==NULL) return;
        strcpy(tstr,ccc);
        tstr[19] = 0;
        sprintf(bf,"%s: %s",tstr+8,in);
	fprintf(flog,"%s",bf);
	fflush(flog);
}
void emsg_flush(void)
{
	fflush(flog);
}
void emsg_open(void)
{
	char bf[BFSZ]; 
	sprintf(bf,"dspy.log");
	flog = fopen(bf,"a");
	if (flog==NULL) {
		printf("File %s\n",flog);
		perror("Couldn't open output log file");
		exit(1);
	}
}
void init_sig(void)
{
        sig_init();
}

#ifdef NETWARE
#define MISSING_SIGNALS TRUE
#endif
#ifdef WIN32
#define MISSING_SIGNALS TRUE
#endif
#ifdef Mac
#define MISSING_SIGNALS TRUE
#endif
#ifdef OS2
#define MISSING_SIGNALS TRUE
#endif

void sig_hup(int x);
void sig_exit(int x);
void sig_init(void)
{
#ifndef MISSING_SIGNALS
        signal(SIGHUP,sig_hup);
        signal(SIGTERM,sig_exit);
#endif
#ifdef unix
#ifndef MISSING_SIGNALS
        signal(SIGPIPE,SIG_IGN);
        signal(SIGTRAP,sig_exit);
        /* signal(SIGBUS,sig_exit);*/
        signal(SIGQUIT,sig_exit);
#endif
       	signal(SIGSEGV,sig_exit);
        signal(SIGFPE,sig_exit);
#endif
}
void sig_hup(int x)
{
        emsg("dnews: Signal %d SIGHUP \n",x);
        sig_init();
}
void sig_exit(int x)
{
   emsg("dnews: Signal %d causing exit \n",x); 
   dmulti_exit("Signal");	
}
char *mystrupr(char *s)
{
        char *ss=s;
        for (;*s!=0;s++) if (islower(*s)) *s = _toupper(*s);
        return ss;
}
char *mystrlwr(char *s)
{
        char *ss=s;
        for (;*s!=0;s++) if (isupper(*s)) if (*s>0) *s = _tolower(*s);
        return ss;
}
#ifdef NEED_MEMMOVE
void *memmove(char *dst, char *src, int len)
{
        bcopy(src,dst,len);
        return (void *) dst;
}
#endif

int isasc(char *end)
{
 if (*end >= ' ' && *end <= '~' && 
  *end != '<' && *end != '>')
  return 1;
 return 0;
}

char *cvt_bin_string(unsigned char *bin_string, int size)
{       
	static char out[100000];
	int outlen=90000;
	char *xout=out;
	int i;
	char c;
	sprintf(xout,"(GOT %d BYTES)",size);
	xout += strlen(xout);
	for (i=0; i<size; i++) {
		c = bin_string[i];
		if (isprint(c)) *xout++ = c;
		else {
			sprintf(xout,"<%d>",c);
			xout += strlen(xout);
		}
		if (xout-out > (outlen-19)) break;
	}
	*xout++ = 0;
	return out;	
}