#define MAX_CHAN 800 /*Also defined in chan.h & nntp.c */


/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#ifdef VMS
#define GOT_TIME TRUE
#ifdef MULTINET
#define NEED_DECLARE XXX
#include "multinet_root:[multinet.include]errno.h"
#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.sys]time.h"
#include "multinet_root:[multinet.include.sys]ioctl.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include "multinet_root:[multinet.include]netdb.h"
#endif

#ifdef UCX
#define NEED_DTABLE TRUE
#define socket_perror perror
#define FD_SETSIZE 256
#include "fdset.h"
/*#include <unistd.h>*/
#include  <errno.h>
#include  <types.h>
#include  <stdio.h>
#include  <socket.h>
#include  <in.h>
#include  <netdb.h>             /* change hostent to comply with BSD 4.3 */
#include  <inet.h>
#include  <ucx$inetdef.h>       /* INET symbol definitions */
#define SOCKET_DEF TRUE
#endif
#endif


#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


#include "chan_dmy.h"
#include "chan.h"

#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>

#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



#include "dnews.h"
#include "str.h"
#include "lib.h"
#include "emsg.h"
#define EOL "\015\012"
#include "nntp.h"

/* void channel_read(int c, int gotio); */
void event_wait(void);
void suck_do(void);
void lib_sleep(int x);
void chan_init(int c);
int nntp_fastout(int c);
void nntp_speedout(int c, int sz);


#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 struct sockaddr_in chan_sin[MAX_CHAN];
static char *chan_outbf[MAX_CHAN];
static String nntp_chan_write[MAX_CHAN];
static int chan_busy[MAX_CHAN];
static int cmd_busy[MAX_CHAN],cmd_anybusy;
static int nntp_chan_close[MAX_CHAN];
static int chan_closetime[MAX_CHAN];
static int chan_block[MAX_CHAN];
int nchan;

static  struct sockaddr_in xsin;
static  struct servent *sp,fakesp;
/* #define BIGSZ 4000  Temporary change for debugging mosaic */
#define BIGSZ 8000
void nntp_perror(char *s);
int nntp_errbusy(void);
char *chan_getipnum(int c);
int chan_isfree(int c);
void chan_newchan(int c);
void nntp_addidle(int x);


void log_socket_write(int chan, char *bf, int sz);
int nntp_chan_init(int i);
void nntp_doclose(int c);
void nntp_closeit(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);


#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 int in_count[MAX_CHAN];
static double in_speed[MAX_CHAN];
static int in_time[MAX_CHAN];
static int out_count[MAX_CHAN];
static double out_speed[MAX_CHAN];
static int out_time[MAX_CHAN];
static int max_speed=1000000;
static int max_out=1000000;

int nntp_ifnoblock(void);

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

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;
}
int nntp_getbusy(int c)
{
	return cmd_busy[c];
}
void nntp_setbusy(int c, int v)
{
 	cmd_busy[c] = v;
	if (v) cmd_anybusy++;
	else cmd_anybusy--;
	if (cmd_anybusy<0) cmd_anybusy = 0;
}
void nntp_setblock(int c, int t)
{
        chan_block[c] = 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 nntp_chan_init(int i)
{
	int on= -1;
        if (chan_outbf[i]==NULL) {
                chan_outbf[i] = mymalloc(BIGSZ+2);
        }
	cmd_busy[i] = FALSE;
        nntp_chan_close[i] = FALSE;
       	if (check_sock(i,"setblock")) nntp_setblock(i,FALSE);

	
#ifdef HAS_NODELAY

	if (config_true(C_TCP_NODELAY)) {
        	imsg("nntp: Setting TCP_NODELAY for socket [%d]\n",i);
        	setsockopt(chan[i], IPPROTO_TCP, TCP_NODELAY,(char *) &on, sizeof(int));
	}
#endif 
/*
#ifdef WIN32
	int xxx;
	xxx = 1;
	setsockopt(chan[i],SOL_SOCKET,SO_OOBINLINE,&xxx,sizeof(xxx));
#endif
*/
        str_clear(&nntp_chan_write[i]);
	str_expand(&nntp_chan_write[i],8000);
        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;
                str_init(&nntp_chan_write[i]);
        }

}
#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;
                str_init(&nntp_chan_write[i]);
/*              chan_outbf[i] = mymalloc(BIGSZ+2); */
        }
        /*
         *  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);
 

#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;
	if (nntp_ifnoblock()) 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);
        }
        dmsg("nntp: init done\n");
}
static struct timeval timeout;
static struct timeval timecopy;
void nntp_setsleep(int x)  /* Used when background job wants to be called alot*/
{
	if (x>1) x = 1;
        timeout.tv_sec = x;
        dmsg("nntp: setsleep %d \n",x);
}

static int measure_idle = FALSE;
int nntp_measure_idle(int t)
{
 	measure_idle = t;
	return TRUE;
}
int xtime_now;
int cmd_finish(int i);
#define OUT_LIMIT 20000
void nntp_wait(void)
{
	static int last_give;
        int i,nc;
	int xnow;
        fd_set readfd,writefd,exceptfd;
	static int curtime=0;
	SOCKET wary[3000];
	int my[3000];
	int nw,nr,n;
        dmsg("nntp: Wait start\n");

        timeout.tv_sec = 1; /*Wake up once a second*/
        timeout.tv_usec = 0;
	xnow = xtime_now = lib_gmtime();
again:
	n = nw = nr = 0;
	wary[n++] = x_listen;
	xtime_now = lib_gmtime();
	if (curtime!=xtime_now) {
	 	curtime = xtime_now;
		for (i=0; i<nchan; i++) {
			nntp_speedin(i,0);
			nntp_speedout(i,0);
		}
	}
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                 if (nntp_chan_close[i]) {
                        if (!chan_busy[i]) nntp_doclose(i);
                        else {
                                if ((lib_gmtime()-chan_closetime[i]) >20) {
                                        wmsg("nntp: Closing channel with data still in buffer %d\n",i);
                                        nntp_doclose(i);
                                }
                        }
                 }
        }
        timecopy = timeout; /* Because linux clears the value each call */
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                if (chan[i]!=INVALID_SOCKET) {
                   if (nntp_outsz(i)<OUT_LIMIT)  if (!nntp_toofast(i)) if (!cmd_busy[i]) {
				my[n] = i;
			   wary[n++] = chan[i];
		   }
		   if (cmd_busy[i]) if (nntp_outsz(i)<OUT_LIMIT) timecopy.tv_sec = 0;
                }
        }
	nr = n;
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                if (chan[i]!=INVALID_SOCKET) {
                   if (chan_busy[i]) if (!nntp_fastout(i)) {
			my[n] = i;
			   wary[n++] = chan[i];
		   }
                }
        }
	nw = n - nr;
 	timecopy.tv_usec = 0;
        nc = select(wary,nr,nw,0,timeout.tv_sec*1000);
	xnow = lib_gmtime();
	if ((xnow-xtime_now)>0) nntp_addidle(xnow-xtime_now);
	xtime_now = xnow;
      	nntp_lasterror();		
        if (nc<0) nntp_perror("Select call failed");
        if (measure_idle) stats_end("idle");
	
	if (x_listen!=INVALID_SOCKET) if (wary[0]!= -1)  {
        	nntp_newchan_all();	
        }
	for (i=0; i<nr; i++) if (wary[i]!= -1) {
             channel_read(my[i]);
	}
	for (i=nr; i<n; i++) if (wary[i]!= -1) {
             nntp_write_now(my[i]);
	}

        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
	  	if (nntp_outsz(i)<OUT_LIMIT) if (cmd_busy[i]) cmd_finish(i); /* Still processing command */
        }


        dmsg("nntp: suck_call\n");
	{
		int start_time = lib_gmtime();
		int start_clock = stats_clock();
	        suck_do(); /*do back ground jobs */
		stats_add("Background",start_time,start_clock);
	}
        dmsg("nntp: suck_returned\n");
#ifdef WIN_SOCK
/*      wsock_do();*/
#endif
   }
      goto again;
}
#ifdef unix
#ifndef WIN_SOCK
#define MIGHT_FAIL TRUE
#endif
#endif


int nntp_except(int c)
{
#ifdef WIN_SOCKxx
	int r;
	char bf[BFSZ];
	if (chan[c]==INVALID_SOCKET) { nntp_doclose(c); return TRUE; }
        r =  recv(chan[c],bf,BFSZ-1,MSG_OOB);
      	nntp_lasterror();		
	if (r>0) {
		bf[r] = 0;
	 	emsg("nntp: Except data? {%s}\n",bf);
		return TRUE;
	}
#ifdef MIGHT_FAIL
        if (werrno==EAGAIN) {
                emsg("nntp: Socket reported EAGAIN, trying to continue\n");
                return 0;
        }
        if (werrno!=0) imsg("nntp: Socket closed, errno=%d\n",werrno);
#endif
        if (r!=0) nntp_perror("socket_read failed");
        else imsg("nntp: Socket closed by remote end [%d] %d\n",c,chan[c]);
#endif
        nntp_doclose(c);
        return FALSE;
}
int nntp_iswaiting(void)
{
        int i,nc;
        fd_set readfd,writefd,exceptfd;
	static struct timeval timeout;
	
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
             if (cmd_busy[i]) if (nntp_outsz(i)<OUT_LIMIT) return TRUE;
        }

        timeout.tv_sec = 0; /*Wake up once a second*/
        timeout.tv_usec = 0;

        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);

        if (x_listen!=INVALID_SOCKET) FD_SET(x_listen,&readfd);
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                if (chan[i]!=INVALID_SOCKET) {
                   if (nntp_outsz(i)<OUT_LIMIT)  if (!nntp_toofast(i)) FD_SET(chan[i],&readfd);
                   FD_SET(chan[i],&exceptfd);
                   if (chan_busy[i]) FD_SET(chan[i],&writefd);
                }
        }
        timecopy = timeout; /* Because linux clears the value each call */
        nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timecopy);
       	nntp_lasterror();		
        if (nc<0) nntp_perror("Select call failed");
        if (nc==0) return FALSE;
	return TRUE;

}
int nntp_getchan(void)
{
        /* shoould search thru list for unallocated one.*/
        int i;
        for (i=0; i<nchan ; i++) {
                if ((chan[i]==INVALID_SOCKET) && chan_isfree(i) ) {
                        chan_init(i);
                        return i;
                }
        }
        chan_init(nchan);
    
        if ((nchan+4)>MAX_CHAN) emsg("Channels exhausted, panic %d \n",nchan);
        if (chan_outbf[nchan]==NULL)  chan_outbf[nchan] = mymalloc(BIGSZ+2);
        return nchan++;
}
void nntp_close(int c)
{
        if (c==INVALID_SOCKET) return;
        if (chan[c]==INVALID_SOCKET) return;
        dmsg("chan: que close  %d %d \n",c,chan[c]);
        chan_closetime[c] = lib_gmtime();
        nntp_chan_close[c] = TRUE;
}
void nntp_closeit(int c)
{
        chan[c] = INVALID_SOCKET;
	if (cmd_busy[c]) nntp_setbusy(c,FALSE);
        nntp_chan_close[c] = FALSE;
        chan_busy[c] = FALSE;
        if (chan[c]!=INVALID_SOCKET) if (socket_close(chan[c])<0) imsg("nntp: closit failed\n");
        str_clear(&nntp_chan_write[c]);
	ihavein_clear(c);
        if (c==nchan-1) nchan--; /* should search backwards too, but doesn' matter much*/
}
void nntp_doclose(int c)
{
	if (cmd_busy[c]) nntp_setbusy(c,FALSE);
        dmsg("chan: real close  %d %d \n",c,chan[c]);
        if (chan[c]==INVALID_SOCKET) emsg("nntp: Channel is not open, %d %d \n",c,chan[c]);
        else if (socket_close(chan[c])<0) filemsg("nntp: Channel close failed\n");
        chan[c] = INVALID_SOCKET;
        nntp_chan_close[c] = FALSE;
        chan_busy[c] = FALSE;
        str_clear(&nntp_chan_write[c]);
	ihavein_clear(c);
        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


static char nntp_status[200];
char *nntp_getstatus(void)
{
        return nntp_status;
}
int nntp_newchan_waiting(void)
{
        int nc;
        fd_set readfd,writefd,exceptfd;
	static struct timeval timeout;
	
        timeout.tv_sec = 0; /*Wake up once a second*/
        timeout.tv_usec = 0;

        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);

        FD_SET(x_listen,&readfd);
        timecopy = timeout; /* Because linux clears the value each call */
        nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timecopy);
       	nntp_lasterror();		
        if (nc<0) nntp_perror("Select call failed");
        if (nc==0) return FALSE;
	return TRUE;
}
void nntp_newchan_all(void)
{
	int r; 	
	int n;
	if (x_listen==INVALID_SOCKET) return ;

	r = TRUE;
	for (n=0;r;n++) {
		nntp_newchan();
		r = nntp_newchan_waiting();
	}
	if (n>2) emsg("Processed %d pending new channel requests, maybe a reader is in a loop\n",n);
}
void nntp_newchan(void)
{
        char buf[1000];
        int n=nntp_getchan();
        int sizesin=sizeof(chan_sin[0]);
        int status,lookup,gotname;
        char ipname[200];
        char *canpost;
        char *s;
        char bf[BFSZ];
        strcpy(bf,"");

        dmsg("nntp: Newchan step 1 [%d]\n",n);
#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
        dmsg("nntp: Newchan step 2 %d \n",chan[n]);
        if (chan[n] == INVALID_SOCKET) {
          nntp_perror("Socket newchan accept() call failed");
          filemsg("Socket newchan accept() call failed\n");
          return;
        }
        lookup = lib_gmtime();

        gotname = nntp_getname(ipname,&chan_sin[n]);

        lookup = lib_gmtime()-lookup;
        if (lookup>3) imsg("Reverse Name lookup took %d seconds \n",lookup);
        if (lookup>14) {
                emsg("Reverse Name lookup took %d seconds, disable lookups if possible \n",lookup);
                sprintf(nntp_status,"Reverse lookup took %d seconds, disable if possible (see nodns)\015\012",lookup);
        }

        dmsg("nntp: newchan step 3 \n");
        chan_setipnum(n,inet_ntoa(chan_sin[n].sin_addr));
        if (gotname) {
                chan_setipname(n,ipname);
        } else {
                chan_setipname(n,inet_ntoa(chan_sin[n].sin_addr));
                if (!config_true(C_NODNS))
                   imsg("Reverse Name lookup failed for {%s}\n",chan_getipname(n));
        }
        wmsg("nntp: connection [%d:%d] From %s %s\n",n,chan[n],chan_getipname(n),chan_getipnum(n));
        nntp_chan_init(n);

        if (!chan_cantalk(n)) {
                emsg("Refusing connection from {%s}, not in access.conf\n",chan_getipname(n));
                s = config_get(C_NNTP_LOGOFF);
                if (strlen(s)==0) s = "You have no permission to talk";
		if (strlen(chan_elimit())>0) {
                	sprintf(buf,"502 %s {%s}, Goodbye\015\012",chan_elimit(),chan_getipname(n));
		} else {
                	sprintf(buf,"502 %s {%s}, Goodbye\015\012",s,chan_getipname(n));
		}
                if (nntp_isopen(n)) {
                  nntp_write_s(n, buf, strlen(buf));
                  nntp_close(n);
                }
                access_read(); /* Just incase it is local */
                return;
        }

        status = 201;
        canpost = "posting disabled";
        if (chan_canpost(n)) {canpost = "posting OK"; status=200;}
        sprintf(buf, "%d %s %s Version %s, %s \015\012",
               status,config_get(C_MYNAME),DNEWS_NAME,DNEWS_VERSION,canpost);

        if (nntp_isopen(n)) if (nntp_ifnoblock()) nntp_setblock(n,FALSE);
 
        if (nntp_isopen(n)) nntp_write_s(n, buf, strlen(buf));

	if (n>(getdtablesize()-2)) {
                if (nntp_isopen(n)) {
                        sprintf(buf,"502 Sorry only %d simultaneous users permitted, try later\015\012",getdtablesize());
                        nntp_write_s(n, buf, strlen(buf)); nntp_close(n);
			wmsg("User rejected because 285 users already connected\n");
       		}
	}
        if (!nt_getkey(bf,nchan)) {
                if (nntp_isopen(n)) {
                        sprintf(buf,"502 DNEWS License problem {%s}, See license.txt, Goodbye\015\012",bf);
                        nntp_write_s(n, buf, strlen(buf));
                        nntp_close(n);
                }
        }
	if (nntp_isopen(n)) {
		if (max_xx>0) if (nchan>max_xx) {
                        sprintf(buf,"502 DNEWS license faulty or hacked version, Please inform Netwin@netwinsite.com\015\012",bf);
                        nntp_write_s(n, buf, strlen(buf));
                        nntp_close(n);
		}
	}
 	if (nntp_isopen(n)) chan_newchan(n);

}
static int nntp_doblock;
void nntp_xxsetup(int x)
{
	max_xx = x;
}
void nntp_alwaysblock(void)
{
        nntp_doblock = TRUE;
}
int nntp_ifnoblock(void)
{
	int dontblock = TRUE;
        if (config_true(C_NNTP_BLOCK)) dontblock = FALSE;
        if (nntp_doblock) dontblock = FALSE;
#ifdef VMS
        dontblock = FALSE;
#endif
	return dontblock;
}


#ifdef WIN_SOCK
#define CAST_SOCKET TRUE
#endif
#ifdef NETWARE
#define CAST_SOCKET  TRUE
#endif

int nntp_nsockets(void)
{
	SOCKET xx[10000];
	int i;
	int n;
	for (i=0; i<10000; i++) xx[i] = INVALID_SOCKET;
	for (i=0; i<10000; i++) {
	        xx[i] = socket(AF_INET,SOCK_STREAM, 0);
		if (xx[i]==INVALID_SOCKET) break;
	}
	n = i;
	for (i=0; i<10000; i++) {
		if (xx[i]!=INVALID_SOCKET) socket_close(xx[i]);
	}
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) n++;
	return n;
}
int nntp_open(char *dst_name)
{
        int dst_port=119;
        struct sockaddr_in xsin;
        int st,lookup;
        int gotaddr;
        int n=nntp_getchan();
        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;
        }
        strlwr(dst_name); /* Seems to be required by NIS */
        lookup = lib_gmtime();
        gotaddr = nntp_getaddr(dst_name,&xsin);
        lookup = lib_gmtime() - lookup;
        if (lookup>4) emsg("nntp_open: Lookup took %d seconds \n",lookup);
        if (lookup>20) {
                emsg("nntp_open: Lookup took %d seconds, use numbers if possible \n",lookup);
                sprintf(nntp_status,"Host lookup took %d seconds, use numbers if possible\015\012",lookup);
        }
	if (!gotaddr) {
                emsg("Host Lookup failed {%s} \n",dst_name);
                chan[n] = INVALID_SOCKET;
                nntp_closeit(n);
                return -1;
        }

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

        dontblock = TRUE;
        if (config_true(C_OPEN_BLOCK)) dontblock = FALSE;
        if (nntp_doblock) dontblock = FALSE;
#ifdef VMS
        dontblock = FALSE;
#endif
        if (dontblock) {
                nntp_setblock(n,FALSE);
        }

#ifdef CAST_SOCKET
        st = connect(chan[n],(struct sockaddr *)   &xsin, sizeof(xsin));
#else
        st = connect(chan[n], &xsin, sizeof(xsin));
#endif
	nntp_lasterror();		
     	chan_busy[n] = FALSE;  /* initialize busy flag */
     	if (st!=0) {
                if (!nntp_errbusy()) {
                        nntp_perror(dst_name);
                        nntp_doclose(n);
                        return -1;
                }
		chan_busy[n] = TRUE; /* connection in progress, mark as busy */
        }

dmsg("nntp: open7\n");
        chan_setipnum(n,inet_ntoa(xsin.sin_addr));
dmsg("nntp: open8\n");
        chan_setipname(n,dst_name);
        dmsg("nntp: open feeder channel %d %d %s \n",n,chan[n],chan_getipname(n));
        nntp_chan_init(n);
        return n;
}



#define FASTNNTP  TRUE
int nntp_write_s(int c, char *s, int len)
{
        static String *ss;
        if (ss==NULL) ss = str_new();
        str_copysn(ss,s);
        nntp_write(c,ss);
        str_clearbig(ss);
        return 0;
}
int nntp_write(int c, String *ss)
{
        int sz = str_len(ss);
        int wasbusy;
        if (!check_sock(c,"nntp_write")) return TRUE; 
        if (sz==0) {emsg("nntp_write, empty string? \n"); return TRUE;}
        str_add(&nntp_chan_write[c],ss);
        wasbusy = chan_busy[c];
        chan_busy[c] = TRUE;
        if (chan_block[c]) nntp_write_now(c);
        else {
                if (chan[c]==INVALID_SOCKET) {emsg("nntp: channel not open %d ",c); return TRUE;}
                if (!config_true(C_NNTP_SLOW)) {
                        if (sz<2000) if (!wasbusy) nntp_write_now(c);
                }
                /* chan_busy[c] = TRUE; */  /* never send more than one packet without checking*/
        }
        return TRUE;
}
void show_outbf(char *s, int len);


int nntp_write_now(int c)
{
        int sz;
	int i;
	char *s;
        int r;
	int out_size = atoi(config_get(C_OUT_SIZE));
        int blen=str_len(&nntp_chan_write[c]);
        if (!check_sock(c,"nntp_write_now")) return TRUE; 

        sz = BIGSZ-1;
	if ((out_size>0)  && (out_size<BIGSZ)) sz = out_size;
        if (sz > blen) sz = blen;

        if (blen==0) {
                chan_busy[c] = FALSE;
                return 0;
        }

	/* Wrap on crlf records for dumb news readers, doesn't work because socket() adds records back together */

	if (config_true(C_TCP_NODELAY)) {
		s = str_s(&nntp_chan_write[c]) + sz - 1;
		if (sz>1000) for (i=0; i<900; i++,s--) {
			if (*s == '\012') {
				dmsg("nntp: Wrapped at %d/%d\n",sz-i,sz);
				sz -= i;
				break;
			}
		}
	}

        dmsg("nntp: [%d:%d] write %d bytes \n",c,chan[c],sz);
        
        /* show_outbf(str_s(&nntp_chan_write[c]),sz);*/
        dmsg("nntp: [%d:%d] write %d bytes \n",c,chan[c],sz);
        ncpy(chan_outbf[c],str_s(&nntp_chan_write[c]),sz);
        r = socket_write(chan[c],chan_outbf[c],sz);
	nntp_lasterror();		
        dmsg("nntp: write_done, wrote %d bytes\n",r);
	nntp_speedout(c,sz);
 
        if (r<0) {
                if (nntp_errbusy()) {
                        chan_busy[c] = TRUE;
                        return 0;
                }
                nntp_perror("socket_write error");
		if (nntp_errclosed()) {
			emsg("Beter pretend channel is closed just incase\n");
	                chan[c] = INVALID_SOCKET;
		}
		imsg("nntp: closing chan[%d] %d\n",c,chan[c]);
		nntp_doclose(c);
                r = 0;
                return r;
        }
        if (r<sz) sz = r;
        if (blen>sz) {
                str_right(&nntp_chan_write[c],sz);
        } else {
                str_clearbig(&nntp_chan_write[c]);
		str_expand(&nntp_chan_write[c],8000);
                chan_busy[c] = FALSE;
        }

        return r;
}
int nntp_isbusy(int c)
{
	if (c<0) return FALSE;
        return chan_busy[c];
}
int nntp_outsz(int c)
{
        if (!check_sock(c,"nntp_outsz")) return 0; 
        return str_len(&nntp_chan_write[c]);
}

int nntp_read(int c, char *bf, int sz)
{
  int r;
  int nbytes=sz;

        if (!check_sock(c,"nntp_read")) return 0; 
#ifdef MIGHT_FAIL
	errno = 0;
#endif
#ifdef unixxx_messed_up_solaris
  if (!chan_block[c]) {
#ifdef OS2
         socket_ioctl(chan[c],FIONREAD,&nbytes,sizeof(nbytes));
#else
#ifndef VMS
         socket_ioctl(chan[c],FIONREAD,&nbytes);
#endif
#endif


        if (nbytes<sz) sz = nbytes;
  } else nbytes = sz;
#else
        nbytes = sz;
#endif
  r = 0;
  if (nbytes>0) {
        dmsg("nntp: Socket read %d bytes \n",sz);
#ifdef MIGHT_FAIL
	errno = 0;
#endif
        r =  socket_read(chan[c],bf,sz);
       	nntp_lasterror();		
        dmsg("nntp: Socket read_done, got %d bytes\n",r);
  }
#ifdef MIGHT_FAIL
  if (r<0) {
        if ((werrno==EAGAIN) || nntp_errbusy() || (werrno==0)) {
                filemsg("nntp: Socket reported EAGAIN , trying to continue\n");
	 			in_count[c]  = max_speed + 100;
                return 0;
        }
        if (werrno!=0) filemsg("nntp: Socket closed, errno=%d\n",werrno);
  }
#endif
  if (r<=0) {
        if (r!=0) {
		nntp_perror("socket_read failed");
		imsg("nntp: Closing socket chan[%d] %d\n",c,chan[c]);
	}
        else imsg("nntp: Socket closed by remote end [%d] %d\n",c,chan[c]);
        nntp_doclose(c);
        return -1;
  }
  nntp_speedin(c,r);
  return r;
}
void nntp_speedin(int c, int sz)
{
	int delay;
	int t = lib_gmtime();
	if (in_time[c]!=t) {
		max_speed = atoi(config_get(C_MAX_SPEED));
		if (max_speed==0) max_speed = 1000000;
		delay = t - in_time[c];
		if (delay>20) delay = 20;
		in_speed[c] = in_speed[c]*(1.0-delay*.05) + in_count[c]*(delay*.05);
		in_count[c] = 0;
		in_time[c] = t;
	}
	in_count[c] += sz;
}
void nntp_speedout(int c, int sz)
{
	int delay;
	int t = lib_gmtime();
	if (out_time[c]!=t) {
		max_out = atoi(config_get(C_MAX_OUT));
		if (max_out==0) max_out = 1000000;
		delay = t - out_time[c];
		if (delay>20) delay = 20;
		out_speed[c] = out_speed[c]*(1.0-delay*.05) + out_count[c]*(delay*.05);
		out_count[c] = 0;
		out_time[c] = t;
	}
	out_count[c] += sz;
}
int nntp_toofast(int c)
{
 	static int done;
 	if (in_count[c]>max_speed) {
 		done = TRUE;
 		if (!done) imsg("Limiting speed for channel [%d] %s\n",c,chan_getipname(c));
 	}
 	if (in_count[c]>max_speed) return TRUE;
	return FALSE;
}
int nntp_fastout(int c)
{
 	static int done;
 	if (out_count[c]>max_out) {
 		done = TRUE;
 		if (!done) imsg("Limiting outgoing speed for channel [%d] %s\n",c,chan_getipname(c));
 	}
 	if (out_count[c]>max_out) return TRUE;
	return FALSE;
}
int nntp_curspeed(int c)
{
 	return (int) in_speed[c];
}
int nntp_curspeedout(int c)
{
 	return (int) out_speed[c];
}
void hexdump(char *x)
{
   int i;
   printf("Hex: ");
   for (i=0; i<8; i++,x++)  {
      printf("%2x ",*x);
   }
  printf("\n");
}
int nntp_isopen(int c)
{
        if (chan[c]==INVALID_SOCKET) return FALSE;
        return TRUE;
}
char *nntp_hostname(void)
{
   static char host[200];
   if (gethostname(host,190)!=0) emsg("nntp: Can't get host name\n");
   return host;
}
char *nntp_hostip(void)
{
        struct sockaddr_in xsin;
        int gotaddr;
        int dontblock = FALSE;
	static char answer[100];

	if (strlen(answer)>0) return answer;
        memset(&xsin,0,sizeof(xsin));
        xsin.sin_family = AF_INET;
        gotaddr = nntp_getaddr(nntp_hostname(),&xsin);
	if (!gotaddr) {
		emsg("Host lookup failed\n");
		return nntp_hostname();
	}
	strcpy(answer,inet_ntoa(xsin.sin_addr));
	imsgf("Looking up host ip address (%s) = %s\n",nntp_hostname(),answer);
	return answer;
}  

#ifdef hpux
#define NEED_DTABLE
#endif

#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>1024) max = 1024;
#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 = 1024;
#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 log_socket_write(int chan, char *bf, int sz)
{
        static FILE *f;
        if (f==NULL) {
                f = fopen("socket.log","w");
        }
        if (f==NULL) {emsg("Can't open socket.log\n"); return;}
        fprintf(f,"WRITE TO Channel %d , size %d \n",chan,sz);
        fwrite(bf,1,sz,f);
}
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
        filemsg("nntp_perror: %s\n",s);
#else
        filemsg("nntp_perror: %s\n",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) {
                dmsg("wsock: The socket is marked as non-blocking and the requested operation would block.\n");
                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
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;
                stats_cmd("ForwardDNS");
		imsgf("nntp: Doing forward DNS lookup\n");
                hp = gethostbyname(name);
		imsgf("nntp: forward DNS completed\n");
                stats_end("ForwardDNS");
		dmsg("nntp: done lookup %p {%s}\n",hp,name);
                if (hp==NULL) {dns_add(name,sin,FALSE); goto again; }
                memcpy(&(sin->sin_addr),hp->h_addr,  hp->h_length);
		dmsg("nntp: lookup worked %d\n",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)
{
        struct hostent *hp;
        int i;
	int xloop=0;

        if (config_true(C_NODNS)) return FALSE;

again:
	xloop++;
	if (xloop>2) {
                ncpy(name,"",99);
                return FALSE;
	}
        i = dns_find_addr(sin);
        if (i>=0) if (dns_ageok(i,TRUE)) {
                ncpy(name,dns_name[i],99);
                return dns_ok[i];
        }

        stats_cmd("RevDNS");
/*RHP cast for mac version */
	imsgf("nntp: Doing reverse DNS lookup\n");
#ifdef Mac
        hp = gethostbyaddr( (ip_addr *)&(sin->sin_addr), sizeof(sin->sin_addr), AF_INET);
#else
        hp = gethostbyaddr( (char *) &(sin->sin_addr), sizeof(sin->sin_addr), AF_INET);
#endif
	imsgf("nntp: Reverse DNS completed\n");
        stats_end("RevDNS");
        if (hp==NULL) {
                dns_add("",sin,FALSE);
                goto again;
        }
        dns_add(hp->h_name,sin,TRUE);
        goto again;
}

String *nntp_show_dns(void)
{
        static String *stat;
        char bf[BFSZ];
        int i;
        char *nn,*s;
        if (stat==NULL) stat = str_new();

        str_copysn(stat,"");
        sprintf(bf,"DNS caching, entries %d \015\012",ndns);
        str_addsn(stat,bf);
        for (i=0; i<DSIZE; i++) {
                s = dns_name[i];
                if ((s!=NULL) || (dns_sin[i].sin_addr.s_addr !=0)) {
                        if (s==NULL) s = "(none)";
                        if (strcmp(s,"")==0) s = "(unknown)";
                        nn = inet_ntoa(dns_sin[i].sin_addr);
                        sprintf(bf,"[%d] ok=%d age=%d %s = %s \015\012",i,dns_ok[i],lib_gmtime()-dns_time[i],s,nn);
                        str_addsn(stat,bf);
                }
        }
        return stat;
}
void nntp_lasterror(void)
{
	werrno = errno;
#ifdef WIN32
	werrno = WSAGetLastError();
#endif
#ifdef OS2
	werrno = sock_errno();
#endif	
}

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;
}
static int idle_ticks;
static int idle_time;
void nntp_addidle(int x)
{
	idle_ticks += x;	
	if (idle_time==0) idle_time = xtime_now;
}
int nntp_getidle(int *since)
{
	int x = idle_ticks;
	*since = lib_gmtime() - idle_time;
	idle_time = xtime_now;
	idle_ticks = 0;
 	return x;
}
