#include "dfeed.h"
void close_on_exec(int fd);
int imsg(const char * arg_list, ...);
int tcp_wouldblock(void);
void lib_sleep(int x);

static int werrno;

#ifndef WIN_SOCK
#define INVALID_SOCKET  -1
#endif
#ifdef NEED_BCOPY
#define bcopy(src,dst,len) memmove(dst,src,len)
#endif


int mytablesize(void)
{
	int x = FD_SETSIZE;
	if (x>256) x = 256;
	return x;
}
int tcp_isip(char *s)
{
	for (;*s!=0;s++) {
		if (*s=='.') continue;
		if (isdigit(*s)) continue;
		return FALSE;
	}
	return TRUE;
}
int tcp_getaddr(char *name, struct sockaddr_in *xsin)
{
	struct hostent *hp;
        if (tcp_isip(name)) {
                xsin->sin_addr.s_addr = inet_addr(name);
                xsin->sin_family = AF_INET;
		return TRUE;
        } else {
                xsin->sin_addr.s_addr = 0;
                hp = gethostbyname(name);
                if (hp==NULL) return FALSE;
                memcpy(&(xsin->sin_addr),hp->h_addr,  hp->h_length);
                xsin->sin_family = hp->h_addrtype;
		return TRUE;
        }
}
int tcp_bind_out(struct sockaddr_in *sin, char *xme)
{
        int gotaddr;
	if (strlen(xme)==0) return FALSE;
	gotaddr = tcp_getaddr(xme,sin);
        if (!gotaddr) {
                emsg("Host lookup failed for BIND (%s) use a number\n",xme);
                return FALSE;
        } else {
                imsg("bind_out to address (%s)\n",inet_ntoa(sin->sin_addr));    
        }

        return TRUE;
}
void tcp_setsndbf_raw(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);   
	if (was>=sz) return;
	imsg("sndbf buffer was %d\n",was);
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_SNDBUF, s, sizeof(xsz));   
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);   
	imsg("sndbf buffer is now  %d\n",was);

}


SOCKET tcp_openx(char *xname, int dst_port, char *xbind)
{
        struct sockaddr_in xsin,xme;
	SOCKET sk;
        int st;
        int dontblock = FALSE;
	char name[BFSZ];
	char *s;	

	
	strcpy(name,xname);
  
	memset(&xsin,0,sizeof(xsin));
	memset(&xme,0,sizeof(xme));
        xsin.sin_family = AF_INET;

        strlwr(name); /* Seems to be required by NIS */
	s = strchr(name,':');
	if (s!=NULL) {
		*s = 0;
		dst_port = atoi(s+1);
	}
	if (!tcp_getaddr(name,&xsin)) return INVALID_SOCKET;
       
	
	sk = socket(xsin.sin_family,SOCK_STREAM, 0);
       	tcp_lasterror();		
        if (sk==INVALID_SOCKET) {
		return sk;
        }
	close_on_exec((int) sk);

        if (tcp_bind_out(&xme,xbind)) {
                if (bind(sk,(struct sockaddr *) &xme,sizeof(xme))!=0) {
                        tcp_lasterror();
			emsg("Bind to (%s) failed\n",name);
			return INVALID_SOCKET;
                }
        }

        xsin.sin_port = htons((unsigned short) dst_port);

	tcp_setblockraw(sk,FALSE);
        st = connect(sk,(struct sockaddr *)   &xsin, sizeof(xsin));
	tcp_lasterror();		
     	if (st!=0) {
		if (tcp_wouldblock()) return sk;
		socket_close(sk);
		return INVALID_SOCKET;
        }

        return sk;
}
void tcp_setblockraw(SOCKET c, int t)
{
        int w;
        if (t) w=0;
        else w=1;
        socket_ioctl(c,FIONBIO,&w); 
}

#ifdef WIN32
#define HAS_NODELAY TRUE
#else 
#ifdef TCP_NODELAY
#define HAS_NODELAY TRUE
#endif
#endif
int tcp_setdelay(SOCKET sk, int t)
{
	int on;
	if (t) on=0;
	else on = -1;
#ifdef HAS_NODELAY
	setsockopt(sk, IPPROTO_TCP, TCP_NODELAY,(char *) &on, sizeof(int));
#endif 
	return TRUE;
}
int socket_read_timed(SOCKET sk, char *bf, int len, int msec)
{
	int r;
	tcp_setblockraw(sk,FALSE);
	tcp_waitfor(sk,msec);
	r = socket_read(sk,bf,len);
	tcp_setblockraw(sk,TRUE);
	return r;
}
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(mytablesize(),&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) return TRUE;
	return FALSE;
}
int tcp_setlinger(SOCKET sk)
{
	static struct linger x;
	int on= 1;
	int w=1;
	memset(&x,0,sizeof(x));
	x.l_onoff = 1;
	x.l_linger = 60;
	if (setsockopt(sk,SOL_SOCKET,SO_LINGER,(char *) &x,sizeof(x))!=0) {
		imsg("linger failed %s\n",tcperror());		
	}

	return TRUE;

}
SOCKET tcp_accept(SOCKET xlisten)
{
        struct sockaddr_in xsin;
	SOCKET x;
	int sizesin=sizeof(xsin);
        x = accept(xlisten,(struct sockaddr *) &xsin, &sizesin);
	close_on_exec((int) x);
	return x;
}
#ifndef WIN32
#define WSAEWOULDBLOCK EWOULDBLOCK
#endif
int tcp_readln(SOCKET sk, char *bf, int maxlen)
{
	char *s=bf;
	int r;
	int len=0;

	for (;TRUE;s++) {
again:
		r = socket_read(sk,s,1);
		tcp_lasterror();
		if (r<0) if (werrno==WSAEWOULDBLOCK) goto again;

		if (r!=1) return FALSE;
		if (*s=='\n') break;
	}
	len = s - bf - 1; 
	bf[len] = 0;
	return TRUE;;
}
#ifdef WIN32
int tcp_wouldblock(void)
{
	if (werrno==WSAEWOULDBLOCK) return TRUE;
	if (werrno==WSAEINPROGRESS) return TRUE;
	return FALSE;
}
#else
int tcp_wouldblock(void)
{
	if (werrno==EWOULDBLOCK) return TRUE;
	if (werrno==EAGAIN) return TRUE;
	if (werrno==EINPROGRESS) return TRUE;
	return FALSE;
}
#endif
int tcp_read(SOCKET sk, char *bf, int maxlen)
{
	int r;
	r = socket_read(sk,bf,maxlen);
	if (r<0) r = 0;
	bf[r] = 0;
	tcp_lasterror();
	if (r<=0) {
		if (tcp_wouldblock()) return 0; else return -1;
	}
	return r;
}
void close_on_exec(int fd)
{
#ifdef isunix
        if (fcntl(fd,F_SETFD,1)==-1){
                imsg("Error doing close on exec fcntl %d %s\n",fd,strerror(errno));
        }
#endif	
}
int tcp_write(SOCKET sk, char *bf, int maxlen)
{
	int r;
/*imsg("socket_write (%d) bytes\n",maxlen);*/
	r = socket_write(sk,bf,maxlen);
	tcp_lasterror();
	if (r<=0) {
		if (tcp_wouldblock()) return 0; else return -1;
	}
	return r;
}
SOCKET tcp_listen(int port)
{
	int quelen;
        struct sockaddr_in xsin;
	int x_listen;
	x_listen = socket(AF_INET, SOCK_STREAM, 0);
	close_on_exec((int) x_listen);
        tcp_lasterror();		
	imsg("Socket listen on %d \n",x_listen);

        if (x_listen == INVALID_SOCKET) {
	     return INVALID_SOCKET;
	}	
        xsin.sin_family = AF_INET;
        xsin.sin_addr.s_addr = INADDR_ANY;
        xsin.sin_port = htons((short) port);
        if (bind(x_listen,(struct sockaddr *)  &xsin, sizeof (xsin)) < 0) {
		tcp_lasterror();		
		return INVALID_SOCKET;
        }
 	quelen = 25;
#ifdef WIN32
	quelen = 200;
#endif        
        if (listen(x_listen, quelen) < 0) {
		tcp_lasterror();		
		return INVALID_SOCKET;
        }
	close_on_exec((int) x_listen);
	return x_listen;
}
void tcp_startup(void)
{
#ifdef WIN_SOCK
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
        wVersionRequested = MAKEWORD( 1, 1 );

        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) { imsg("No winsock found of correct version\n"); return;}
        if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
                imsg("No winsock found of correct version, trying to continue\n");
                goto ret;
        }
/* wmsg("Socket library found and is version 1.1, Great!\n"); */
/* The Windows Sockets DLL is acceptable. Proceed. */
ret: ;
#endif
  
}
#ifdef WIN32
int getdtablesize(void);
#endif

char *tcp_hostname(void)
{
   static char host[200];
   if (gethostname(host,190)!=0) imsg("tcp: Can't get host name %s\n",tcperror());
   return host;
}
void wsock_error(char *s);
#ifdef WIN_SOCK
char *tcperror(void)
{
	static char bf[500];
	char *s="";
        int err = werrno;
        if (err==WSANOTINITIALISED) sprintf(bf,"(%s) A successful WSAStartup must occur before using this API.\n",s);
        else if (err==WSAENETDOWN)      sprintf(bf,"(%s) The Windows Sockets implementation has detected that the network subsystem has failed.\n",s);
        else if (err==WSAEACCES)        sprintf(bf,"(%s) The requested address is a broadcast address, but the appropriate flag was not set.\n",s);
        else if (err==WSAEINTR) sprintf(bf,"(%s) The (blocking) call was canceled via WSACancelBlockingCall.\n",s);
        else if (err==WSAEINPROGRESS)   sprintf(bf,"(%s) A blocking Windows Sockets operation is in progress.\n",s);
        else if (err==WSAEFAULT)        sprintf(bf,"(%s) The buf argument is not in a valid part of the user address space.\n",s);
        else if (err==WSAENETRESET)     sprintf(bf,"(%s) The connection must be reset because the Windows Sockets implementation dropped it.\n",s);
        else if (err==WSAENOBUFS)       sprintf(bf,"(%s) The Windows Sockets implementation reports a buffer deadlock.\n",s);
        else if (err==WSAENOTCONN)      sprintf(bf,"(%s) The socket is not connected.\n",s);
        else if (err==WSAENOTSOCK)      sprintf(bf,"(%s) The descriptor is not a socket.\n",s);
        else if (err==WSAEOPNOTSUPP)    sprintf(bf,"(%s) MSG_OOB was specified, but the socket is not of type SOCK_STREAM.\n",s);
        else if (err==WSAESHUTDOWN)     sprintf(bf,"(%s) The socket has been shutdown; it is not possible to sendln on a socket after shutdown has been invoked with how set to 1 or 2.\n",s);
        else if (err==WSAEWOULDBLOCK)   sprintf(bf,"(%s) The socket is marked as non-blocking and the requested operation would block.\n",s);
        else if (err==WSAEMSGSIZE)      sprintf(bf,"(%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)        sprintf(bf,"(%s) The socket has not been bound with bind.\n",s);
        else if (err==WSAECONNABORTED)  sprintf(bf,"(%s) The virtual circuit was aborted due to timeout or other failure.\n",s);
        else if (err==WSAECONNRESET)    sprintf(bf,"(%s) The virtual circuit was reset by the remote side.\n",s);
        else if (err==WSAETOOMANYREFS)  sprintf(bf,"(%s) Too many refs.\n",s);
        else if (err==WSAETIMEDOUT)     sprintf(bf,"(%s) Timedout\n",s);
        else if (err==WSAECONNREFUSED)  sprintf(bf,"(%s) Connection Refused\n",s);
        else if (err==WSAEDESTADDRREQ)  sprintf(bf,"(%s) DestAddrReq\n",s);
        else if (err==WSAEMSGSIZE)      sprintf(bf,"(%s) Message Size\n",s);
        else if (err==WSAEADDRINUSE)    sprintf(bf,"(%s) Address in use\n",s);
        else if (err==WSAEADDRNOTAVAIL) sprintf(bf,"(%s) Address not avail\n",s);
        else if (err==WSAENETUNREACH)   sprintf(bf,"(%s) Network Unreachable\n",s);
        else if (err==WSAENETRESET)     sprintf(bf,"(%s) Network Reset\n",s);
        else if (err==WSAENOBUFS)       sprintf(bf,"(%s) No bufs\n",s);
        else if (err==WSAESHUTDOWN)     sprintf(bf,"(%s) Shutdown\n",s);
        else if (err==WSAENAMETOOLONG)  sprintf(bf,"(%s) Name too long\n",s);
        else if (err==WSAEHOSTDOWN)     sprintf(bf,"(%s) Host down\n",s);
        else if (err==WSAEHOSTUNREACH)  sprintf(bf,"(%s) Host Unreachable\n",s);
        else if (err==WSAENOTEMPTY)     sprintf(bf,"(%s) Not Empty\n",s);
        else sprintf(bf,"(%s) Socket Unkown error code %d \n",s,err);
	return bf;
}
#else
char * tcperror(void)
{
	return strerror(werrno);
}
#endif

void tcp_lasterror(void){
	werrno = errno;
#ifdef WIN32
	werrno = WSAGetLastError();
#endif
#ifdef OS2
	werrno = sock_errno();
#endif	
}

static SOCKET savesk;
static int isbuffer;
int tcp_printf(SOCKET sk,char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[7000];

        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsnprintf(output, 6000 , format, arg_ptr);
	output[6000] = 0;
	savesk = sk;
	if (xsocket_write(sk,output,len)!=len) return FALSE;
	imsg("-> %s",output);
	return TRUE;
}
static char tbf[90000];
static int tbf_len;
int xsocket_write(SOCKET sk, char *s, int len)
{
	FILE *f;
	f = fopen("raw.dat","ab");
	fwrite(s,1,len,f);
	fclose(f);
	
/*	memcpy(tbf+tbf_len,s,len);
	tbf_len += len;
	return len;
*/
	return socket_write(sk,s,len);
}
void socket_flush(SOCKET sk)
{
	int r;
	int sent=0;

	for (;sent<tbf_len;) {
		r = socket_write(sk,tbf+sent,tbf_len-sent);
	        tcp_lasterror();		
		imsg("Actually wroted %d bytes %s\n",r,tcperror());
		if (r<0) break;
		sent+=r;
	}	
	tbf_len = 0;
}
int tcp_waitlist(SOCKET list[], int nlist, int isready[], int msec)
{
    fd_set readfd,writefd,exceptfd;
	int i;
	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);
	for (i=0; i<nlist; i++) {
		if (list[i]!=INVALID_SOCKET) FD_SET(list[i],&readfd);
		isready[i] = FALSE;
	}
    nc = select(mytablesize(),&readfd,&writefd,&exceptfd,&timeout);
    if (nc<0) {
	emsg("select call failed fatal error\n");
	lib_sleep(1);

    }
	if (nc>0) {
		for (i=0; i<nlist; i++) {
			if(list[i]==INVALID_SOCKET)
				continue;
			if (FD_ISSET(list[i],&readfd)) isready[i] = TRUE;
		}
		return TRUE;
	}
	return FALSE;
}
void lib_sleep(int x)
{
	lib_msleep(x*1000);
}
static int nextwait;
int tcp_nextwait(int msec)
{
	nextwait = msec;
	return TRUE;
}
int tcp_waitchans(Chan *cstart)
{
	fd_set readfd,writefd,exceptfd;
	int nc=0;
	static int xx;
	int nchan=0;
	Chan *c;
	struct timeval timeout;
	
	timeout.tv_sec = 3;
	timeout.tv_usec = 0;
	if (nextwait>0) {
		timeout.tv_sec = 0;
		timeout.tv_usec = nextwait*1000;
		if (nextwait==1) timeout.tv_usec = 0;
		nextwait = 0;
	}
	FD_ZERO(&readfd);
	FD_ZERO(&writefd);
	FD_ZERO(&exceptfd);
	/* imsg("waitchans %p\n",cstart);*/
	for (c=cstart;c!=NULL;c=c->next) {
		/*dmsg("Wait for chan %p sending %d\n",c,c->sending);*/
		if (c->sk!=INVALID_SOCKET) {
			if (c->sending) FD_SET(c->sk,&writefd);
			FD_SET(c->sk,&readfd);
			FD_SET(c->sk,&exceptfd);
			nchan++;
		}
	}
	if (nchan>=mytablesize()) {
		emsg("FATAL ERROR, Too many channels used, set nchan lower for each feed\n");
	}
	nc = 0;
	if (nchan==0) {	
		stats_start(S_IDLE);
		lib_sleep(1); 
		stats_end(S_IDLE);
		goto cont;
	}
	stats_start(S_IDLE);
	xx++;
	if (xx % 6==3) lib_msleep(2); /* Sleep 4/1000 of a second so packets are reasonably big */
	nc = select(mytablesize(),&readfd,&writefd,&exceptfd,&timeout);
	stats_end(S_IDLE);
	if (nc<0) {
		emsg("Select call failed, should check for bad socket\n");
		for (c=cstart;c!=NULL;c=c->next) {
			FD_ZERO(&readfd);
			FD_ZERO(&writefd);
			FD_ZERO(&exceptfd);
			if (c->sk!=INVALID_SOCKET) {
				if (c->sending) FD_SET(c->sk,&writefd);
				FD_SET(c->sk,&readfd);
				nc = select(mytablesize(),&readfd,&writefd,&exceptfd,&timeout);
				if (nc<0) emsg("Bad socket found %d\n",c->sk);
			}
		}		
		dfeed_exit();
	}
cont:
	if (nc>0) {
		for (c=cstart;c!=NULL;c=c->next) {
			if(c->sk==INVALID_SOCKET) continue;
			if (FD_ISSET(c->sk,&readfd) || FD_ISSET(c->sk,&exceptfd)) {
				(*c->fn_read)(c,c->p1);
			}
			if(c->sk==INVALID_SOCKET) continue;
			if (FD_ISSET(c->sk,&writefd)) {
				(*c->fn_write)(c,c->p1);
			}
		}
		return TRUE;
	} else { /* Idle */
		chan_idle();
	}
	chan_loop();
	return FALSE;
}
int handle_count(void)
{
        SOCKET f[10000];
        int i;
        int n;
        for (i=0; i<300; 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;
}
SOCKET tcp_create_ip(int domain, int type, char *lname, int lport, char *rname, int rport);
SOCKET tcp_create_udp(char *lname, int lport, char *rname, int rport)
{
	return tcp_create_ip(AF_INET, SOCK_DGRAM, lname, lport, rname, rport);
}
SOCKET tcp_create_ip(int domain, int type, char *lname, int lport, char *rname, int rport)
{
	SOCKET sockfd;
	struct sockaddr_in sinme, sinhim;

	if ((sockfd = socket(domain, type, 0)) == INVALID_SOCKET ) {
		tcp_lasterror();		
       		emsg("create: socket() failed %s\n",tcperror());
		return INVALID_SOCKET;
	}
	sinme.sin_family = domain;
	sinme.sin_port = htons((short) lport);
	sinme.sin_addr.s_addr = 0;
	if (!lname) {
		lname = "0.0.0.0";
		sinme.sin_family = domain;
		sinme.sin_addr.s_addr = htonl(INADDR_ANY);
		}
	else {
		sinme.sin_family = domain;
		sinme.sin_addr.s_addr = inet_addr(lname);
	}
#ifdef WIN32
	{
		int on=1;
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,(const char *) &on, sizeof(int));
	}
#endif
	if (bind(sockfd, (struct sockaddr *)&sinme, sizeof(sinme)) < 0) {
		tcp_lasterror();		
       		socket_close(sockfd);
		emsg("create: bind() failed %s\n",tcperror());
		return INVALID_SOCKET;
	}
	if (!rname) return sockfd;
	sinhim.sin_port = htons((short) rport);

	sinhim.sin_family = domain;
	sinhim.sin_addr.s_addr = inet_addr(rname);
	if (connect(sockfd, (struct sockaddr *)&sinhim, sizeof(sinhim)) < 0) {
		tcp_lasterror();		
       		socket_close(sockfd);
		emsg("create: connect() failed %s\n",tcperror());
		return INVALID_SOCKET;
	}
	return sockfd;
}

int tcp_multicast_ttl(SOCKET fd, int ttl) 
{
	static unsigned char t;
	t = ttl;

	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,(char *) &ttl, sizeof(ttl)) < 0) {
		tcp_lasterror();		
		emsg("multicast_ttl failed %s\n",tcperror());
		return FALSE;
	}
	return TRUE;
}
/*
int setsockopt ( 
    SOCKET s,	
    int level,	
    int optname,	
    const char FAR *optval,	
    int optlen 	
   );

int UP_fd_multicast_ttl(struct UP_fd_struct *tsock, int sockfd, int ttl, void(*error_callback)()) {
	int fd;
	unsigned char t;
	t = ttl;
	if (tsock)
		fd = tsock->sockfd;
	else
		fd = sockfd;
	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&t, sizeof(t)) < 0) {
		if (error_callback)
			(*error_callback)("Failed to set multicast ttl to %d, errno is %d, fd is %d\n", ttl, errno, fd);
		return 0;
		}
	return 1;
	}

*/