/*
When welcome message arrives:
	If it's a 200..
	Then send 'xdrealip', and swallow original welcome. 
	In dnews, re-send 200 stuff after xdrealip

Re-write to use pipes.

Use a different socket instead of msock !!!!
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
#include <i86.h>
#include <dos.h>
#endif


#ifdef WIN32
#define WIN_SOCK TRUE
#endif

#ifdef INCREASE_FD
#define FD_SETSIZE 2048
#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 <netinet/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 <process.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

#ifndef WIN32
#include <unistd.h>
#include <fcntl.h>
#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>
#ifndef OS2
#include <sys/wait.h>
#endif
#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





#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 int slave_port;
int socket_low_free(SOCKET xsk);

static SOCKET l_chan[100];
static int l_port[100];
static int nlisten;


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];
static int xisfeed;
int nchan;
static  struct sockaddr_in xsin;
static  struct servent *sp,fakesp;
static int istesting;
 
char *conf_getpass(void);
void nntp_setlocalip(int c,char *s);
char *nntp_localip(int c);

void slave_check(void);
void close_on_exec(int fd);
void nntp_newchan_sk(SOCKET sk);
SOCKET nntp_listen(int port);

void check_exit(void);
void dmsg(char * arg_list, ...);
int nntp_getports(int c, int *local, int *remote);
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);
int con_count(char *s);


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

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);
void dbug(char * arg_list, ...);
static int conlimit;
char *file_work(char *s);
static SOCKET chan_slave[100];
void drain_msock(void);
void drain_slave(int i);
int give_socket(int c, int slavei);
static int ischild;

#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
static SOCKET msock = INVALID_SOCKET;

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


#define BIGSZ 64000
static int bigsz = BIGSZ;
#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);
char *file_config(char *s);
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 *isfeed);
int emsg(char * arg_list, ...);
int imsg(const char * arg_list, ...);
void emsg_do(char *in);
void emsg_flush(void);
void emsg_open(void);
int lib_sleep(int x);
void do_restart(void);
#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
        ,C_SLAVE_DEBUG,C_CONCURRENT_LIMIT
        ,C_LOGDIR
        ,C_OUT_SIZE
        ,C_BIND_IN
	,C_SLAVE_ROUTE
	,C_LISTEN_PORTS
	,C_MASTER_RESTART
	,C_STARTUP_DELAY
};		
static char *conf_name[]={
        "TCP_NODELAY","PORT","OPEN_RETRY"
        , "SLAVE_PORTS", "SLAVE_N", "MYNAME", "CONFIG"
        , "SLAVE_FEED", "WORKAREA", "SLAVE_RESTART"
        ,"SLAVE_DEBUG","CONCURRENT_LIMIT"
        ,"LOGDIR"
        ,"OUT_SIZE"
        ,"BIND_IN"
	,"SLAVE_ROUTE"
	,"LISTEN_PORTS"
	,"MASTER_RESTART"
	,"STARTUP_DELAY"
, NULL};

#ifdef HAS_TABLESIZE
#define getdtablesize xgetdtablesize
#endif

#ifdef WIN32
static int cangive=TRUE;
#else
static int cangive=FALSE;
#endif

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)
{
dbug("Block Set channel [%d] to %d\n",c,t);
        nntp_setblockraw(chan[c],t);
}
#ifdef WIN32
#define HAS_NODELAY TRUE
#else 
#ifdef TCP_NODELAY
#define HAS_NODELAY TRUE
#endif
#endif
void nntp_setblockraw(SOCKET c, int t)
{
#ifdef SET_LINGER
        struct linger ling;
#endif
        int on;
        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
dbug("Block Set channel =%d to %d\n",c,t);
        if (socket_ioctl(c,FIONBIO,&w)!=0) filemsg("setblock failed\n");
#endif
#endif

#ifdef SET_LINGER
        ling.l_onoff = 1;
        ling.l_linger = 2000; 
        setsockopt(c, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));   
#endif
        on = 0;
#ifdef HAS_NODELAY
        setsockopt(c, IPPROTO_TCP, TCP_NODELAY,(char *) &on, sizeof(int));
#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
        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


#ifndef WIN32
int lib_usleep(int x)
{
#ifndef NONANO
	struct timespec rqtp;
	rqtp.tv_sec = x/1000000;
	rqtp.tv_nsec = (x%1000000)*1000;
	nanosleep(&rqtp,NULL);
#else
	usleep(x);
#endif
}
#endif
#ifdef WIN32
int lib_usleep(int x)
{
	Sleep(x/1000);
	return TRUE;
}
#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_listen_ports(void)
{
	char *s;
	char ports[BFSZ];
	ncpy(ports,config_get(C_LISTEN_PORTS),200);
	if (strlen(ports)==0) return;
	s = strtok(ports,", \t");
	for (;s!=NULL;) {
		l_port[nlisten] = atoi(s);
		imsg("Attempting to listen on extra port %d\n",l_port[nlisten]);
		l_chan[nlisten] = nntp_listen(l_port[nlisten]);
		if (l_chan[nlisten]!=INVALID_SOCKET) nlisten++; 
		s = strtok(NULL,", \t");
	}	
}
SOCKET nntp_listen(int port)
{
        int on=1;
        int r;
        int quelen;
	SOCKET sk;

	sk = socket(AF_INET, SOCK_STREAM, 0);
        nntp_lasterror();               
        if (sk == INVALID_SOCKET) {  
		nntp_perror("nntp: Can't listen on extra port");
		return INVALID_SOCKET;
	}
        sp = &fakesp;
        fakesp.s_port = htons(port);
        r = setsockopt(sk, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));
        nntp_lasterror();               
        if (r < 0) {
		filemsg("nntp: port: Can't setsockopt  %d \n",r);
                nntp_perror("tcpnntpserver: setsockopt");
                return INVALID_SOCKET;
	}
	xsin.sin_family = AF_INET;
        xsin.sin_addr.s_addr = INADDR_ANY;
        xsin.sin_port = sp->s_port;
#ifdef GOT_SOCKADDR
        if (bind(sk,(struct sockaddr *)  &xsin, sizeof (xsin)) < 0) {
#else
        if (bind(sk,&xsin, sizeof (xsin)) < 0) {
#endif
		nntp_lasterror();               
                filemsg("nntp: Can't bind \n");
                nntp_perror("tcpnntpserver: bind");
		socket_close(sk);
                return INVALID_SOCKET;
        }
        
        nntp_setblockraw(sk,FALSE);
        close_on_exec(sk);
        quelen = 25;
        if (listen(sk, quelen) < 0) {
                nntp_lasterror();               
                filemsg("nntp: Can't set listen for port %d\n",port);
                nntp_perror("tcpnntpserver: listen");
		socket_close(sk);
		return INVALID_SOCKET;
        }
	return sk;
}

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);
                }

                sp = getservbyname("nntp", "tcp");
                if (sp == NULL) {
                        emsg("nntp: Can't getservbyname \n");
                        sp = &fakesp;
                        fakesp.s_port = htons(119);
                }

                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;
                s = config_get(C_PORT);
                if (strlen(s)!=0) imsg("nntp: port selected %s \n",s);
                if (strlen(s)!=0) xsin.sin_port = htons((unsigned short) atoi(s));
                if (xsin.sin_port == 0) xsin.sin_port = htons(119);
		if (istesting) {
			xsin.sin_port = htons(3119);
		}
		
#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);
        close_on_exec(x_listen);
        
        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;
#ifdef OS2
void usleep(int x)
{
        delay(x/1000);
}
#endif
#ifdef WIN32 
void usleep(int x)
{
        Sleep(x/1000);
}
#endif
int mygetdtablesize(void)
{
#ifdef LINUX
	int r = getdtablesize();
	if (r>1024) r = 1024;
	return r;
#else
	return getdtablesize();
#endif
	
}
void nntp_wait(void)
{
        static int last_give;
        int i,nc;
	int snooze;
        static int xlast;
        fd_set readfd,writefd,exceptfd,writecopy;
        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)) {
                if (chan_bsy2[i]) FD_SET(chan[i],&writefd);
                FD_SET(chan[i],&exceptfd);
                if (chan_bsy1[i]) 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);
	for (i=0; i<nlisten; i++) { /* listening on other ports */
		FD_SET(l_chan[i],&readfd);
	}
        nc = select(mygetdtablesize(), &readfd, &writefd, &exceptfd, &timecopy);
        memcpy(&writecopy,&writefd,sizeof(writefd));

        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);
                if (!chan_bsy2[i]) if (FD_ISSET(chan[i],&writefd)) {
                        FD_SET(chan_out[i],&readfd);
/*                      dbug("selreadout %d\n",i);      */
                }
                if (!chan_bsy1[i]) if (FD_ISSET(chan_out[i],&writefd)) {
                        FD_SET(chan[i],&readfd);
/*                      dbug("selread %d\n",i); */
                }
        }
        if (x_listen!=INVALID_SOCKET) FD_SET(x_listen,&readfd);
        FD_ZERO(&writefd);
        dbug("Select...\n");
	for (i=0; i<nlisten; i++) { /* listening on other ports */
		FD_SET(l_chan[i],&readfd);
	}
        nc = select(mygetdtablesize(), &readfd, &writefd, &exceptfd, &timecopy);
#ifndef NOUSLEEP
	snooze++;
	if (snooze % 110 ==4) lib_usleep(1000); 
#endif
        dbug("Select ready %d\n",nc);
        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]) if (FD_ISSET(chan_out[i],&writecopy)) {
                        nntp_writerest1(i);
                }
                if (chan[i]==INVALID_SOCKET) continue;
                if (chan_out[i]==INVALID_SOCKET) continue;
                if (chan_bsy2[i]) if (FD_ISSET(chan[i],&writecopy))     {
                        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 (x_listen!=INVALID_SOCKET) if (FD_ISSET(x_listen,&readfd)) nntp_newchan();

	for (i=0; i<nlisten; i++) { /* listening on other ports */
		if (FD_ISSET(l_chan[i],&readfd)) nntp_newchan_sk(l_chan[i]);
	}

        if (xlast!=lib_gmtime()) {
                xlast = lib_gmtime();
                m_idle();
        }
        goto again;
}
int tcp_waitfor(SOCKET sk, int msec)
{
        fd_set readfd,writefd,exceptfd;
        int nc;
        struct timeval timeout;

        
        timeout.tv_sec = msec/1000; /*Wake up once a second*/
        timeout.tv_usec = (msec%1000) * 1000 ;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
        FD_SET(sk,&readfd);             
        nc = select(mygetdtablesize(),&readfd,&writefd,&exceptfd,&timeout);
        if (nc>0) return TRUE;
        return FALSE;
}
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(mygetdtablesize(), &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 con_count(char *s)
{
        int i;
        int t=0;
        for (i=0; i<nchan; i++) {
                if (strcmp(chan_ipnum[i],s)==0) t++;
        }
        return t;
}
int nntp_nused(void)
{
        /* should search thru list for unallocated one.*/
        int i;
        int n=0;
        for (i=0; i<nchan ; i++) {
                if ((chan[i]!=INVALID_SOCKET)) n++;
                if ((chan_out[i]!=INVALID_SOCKET)) n++;
        }
        return n;
}
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_low_free(chan[c]);
                socket_close(chan[c]);  
        }
        if (chan_out[c]!=INVALID_SOCKET) {
		socket_low_free(chan_out[c]);
                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

/*
     xdrealip ip.address lcl.ip.addres tellnes_pass 

	
*/

#ifdef SAVE_LOW
static char low_used[2000];
static int nlow;
SOCKET socket_low(SOCKET sk)
{
	int i;
	if (sk==INVALID_SOCKET) return sk;
	if (sk>3000) return sk;
	for (i=300; i<2000; i++) {
		if ((i>=nlow) || (!low_used[i])) {
			low_used[i] = TRUE;
			if (nlow<=i) nlow = i+1;
			if (dup2(sk,i) == -1) {
				filemsg("dup2 failed %s\n");
				low_used[i] = FALSE;
				socket_close(sk);
				return INVALID_SOCKET;
			}
			socket_close(sk);
			return (SOCKET) i;
		}
	}
	emsg("No handles free for socket_low\n");
	abort();
}
#endif
int socket_low_free(SOCKET xsk)
{
#ifdef SAVE_LOW
	int sk = (int) xsk;
	if (sk<0) return 0;
	if (sk>=2000) return 0;
	low_used[sk] = FALSE;
#else
	return xsk;
#endif
}
void nntp_setrcvbf(SOCKET c, int sz)
{
	int xsz=sz;
	int was;
	int xlen=sizeof(xsz);
	char *s;
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_RCVBUF, s, &xlen);   
	imsg("receive buffer was %d\n",was);
	if (was>=sz) return;
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_RCVBUF, s, sizeof(xsz));   
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_RCVBUF, s, &xlen);   
	imsg("receive buffer is now  %d\n",was);

}

void nntp_setsndbf(SOCKET c, int sz)
{
	int xsz=sz;
	int was;
	int xlen=sizeof(xsz);
	char *s;
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);   
	imsg("send buffer was %d\n",was);
	if (was>=sz) return;
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_SNDBUF, s, sizeof(xsz));   
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);   
	imsg("send buffer is now %d\n",was);

}


void nntp_newchan(void)
{
	nntp_newchan_sk(x_listen);
}
void nntp_newchan_sk(SOCKET sk)
{
        int n=nntp_getchan();
        int sizesin=sizeof(chan_sin[0]);
        char ipnum[200];
        char xhost[BFSZ];
        char bf[BFSZ];
        char buf[BFSZ];
	int isfeed;
        strcpy(bf,"");

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

        
        if (chan[n] == INVALID_SOCKET) {
          nntp_perror("Socket newchan accept() call failed");
          filemsg("Socket newchan accept() call failed\n");
          return;
        }

        close_on_exec((int) chan[n]);

        sent_ip[n] = FALSE;
        strcpy(ipnum,inet_ntoa(chan_sin[n].sin_addr));
        strcpy(chan_ipnum[n],ipnum);
        strcpy(xhost,choose_server(ipnum,&isfeed));

	if (cangive) {
		imsg("Giving connection from %s to s%d\n",ipnum,isfeed);
		give_socket(n,isfeed);
		socket_close(chan[n]);
		socket_low_free(chan[n]);
		chan[n] = INVALID_SOCKET;
		return;
	} 

        if (n>1950) {
                emsg("Too many connections - disconnecting\n");
                nntp_doclose(n);
                return; 
        }
	if (xisfeed) {
		nntp_setrcvbf(chan[n], 32000);
	}
        if (!xisfeed) if (conlimit>0) if (con_count(ipnum)>conlimit) {
                sprintf(buf,"502 Sorry you have exceeded the limit of %d connections, Goodbye\015\012",conlimit);
                emsg("Rejecting user (%s) as he has %d connections, limit=%d\n",ipnum,con_count(ipnum),conlimit);
                socket_write(chan[n], buf, strlen(buf));
                nntp_doclose(n);
                return;
        }
	imsg("Open connection to (%s)\n",xhost);
        nntp_open(xhost,n,FALSE);
        if (chan_out[n]==INVALID_SOCKET) {
                nntp_doclose(n);
                return;
        }
#ifdef SAVE_LOW
	chan_out[n] = socket_low(chan_out[n]);
#endif

        close_on_exec((int) chan_out[n]);
        imsg("Connection from %s %d h=%d h2=%d\n",ipnum,n,chan[n],chan_out[n]);
        nntp_setblock(n,FALSE);
	if (xisfeed) {
		nntp_setsndbf(chan_out[n], 65535);
	}

	nntp_setsndbf(chan[n], 65535);
	nntp_setrcvbf(chan[n], 65535);
	nntp_setrcvbf(chan_out[n], 65535);
	nntp_setsndbf(chan_out[n], 65535);
}

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 */
	imsg("Open link to %s port %d\n",dst_name,dst_port);
        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;
        }
        close_on_exec((int) chan_out[n]);
        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()) {
			imsg("Connect failed to %s %d\n",dst_name,dst_port);
                        nntp_perror(dst_name);
                        nntp_doclose(n);
                        return -1;
                }
        }
        return n;
}
int nntp_getports(int c, int *local, int *remote)
{
        int len;
        struct sockaddr_in hsoc, usoc, *him, *us;

        
        him = &hsoc;
        len=sizeof(struct sockaddr_in);
        if ( getpeername(chan[c], (struct sockaddr *) him, &len) < 0 ) {
          filemsg("Getports failed for [%d] \n",c);
          return FALSE;
        }

        /* get the port on our machine he is connecting to */
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getsockname(chan[c],(struct sockaddr *) us,&len) < 0 ){
          filemsg("Getports failed for [%d] \n",c);
          return FALSE;
        }

        *local = (int) ntohs(us->sin_port);
        *remote = (int) ntohs(him->sin_port);
        return TRUE;
}

void nntp_readwrite1(int c)
{
        int r,w;
        char tmp[BFSZ];
        char bf[BIGSZ];
	char bf2[BFSZ];
        int lport,rport;

/*
        if (!sent_ip[c]) {
                nntp_getports(c,&lport,&rport);
                sprintf(bf,"xdrealip %s %s %s %d %d\r\n",chan_ipnum[c],nntp_localip(c),conf_getpass(),lport,rport);
                socket_write(chan_out[c],bf,strlen(bf));
                sent_ip[c] = TRUE;
        }
*/
        errno = 0;
        dbug("read reader [%d]\n",c);
        r =  socket_read(chan[c],bf,bigsz-200);
	if (r>=0) bf[r] = 0;
        nntp_lasterror();
        if (r<=0) imsg("Socket read from reader[%d] %d bytes e=%d\n",c,r,werrno);
        if (r>0) {
                ncpy(tmp,bf,10);
                if (r<10) tmp[r] = 0;
                dbug("from reader [%d] %d bytes (%s) \n",c,r,tmp);
        }
        if (r<=0) {
#ifndef OS2
                if (nntp_errbusy() ) {
                        return;
                }
#endif
                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;
        dbug("Write to news [%d]\n",c);

/*        if (!sent_ip[c]) {
                nntp_getports(c,&lport,&rport);
                sprintf(bf2,"xdrealip %s %s %s %d %d\r\n",chan_ipnum[c],nntp_localip(c),conf_getpass(),lport,rport);
imsg("tellip %s %s %s %d %d\n",chan_ipnum[c],nntp_localip(c),conf_getpass(),lport,rport);
                memmove(bf+strlen(bf2),bf,r);
		memmove(bf,bf2,strlen(bf2));
		r+= strlen(bf2);
                sent_ip[c] = TRUE;
        }        
*/	
	w = socket_write(chan_out[c],bf,r);
        nntp_lasterror();
        dbug("Write done to news[%d]\n",c);
        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[BIGSZ];
        char bf2[BFSZ];
        char tmp[BFSZ];
	int lport,rport;
	int nloop=0;
        errno = 0;



again:
	nloop++;
        dbug("read news [%d]\n",c);
        r =  socket_read(chan_out[c],bf,bigsz);
	imsg("nloop %d read %d bytes\n",nloop,r);
	if (r>=0) bf[r] = 0;
        nntp_lasterror();
        if (r<=0) imsg("Socket read from news[%d] %d bytes e=%d\n",c,r,errno);
        if (r>0) {
                ncpy(tmp,bf,10);
                if (r<10) tmp[r] = 0;
                dbug("from news [%d] %d bytes (%s) \n",c,r,tmp);
        }
        if (r<=0) {
#ifndef OS2
                if (nntp_errbusy() ) {
                        return;
                }
#endif
                if (r!=0) nntp_perror("Socket closed on read from dnews");
                else imsg("Socket closed normally [%d] \n",c);
                nntp_doclose(c); 
                return;
        }

        if (!sent_ip[c]) {
                nntp_getports(c,&lport,&rport);
                sprintf(bf2,"xdrealip2 %s %s %s %d %d\r\n",chan_ipnum[c],nntp_localip(c),conf_getpass(),lport,rport);
imsg("tellip %s %s %s %d %d\n",chan_ipnum[c],nntp_localip(c),conf_getpass(),lport,rport);
		r = socket_write(chan_out[c],bf2,strlen(bf2));
		if (r!= (int) strlen(bf2)) {
			nntp_perror("Unable to send xdrealip");
			nntp_doclose(c);
			return;
		}
                sent_ip[c] = TRUE;
		if ((atoi(bf)>199) && (atoi(bf)<299)) {	
			imsg("Swallowed initial welcome %.180s\n",bf);	
			return ;
		}
		/* */
        }        

        errno = 0;
        dbug("write reader [%d]\n",c);
        w = socket_write(chan[c],bf,r);
        nntp_lasterror();
        dbug("write reader done [%d]\n",c);
        if (w<r) imsg("Socket write to reader [%d] %d/%d bytes e=%d\n",c,w,r,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); 
        }
	if (nloop<10) goto again;
}
void nntp_writerest1(int c)
{
        int n,w;
        errno = 0;
        n = chan_data1_len[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];
        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 = atoi(config_get(C_OPEN_RETRY));
        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 char dnews_conf[BFSZ];
static int isrestart;
static int forkat;
#ifdef WIN32
void svc_init(char *s);
void svc_exiting(void);
void svc_exit(void);
int ServiceMain(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
        int i;
        int pchan = -1;
	int xdelay;
#ifdef USEONEXIT
        on_exit(exit_handler); 
#else
        atexit(exit_handler); 
#endif

        for (i=1; i<argc; i++) {
           if (strncmp(argv[i],"-p",2)==0)  {
                   pchan = atoi(argv[i]+2);
           }
           if (strncmp(argv[i],"-restart",4)==0)  {
                isrestart = TRUE;
           }
           if (strcmp(argv[i],"-testing")==0)  {
		istesting = TRUE;
           }
	   if (strcmp(argv[i],"-forkat")==0) {
		forkat = atoi(argv[++i]);
	   }
	   if (strncmp(argv[i],"-conf",3)==0) {
		strcpy(dnews_conf,argv[++i]);
	   }
        }



        config_read();
        emsg_open();
        imsg("DMULTI, Version %s Starting\n",DNEWS_VERSION);
        remove(DMULTI_EXIT);
        for (i=0; i<argc; i++) imsg("argv[%d] = %s\n",i,argv[i]);
        sig_init();
#ifdef WIN32
        svc_init("dmulti");
#else
#ifdef SAVE_LOW
	if (forkat==0) forkat = 900;
#endif
#endif
        nntp_startup();
        nntp_init(pchan);
	nntp_listen_ports();

	xdelay = atoi(config_get(C_STARTUP_DELAY));
#ifdef WIN32
	if (xdelay==0) {
		emsg("Using default 5 second delay on startup as this is sometimes needed on NT for disks to appear\n");
		xdelay = 5;
	}
#endif
	if (xdelay>0) {
		emsg("Startup delay %d seconds\n",xdelay);
		lib_sleep(xdelay);
		emsg("Startup delay %d seconds - done\n",xdelay);
	}


        conlimit = atoi(config_get(C_CONCURRENT_LIMIT));
        /* start n dnews processes */
        if (!istesting) if (!isrestart) start_servers();
	if (istesting) {
	        slave_port = atoi(config_get(C_SLAVE_PORTS));
	        if (slave_port==0) slave_port = 7500;		
	}
        bigsz = atoi(config_get(C_OUT_SIZE));
#ifdef LIMIT_OUT_SIZE
        if (bigsz<10) bigsz = 2000;
#endif
        if (bigsz<10) bigsz = BIGSZ;
        if (bigsz>BIGSZ) bigsz = BIGSZ;
        imsg("out_size set to %d\n",bigsz);
        nntp_wait();
        return 0;
}
#ifdef WIN32
typedef int MYHANDLE;
#define MFILE MYHANDLE
#endif

#ifdef OS2
typedef int MYHANDLE;
#define MFILE MYHANDLE
#endif

#ifdef isunix
typedef int MYHANDLE;
#define MFILE int
#endif

int isalive(MFILE *x);
static int nslaves;
MFILE *slave_pipe[300];
MFILE *myspawn(char *cmd, char *mde);
void myspawnx(MFILE *x);
#ifdef WIN32
#define popen myspawn
#define pclose myspawnx
#else
#define popen myspawn
#define pclose myspawnx
#endif
char *dnews_bin(void);
void start_servers(void)
{
        int i;
        char bf[BFSZ];
	if (ischild) return;
        nslaves = atoi(config_get(C_SLAVE_N));
   	for (i=0; i<nslaves; i++) chan_slave[i]=INVALID_SOCKET;
	if (nslaves<1) {
                emsg("slave_n must be 2 or more, dmulti exiting\n");
                dmulti_exit("slave_n not defined in dnews.conf");
        }
        slave_port = atoi(config_get(C_SLAVE_PORTS));
        if (slave_port==0) slave_port = 7500;
        for (i=0; i<nslaves; i++) {
                sprintf(bf,"%s -s %d >slave_%d.out",dnews_bin(),i,i);
                imsg("Spawning process %s\n",bf);
                slave_pipe[i] = popen(bf,"w");
        }
}
#ifdef WIN32
MFILE *myspawn(char *cmd, char *mde)
{
        static char *args[10];
        int n=0;
        MFILE *ph;
        char *xcmd;
        char *s;
        int h;  
        s = strtok(cmd," ");
        for (;s!=NULL;) {
                args[n++] = strdup(s);
                if (n==1) xcmd = strdup(s);
                s = strtok(NULL," ");
        }
	args[n++] = NULL;
        h = _spawnv(_P_NOWAIT,xcmd,args);
        if (h<0) {
		filemsg("spawn: FAILED (%s) \n",cmd);
		perror("Problem with spawn"); 
		return NULL;
	}
        emsg("Handle %d\n",h);
        ph = mymalloc(sizeof(int));
        *ph = h;
        return ph;
}
void myspawnx(MFILE *x)
{
        if (x!=NULL) {
                myfree(x);
        }
}
int isalive(MFILE *x)
{
        int ecode;
        if (GetExitCodeProcess((HANDLE) *x,&ecode)) {
                if (ecode==STILL_ACTIVE) return TRUE;
	        emsg("GetExitCode(%d) ecode=%d Failed, error=%d\n",*x,ecode,GetLastError());
                return FALSE;
        } 
        emsg("GetExitCode(%d) Failed, error=%d\n",*x,GetLastError());
        return TRUE;
}
int slave_kill(MFILE *x)
{

        if (isalive(x)) {
          emsg("Killing slave process, handle %d\n",*x);
          if (TerminateProcess((HANDLE) *x,0)) {
                return TRUE;
          } 
          emsg("TerminateProcess(%d) Failed, error=%d\n",*x,GetLastError());
        }
        return TRUE;
}
#endif /* Unix versions of the same stuff */

#ifdef OS2
MFILE *myspawn(char *cmd, char *mde)
{
        char *args[10];
        int n=0;
        MFILE *ph;
        char *xcmd;
        char *s;
        int h;  
        s = strtok(cmd," ");
        for (;s!=NULL;) {
                args[n++] = strdup(s);
                if (n==1) xcmd = strdup(s);
                s = strtok(NULL," ");
        }
	args[n++] = NULL;
        h = spawnv(P_NOWAITO,xcmd,args);
        emsg("Handle %d\n",h);
        if (h<0) {perror("Problem with spawn"); return NULL;}
        ph = mymalloc(sizeof(int));
        *ph = h;
        return ph;
}
void myspawnx(MFILE *x)
{
        if (x!=NULL) {
                myfree(x);
        }
}
int isalive(MFILE *x)
{
        return TRUE;
}
int slave_kill(MFILE *x)
{
    emsg("Cannot terminate process");
    return TRUE;
}
#endif


#ifdef isunix
MFILE *myspawn(char *cmd, char *mde)
{
        static char *args[10];
        int n=0,r;
        MFILE *ph;
        char *xcmd;
        char *s;
        int h;  
        s = strtok(cmd," ");
        for (;s!=NULL;) {
                args[n++] = strdup(s);
                if (n==1) xcmd = strdup(s);
                s = strtok(NULL," ");
        }
	args[n++] = NULL;
        h = fork();
        if (h==0) { /* Child process */
         if (execv(xcmd,args)<0) {
                emsg("Could not create child process %s %s\n",xcmd,strerror(errno));
                exit(1);
         }
        }
        if (h<0) {
		filemsg("spawn: FAILED (%s) \n",cmd);
		perror("Problem with spawn"); 
		return NULL;
	}
        emsg("Created sub process, pid %d\n",h);
        ph = mymalloc(sizeof(int));
        *ph = h;
        return ph;
}
void myspawnx(MFILE *x)
{
        if (x!=NULL) {
                myfree(x);
        }
}
int isalive(MFILE *x)
{
        int ecode;
        int r;
        r = waitpid(*x,&ecode,WNOHANG | WUNTRACED);
        if (r==*x) {
                emsg("Process %d has exited, status %x\n",*x,ecode);
                return FALSE;
        }
        return TRUE;
}
int slave_kill(MFILE *x)
{

        if (isalive(x)) {
          emsg("Killing slave process, handle %d\n",*x);
          if (kill(*x,SIGKILL)!=0) 
            emsg("kill(%d) Failed, %s\n",*x,strerror(errno));
        }
        return TRUE;
}

#endif

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
        svc_exiting();
        for (i=1; i<nslaves; i++) {
                if (slave_pipe[i]!=NULL) {
                        slave_kill(slave_pipe[i]);
                        pclose(slave_pipe[i]);
                }
                slave_pipe[i] = NULL;
        }
        svc_exit();
#endif
#ifdef isunix
        if (!ischild) for (i=1; i<nslaves; i++) {
                if (slave_pipe[i]!=NULL) {
                        slave_kill(slave_pipe[i]);
                        pclose(slave_pipe[i]);
                }
                slave_pipe[i] = NULL;
        }
#endif
        exit(1);
}

char *choose_servern(int n);
int nntp_lookup(char *ip, char *rslt[], int max);
int match_wild(char *s, char *ip);
void access_convert(void);
static char *feeds[1000];
static int nfeeds;
int check_access(char *ip)
{
        static int done;
        int i;
        int isfeed=FALSE;
        if (!done) access_convert();
        done = TRUE;
        for (i=0; i<nfeeds; i++) {
                if (match_wild(feeds[i],ip)) isfeed = TRUE;
        }
        return isfeed;
}
static char *route_name[1000];
static int route_n[1000];
static int nroute;
void route_add(char *ip, char *rest)
{
        char *s;
        s = strstr(rest,"slave="); if (s==NULL) return;
        route_name[nroute] = strdup(ip);
        route_n[nroute++] = atoi(s+6);
        imsg("Route add (%s) to s%d\n",route_name[nroute-1],route_n[nroute-1]);
}
int route_find(char *ip)
{
        int i;
        int r = -1;
        char *s;
        char bf[BFSZ];
        for (i=0; i<nroute; i++) {
                strcpy(bf,route_name[i]);
                s = strtok(bf,", \n");
                for (;s!=NULL;) {
                        if (match_wild(s,ip)) r = route_n[i];
                        s = strtok(NULL,", ");
                }
        }
        return r;
}
#define ACCESS file_config("access.conf")
void access_convert(void)
{
        int i,j,n;
        char ip[BFSZ],access[BFSZ];
        char *s;
        char xbf[BFSZ];
        char bf[BFSZ];
        static char *rslt[500];
        FILE *f;

        if (rslt[0]==NULL) {
                for (i=0; i<300; i++) rslt[i] = mymalloc(22);
        }
        f = fopen(ACCESS,"r");
        if (f==NULL) {
                filemsg("Could not read (%s) \n",ACCESS);
                return;
        }
        
        for (;!feof(f);) {
                if (fgets(bf,BFSZ-10,f)==NULL) break;
                ncpy(xbf,bf,BFSZ-1);
                s = strchr(bf,'#'); if (s!=NULL) *s = 0;
                s = strtok(bf,":\r\n"); if (s==NULL) continue;
                ncpy(ip,s,BFSZ-1);
                if (strstr(xbf,"slave=")!=NULL) route_add(ip,xbf);
                s = strtok(NULL,":\r\n"); if (s==NULL) continue;
                ncpy(access,s,BFSZ-1);

                if (strncmp(ip,"#",1)==0) continue;
                mystrlwr(access);
                if (strchr(access,'i')==NULL) continue;
                if (isdigit(ip[0])) goto addit;
                if (strchr(ip,'*')!=NULL) goto addit;
                if (strchr(ip,',')!=NULL) goto addit;
                if (strchr(ip,'[')!=NULL) goto addit;
                n = nntp_lookup(ip,rslt,300);
                if (n==0) {
                        imsg("Could not lookup name in access.conf (%s) \n", bf);
                        continue;
                } else {
                        for (j=0; j<n; j++) {
                                feeds[nfeeds++] = strdup(rslt[j]);              
                                imsg("access.conf: Adding ip number for feed name %s->%s\n",ip,rslt[j]);
                        }
                }
                continue;
addit:          feeds[nfeeds++] = strdup(ip);
                imsg("access.conf: Adding ip number for feed %s\n",ip);
        }
        fclose(f);
}

int nntp_lookup(char *ip, char *rslt[], int max)
{
        struct sockaddr_in sin;
        struct hostent *hp;
        int i;
        int xnow;
        imsgf("nntp: Doing forward DNS lookup (%s)\n",ip);
        xnow = lib_gmtime();
        hp = gethostbyname(ip);
        imsgf("nntp: forward DNS completed\n");
        if ((lib_gmtime()-xnow)>4) {
                emsg("forward dns lookup took %d seconds, check access.conf entry (%s) and or set access_noforward true\n",(lib_gmtime()-xnow),ip);
        }
        if (hp==NULL) return 0;
        for (i=0; i<max; i++) {
                if (hp->h_addr_list[i] == NULL) break;
                memcpy(&(sin.sin_addr),hp->h_addr_list[i],  hp->h_length);
                strcpy(rslt[i], inet_ntoa(sin.sin_addr));
        }
        return i;
}


char *choose_server(char *ip, int *xn)
{
        static char bf[5000];
        static int n;
        char *s;
        int i;
        static int done;
        xisfeed = FALSE;
	*xn = 0;

        ncpy(bf,config_get(C_SLAVE_FEED),4000);
        if (!done) imsg("Slave_feed = (%s)\n",bf);
        done = TRUE;
        if (strlen(bf)==0) {
                xisfeed = check_access(ip);
        } else {
                s = strtok(bf,", \n");
                for (;s!=NULL;) {
                        if (match_wild(s,ip)) xisfeed = TRUE;
                        s = strtok(NULL,", ");
                }
        }
        if (xisfeed) return choose_servern(0);

        i = route_find(ip);
	*xn = i;
        if (i>=0) return choose_servern(i);

        
        n++;
        if (n>=nslaves) n = 1;
	*xn = n;
        return choose_servern(n);
}
char *choose_servern(int n)
{
        static char bf[BFSZ];


        sprintf(bf,"%s:%d","127.0.0.1",slave_port+n);
#ifdef OS2
        sprintf(bf,"%s:%d",config_get(C_MYNAME),slave_port+n);
#endif      
        if (strlen(config_get(C_BIND_IN))>0) 
          sprintf(bf,"%s:%d",config_get(C_BIND_IN),slave_port+n);

        return bf;
}

char *choose_master(void)
{
        static char bf[BFSZ];
        sprintf(bf,"%s:%d","127.0.0.1",slave_port);
#ifdef OS2
        sprintf(bf,"%s:%d",config_get(C_MYNAME),slave_port);
#endif      
        if (strlen(config_get(C_BIND_IN))>0) 
          sprintf(bf,"%s:%d",config_get(C_BIND_IN),slave_port);
        return bf;
}
int handle_count(void)
{
        SOCKET f[10000];
        int i;
        int n;
        for (i=0; i<100; i++) {
                f[i] = socket(AF_INET, SOCK_STREAM, 0);
                if (f[i]==INVALID_SOCKET) break;
        }
        n = i;
        for (i=0; i<n; i++) {
                socket_close(f[i]);
        }
        
        return n;
}
void drain_slave(int i)
{
	int r;
	char bf[BFSZ];
	char *s;
	errno = 0;
        r =  socket_read(chan_slave[i],bf,300);
	if (r>=0) bf[r] = 0;
        nntp_lasterror();
        if (r<=0) {
                 if (nntp_errbusy() ) return;
                 if (r!=0) nntp_perror("Slave Socket closed on read");
		 nntp_perror("Slave Socket closed on read");
                 imsg("[%d]Slave socket closed %d\n",i,i);
                 socket_close(chan_slave[i]); 
                 chan_slave[i] = INVALID_SOCKET;
        } else {
		s = strchr(bf,'\r');
		if (s!=NULL) *s = 0;
		imsg("[%d] Drain (%s) \n",i,bf);
	}
}
void drain_msock(void)
{
	int r;
	char bf[BFSZ];
	errno = 0;
	if (ischild) return;
        r =  socket_read(msock,bf,300);
	if (r>=0) bf[r] = 0;
        nntp_lasterror();
        if (r<=0) {
                 if (nntp_errbusy() ) return;
                 if (r!=0) nntp_perror("Slave Socket closed on read");
                 imsg("Master socket closed 0\n");
                 socket_close(msock); 
                 msock = INVALID_SOCKET;
        }
}
int nntp_nused(void);
#ifdef isunix
#define MAXFORK 500
static int forkpid[MAXFORK];
static int nfork;
void fork_pidsave(int pid)
{
	int i;
	for (i=0; i<MAXFORK; i++) {
		if (forkpid[i]<=0) {
			forkpid[i] = pid;
			if (nfork<=i) nfork = i+1;
			return;
		}
	}
	if (nfork>=MAXFORK) emsg("pidsave failed\n");
	else forkpid[nfork++] = pid;
	return;
}
void fork_release(void)
{
	int ecode;
	int r;
	int i;
	for (i=0; i<nfork; i++) {
		if (forkpid[i]<=0) continue;
	        r = waitpid(forkpid[i],&ecode,WNOHANG | WUNTRACED);
		if (r==forkpid[i]) {
			imsg("Child (%d) has exited\n");
			forkpid[i] = -1;
		}
	}
}
int do_fork(void)
{
	int pid;
	int hf,hu;
	int i;
	imsgf("Reached channel limit so forking %d users\n",nntp_nused());
        hf = handle_count();
        hu = nntp_nused();
        imsg("Before child free handles %d used %d total %d\n",hf,hu,hf+hu);
	pid = fork();
	if (pid<0) {
		filemsg("Fork failed, thats odd \n");
		forkat = 0;
		return FALSE;
	}
	if (pid==0) { /* Child process */	
		imsgf("This is the child dmulti running\n");
		ischild = TRUE;
		if (x_listen!=INVALID_SOCKET) socket_close(x_listen);
		x_listen = INVALID_SOCKET;
		if (msock!=INVALID_SOCKET) socket_close(msock);
		msock = INVALID_SOCKET;
		for (i=0; i<nchan; i++) if (chan_out[i]==msock) chan_out[i] = INVALID_SOCKET;
		return TRUE;
	}
	imsgf("Forked dmulti pid=%d\n",pid);
	for (i=0; i<nchan; i++) {
		if (msock == chan_out[i]) continue;
		nntp_doclose(i);
	}
        hf = handle_count();
        hu = nntp_nused();
        imsg("Master now has handles: %d, used %d total %d\n",hf,hu,hf+hu);
	fork_pidsave(pid);
	return TRUE;
}
#else
void fork_release(void)
{
}
int do_fork(void) /**/
{
	return TRUE;
}
#endif
static int ntried;
static int norestart;
void m_idle(void)
{
        int r;
        int n;
	int i;
        char bf[BFSZ],bf2[BFSZ];
        static int start;
        char *s;
        static int trylater = FALSE;
        int hf,hu;
        if (doexit) {
                dnews_shutdown();
                dmulti_exit("Service shutdown requested");
        }
	if (!ischild) if (forkat>0) if (nntp_nused()>forkat) {
		do_fork();
	}
	if (ischild) {
		if (nntp_nused()<=0) dmulti_exit("Child shutting down as nused is now zero\n");
		return;
	}

        slave_check();
        slave_restart();
        if (start==0) start = lib_gmtime();
        if ((lib_gmtime() % 600) == 0) {
                hf = handle_count();
                hu = nntp_nused();
                imsg("Free handles %d used %d total %d\n",hf,hu,hf+hu);
        }
        if ((lib_gmtime() % 20) == 0) {
		fork_release();
                emsg_flush();
                check_exit();
        }
	if (config_true(C_SLAVE_ROUTE)) {
		cangive = FALSE;
	}
	if (cangive) for (i=0; i<nslaves; i++) {
	  if (chan_slave[i]==INVALID_SOCKET) {
		imsg("Creating link to %s:%d ...\n",choose_servern(i),i);
                check_exit();
		n = nntp_getchan();
                nntp_open(choose_servern(i),n,TRUE);
                nntp_setblockraw(chan_out[n],FALSE);
		chan_slave[i] = chan_out[n];
		chan_out[n] = INVALID_SOCKET;
		nntp_doclose(n);
	  } else {
		drain_slave(i);
	  }
	}
        if (msock == INVALID_SOCKET) {
                if (ntried>10) dmulti_exit("Cannot connect to master");
                n = nntp_getchan();
                emsg("Attempting to establish link to master (%s)...\n",choose_master());
                check_exit();
                nntp_open(choose_master(),n,TRUE);
                msock = chan_out[n];
                chan_out[n] = INVALID_SOCKET;
                nntp_doclose(n);
                if (msock!=INVALID_SOCKET) emsg("Link successful !\n");
                else return;
                nntp_setblockraw(msock,FALSE);
                strcpy(bf,"xdmulti\r\n");
                if (socket_write(msock,bf,strlen(bf))!=(int) strlen(bf)) {
                        emsg("Master socket blocked, try later\n");
                        trylater = TRUE;
                }
                if ((lib_gmtime() - start) < 300) ntried++;
        }
        if (msock != INVALID_SOCKET) {
                if (trylater) {
                 trylater = FALSE;
                 strcpy(bf,"xdmulti\r\n");
                 errno = 0;
                 if (socket_write(msock,bf,strlen(bf))!= (int) strlen(bf) ){
                      nntp_lasterror();
                      emsg("Master socket blocked, try later\n");
                      trylater = TRUE;
                        if (nntp_errbusy() ) return;
                        imsg("Master socket closed\n");
                        socket_close(msock);
                        msock = INVALID_SOCKET;
                        return;
                 }
                }
                errno = 0;
#ifdef OS2
                if (!tcp_waitfor(msock,0)) return;
#endif
                r =  socket_read(msock,bf,300);
		if (r>=0) bf[r] = 0;

                nntp_lasterror();
                if (r<=0) {
#ifndef OS2
                 if (nntp_errbusy() ) return;
#endif              
                 if (r!=0) nntp_perror("Master Socket closed on read");
                 else imsg("Master socket closed2\n");
                 socket_close(msock); 
                 msock = INVALID_SOCKET;
                 return;
                }
                bf[r] = 0;
                ncpy(bf2,bf,80);
                s = strchr(bf2,'\r');
                if (s!=NULL) *s = 0;
                emsg("Master %s\n",bf2);
                if (strstr(bf," kill ")!=NULL) {
                        s = strstr(bf,"kill");
                        emsg("Master asked us to kill slave %d\n",atoi(s+5));
			if (atoi(s+5)==0) {
				imsg("Oops, got that wrong, lets ignore that\n");
			} else {
	                        slave_kill(slave_pipe[atoi(s+5)]);
			}
                }
                if (strstr(bf,"norestart")!=NULL) {
                        norestart = TRUE;
                        emsg("Shutting down...\n");
                }
                if (strstr(bf,"shutdown")!=NULL) {
                        dmulti_exit("Master process asked us to shutdown\n");
                }
        }


        /*      if (fflush(slave_pipe[0])!=0) {
                perror("flush failed");
                dmulti_exit("Flush failed");
        }
*/
}
char *conf_getpass(void)
{
        FILE *f;
        char *s;
        char bf[100];
        char fname[200];
        static char r[100];

        sprintf(fname,"%s/tellnews.pass",config_get(C_WORKAREA));
        f = fopen(fname,"r");
        if (f==NULL) {
                emsg("Cannot read tellnews.pass %s %s\n",fname,strerror(errno));
                perror(fname);
                return "cannotreadtellnewspass";
        }
        fgets(bf,20,f);
        fclose(f);
        s = strtok(bf,"\n\t  ");
        if (s!=NULL) strcpy(r,s);
        return r;
}
void check_exit(void)
{
        FILE *f;
        f = fopen(DMULTI_EXIT,"r");
        if (f==NULL) return;
        fclose(f);
        emsg("Shutting down because dmulti.exit found...\n");
        lib_sleep(4);
        remove(DMULTI_EXIT);
        dmulti_exit("Master process asked us to shutdown\n");
}

void slave_check(void)
{
        int i;
        char bf[BFSZ];
	int f=1;

	if (istesting) return;
        if (norestart) return;
        for (i=0; i<nslaves; i++) {
                if (!isalive(slave_pipe[i])) {
                        check_exit();
                        pclose(slave_pipe[i]);
			if (i==0) if (!config_true(C_MASTER_RESTART)) {
		                dmulti_exit("Master crashed so shutting down, examine dnews.log before restarting");			
			}
                        sprintf(bf,"%s -s %d >slave_%d.out",dnews_bin(),i,i);
                        imsg("Restarting missing slave process %s\n",bf);
                        slave_pipe[i] = popen(bf,"w");
                }
        }
}
int lib_sleep(int x)
{
#ifdef OS2
        delay(x*1000);
#else
        sleep(x);
#endif
        return 1;
}
void do_restart(void)
{
        char bf[BFSZ];
	if (ischild) return;
	if (msock==INVALID_SOCKET) return;
        emsg("Nightly slave restart (Config setting slave_restart)\n");
        sprintf(bf,"fcmd %s killslaves\r\n",conf_getpass());
        socket_write(msock,bf,strlen(bf));      
        lib_sleep(2);
        emsg("Slaves should not auto restart...\n");
}
void slave_restart(void)
{
        static int last_hour = -1;
        time_t now;
        struct tm *t; 
        char send_at[BFSZ];

        strcpy(send_at,config_get(C_SLAVE_RESTART));
        if (strlen(send_at)==0) strcpy(send_at,"6");
        now = time(NULL);
        t = localtime(&now);

/*      if ((now-last)>60) {
                last = now;
                imsg("testing restart\n");
                do_restart();
        }
*/
        if (last_hour<0) last_hour = t->tm_hour;
        if (t->tm_hour == last_hour) return;
        if (t->tm_hour == atoi(send_at)) do_restart();
        last_hour = t->tm_hour;
}
void dnews_shutdown(void)
{
        char bf[BFSZ];
	int n;
	if (ischild) {
		imsg("Not doing a tellnews exit as I am a child dslave\n");
		return;
	}
        if (msock==INVALID_SOCKET) {
		imsg("Not doing a tellnews exit because msock is invalid\n");
		return;
	}
        sprintf(bf,"fcmd %s exit\r\n",conf_getpass());
        n = socket_write(msock,bf,strlen(bf));
	nntp_lasterror();
	if (n!=strlen(bf)) nntp_perror("write failed in shutdown");
	imsg("Sent - tellnews exit - to master dnews process\n");
        drain_msock();
        lib_sleep(2);
	imsg("drained msock, it must have got the message now\n");
        if (msock!=INVALID_SOCKET) socket_close(msock); 
        msock = INVALID_SOCKET;
	imsg("Closed it too\n");
}
char *dnews_config(void);
char *dnews_config(void)
{

#ifdef WIN32
        static char sysdir[MAX_PATH];
        static char binpath[MAX_PATH];
	if (strlen(dnews_conf)>0) return dnews_conf;
        GetSystemDirectory(sysdir,MAX_PATH);
        sprintf(binpath,"%s\\dnews.conf",sysdir);
        return binpath;
#else
#ifdef OS2
	if (strlen(dnews_conf)>0) return dnews_conf;
        return "dnews.conf";
#else
	if (strlen(dnews_conf)>0) return dnews_conf;
        return "/etc/dnews.conf";
#endif
#endif
}
char *dnews_bin(void)
{
#ifdef WIN32
        static char sysdir[MAX_PATH];
        static char binpath[MAX_PATH];
	char xtra[BFSZ];
	strcpy(xtra,"");
	if (strlen(dnews_conf)>0) {
		sprintf(xtra," -conf %s",dnews_conf);
	}
        GetSystemDirectory(sysdir,MAX_PATH);
        sprintf(binpath,"%s\\dnews.exe%s",sysdir,xtra);
        return binpath;
#else
        static char bf[400];
	char xtra[BFSZ];
	strcpy(xtra,"");
	if (strlen(dnews_conf)>0) {
		sprintf(xtra," -conf %s",dnews_conf);
	}
        sprintf(bf,"%s/dnews%s",config_get(C_CONFIG),xtra);
        return bf;
#endif
}
static char *conf_value[200];
static int conf_true[200];
void config_read(void)
{
        int j;
        char nam[3201];
        char val[3201];
        char bf[3001];
        char bf3[3001];
        FILE *f;
        char *s;

        f = fopen(dnews_config(),"r");
        if (f==NULL) {
                printf("Could not read %s\n",dnews_config());
                perror(dnews_config()); 
                exit(1);
        }
        for (; !feof(f); ) {
                if (fgets(bf,2000-1,f)==NULL) break;
                s = strtok(bf," \t\n");
                if (s==NULL) continue;
                if (*s=='#') continue;
                strcpy(nam,s);
                s = strtok(NULL,"\n\t");
                if (s==NULL) continue;
                for (;isspace(*s);) s++;
                strcpy(val,s);
                mystrupr(nam);
                for (j=0; conf_name[j]!=NULL; j++) {
                        if (strcmp(nam,conf_name[j])==0) {
                                printf("Got ini setting %s = %s\n",nam,val);
                                conf_value[j] = strdup(val);
                                ncpy(bf3,val,30);
                                mystrlwr(bf3); 
                                if (strstr(bf3,"false")!=NULL) conf_true[j] = FALSE;
                                if (strstr(bf3,"true")!=NULL) conf_true[j] = TRUE;
                                if (strstr(bf3,"on")!=NULL) conf_true[j] = TRUE;
                                break;
                        }
                }
        }
        fclose(f);
}

int config_true(int x)
{
        return conf_true[x];
}
char *config_get(int c)
{
        if (conf_value[c]!=NULL) return conf_value[c];
        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[10000];
int emsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        emsg_do(output);
        emsg_flush();
        return 0;
}
void dmsg(char * arg_list, ...)
{
}
int imsg(const char * arg_list, ...)
{
        va_list arg_ptr;
	const char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        emsg_do(output);
        return 0;
}
void dbug(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        static int done,xdebug;
        if (!done) {
                done = TRUE;
                if (strcmp(config_get(C_SLAVE_DEBUG),"true")==0) xdebug = TRUE;
                if (strcmp(config_get(C_SLAVE_DEBUG),"TRUE")==0) xdebug = TRUE;         
        }
        if (!xdebug) return;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        emsg_do(output);
}
static FILE *flog;

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

	if (flog==NULL) return;
        nlines++;
        if (nlines>7000) {
                nlines = 0;
                if (flog!=NULL) fclose(flog); flog = NULL;
                emsg_open();
        }
                
        t = time(NULL);
        tt = localtime(&t);
        ccc = ctime(&t); if (ccc==NULL) return;
        strcpy(tstr,ccc);
        tstr[19] = 0;
#ifdef isunix
	if (ischild) sprintf(bf,"%s: (%d) %s",tstr+8,getpid(),in);
	else  
#endif
		sprintf(bf,"%s: %s",tstr+8,in);
        fprintf(flog,"%s",bf);
}
void emsg_flush(void)
{
        fflush(flog);
}
char *file_work(char *s)
{
        static char bf[BFSZ];
        sprintf(bf,"%s/%s",config_get(C_WORKAREA),s);
        return bf;      
}

void emsg_open(void)
{
        char bf[BFSZ]; 
        char bf2[BFSZ]; 
        if (flog!=NULL) return;
        sprintf(bf,"%s/dmulti.log",config_get(C_LOGDIR));
        sprintf(bf2,"%s/dmulti2.log",config_get(C_LOGDIR));
        if (istesting) sprintf(bf,"dmulti.log",config_get(C_LOGDIR));
	remove(bf2);
	rename(bf,bf2);
        flog = fopen(bf,"w");
        if (flog==NULL) {
                printf("File %s\n",flog);
                perror("Couldn't open output log file");
                exit(1);
        }
        close_on_exec((int) fileno(flog));
}
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

#ifdef NEED_STRERROR
extern int sys_nerr;
extern char *sys_errlist[];
char *strerror(int x)
{
      if ((x<sys_nerr) && (x>=0)) return sys_errlist[x];
      return "No error";
}
#endif

#ifdef NEED_USLEEP
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
void uwakeup(int x)
{
        return;
}
int usleep(int x)
{
        struct itimerval tval,oval;
        tval.it_interval.tv_usec = 0;
        tval.it_interval.tv_sec = 0;
        tval.it_value.tv_usec = x;
        tval.it_value.tv_sec = 0;
        signal(SIGALRM,uwakeup);
        setitimer(ITIMER_REAL,&tval,&oval);
        pause();
        tval.it_interval.tv_usec = 0;
        tval.it_interval.tv_sec = 0;
        setitimer(ITIMER_REAL,&oval,NULL);
}
#endif
void close_on_exec(int fd)
{
#ifdef isunix
        if (fcntl(fd,F_SETFD,1)==-1){
                filemsg("Error doing close on exec fcntl %d\n",fd);
        }
#endif
}
char *file_log(char *s)
{
        static char bf[300];
        sprintf(bf,"%s/%s",config_get(C_LOGDIR),s);
        return bf;
}
char *file_config(char *s)
{
        static char bf[300];
        sprintf(bf,"%s/%s",config_get(C_CONFIG),s);
        return bf;
}
static char *chan_lclip[MAX_CHAN];

void nntp_setlocalip(int c,char *s)
{
        if (chan_lclip[c]!=NULL) free(chan_lclip[c]);
        chan_lclip[c] = strdup(s);
}
char *nntp_localip(int c)
{
        /* get the port on our machine he is connecting to */
        int len;
        struct sockaddr_in  usoc, *us;
        len=sizeof(struct sockaddr_in);

        if (chan_lclip[c]!=NULL) return chan_lclip[c];

        us = &usoc;
        if (getsockname(chan[c],(struct sockaddr *) us,&len) < 0 ){
          filemsg("Getports failed for [%d] \n",c);
          return "none";
        }
        return inet_ntoa(usoc.sin_addr);
        
}
#ifdef WIN32
int give_socket(int c, int slavei)
{
	int r;
	SOCKET sk=chan[c];
	char bf[BFSZ];
	int lport,rport;
	int hproc;
	HANDLE hishandle;

	hproc = (int) *slave_pipe[slavei];

	if (chan_slave[slavei]==INVALID_SOCKET) {
		imsg("Haven't linked to slaves yet\n");
		return FALSE;
	}

	r = DuplicateHandle(GetCurrentProcess(),(HANDLE) sk,(HANDLE) hproc,
		&hishandle,0,FALSE,
		DUPLICATE_SAME_ACCESS);
	if (!r) emsg("Duplicate handle %d to process %d failed err=%d\n",sk,hproc,GetLastError());

        
        nntp_getports(c,&lport,&rport);
	
	sprintf(bf,"fcmd %s accept %d %s %s %d %d\r\n",conf_getpass()
		,hishandle
		,chan_ipnum[c],nntp_localip(c)
		,lport,rport);
	imsg("send accept %d %s %s %d %d\r\n",hishandle
		,chan_ipnum[c],nntp_localip(c)
		,lport,rport);
	if (chan_slave[slavei]==INVALID_SOCKET) return FALSE;
	r = socket_write(chan_slave[slavei],bf,strlen(bf));  
	drain_slave(slavei);
	if (r<=0) {
		CloseHandle(hishandle);	
	}
	return TRUE;
}
#else
int give_socket(int c, int x)
{
	return TRUE;
}
#endif

