/************************************************************************
 *   psybnc2.2.2, src/p_socket.c
 *   Copyright (C) 2001 the most psychoid  and
 *                      the cool lam3rz IRC Group, IRCnet
 *			http://www.psychoid.lam3rz.de
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef lint
static char rcsid[] = "@(#)$Id: p_socket.c, v 2.2.2 2001/03/25 02:02:02 psychoid Exp $";
#endif

#define P_SOCKET

#include <p_global.h>

#define MAX_SENDQ 1000

jmp_buf	alarmret;

/* gets a socketnode from the corresponding system socket number */

struct socketnodes *previous;

struct socketnodes *getpsocketbysock(int syssock)
{
    struct socketnodes *th;
    th=socketnode;
    previous=socketnode;
    while(th!=NULL)
    {
	if (th->sock!=NULL)
	{
	    if (th->sock->syssock==syssock) return th;
	}
	previous=th;
	th=th->next;
    }
    return NULL;
}

/* creates a socket */

int createsocket(int syssock,int type,int index,int(*constructor)(int),int(*constructed)(int),int(*errorhandler)(int,int),int(*handler)(int),int(*destructor)(int),int proto)
{
    struct psockett *th;
    struct socketnodes *lkm;
    int flags,ret;
    int lsock;
    time_t tm;
    time(&tm);
    pcontext;
    lsock=syssock;
    if(syssock!=0)
    {
	lkm=getpsocketbysock(lsock);
	if (lkm!=NULL) return lsock; /* already existent.. so why the hell... */
    } else 
    	lsock = socket (proto, SOCK_STREAM, IPPROTO_TCP);
    if(lsock<=0)
    {
	log(LOG_ERROR,-1,"Error Creating Socket");
	return 0x0;
    }
    flags = fcntl(lsock,F_GETFL,0);
    ret = fcntl(lsock,F_SETFL,flags | O_NONBLOCK);
    lkm=socketnode;
    while (lkm!=NULL)
    {
	if (lkm->next==NULL || lkm->sock==NULL)
	{
	    if(lkm->sock!=NULL)
	    {
		lkm->next=(struct socketnodes *) pmalloc(sizeof(struct socketnodes));
		lkm=lkm->next;
	    }
	    lkm->sock=(struct psockett *) pmalloc(sizeof(struct psockett));
	    lkm->next=NULL;
	    th=lkm->sock;
	    th->type=type;
	    th->protocol=proto;
	    th->flag=SOC_NOUSE;
	    th->syssock=lsock;
#ifdef CRYPT
	    th->encryption=SE_NOENC;
#endif
	    th->constructor=constructor;
	    th->constructed=constructed;
	    th->errorhandler=errorhandler;
	    th->handler=handler;
	    th->destructor=destructor;
	    th->commbuf=(char *)pmalloc(8192);
	    th->bytesin=0;
	    th->bytesout=0;
	    th->param=index;
	    strmncpy(th->since,ctime(&tm),sizeof(th->since));
	    break;
	}
	lkm=lkm->next;
    }
    if(lkm==NULL)
    {
	log(LOG_ERROR,-1,"CANT ALLOCATE SOCKETSPACE - SHUTTING DOWN");
	exit(0x0);
    }
    return lsock;
}

/* kill a socket. used instead of close. possibly called iterative */

int killsocket(int syssock)
{
    struct socketnodes *lkm;
    int(*caller)(int);
    int rc,i;
    lkm=getpsocketbysock(syssock);    
    if(lkm==NULL || lkm==socketnode) return 0x0;
    if(lkm->sock!=NULL)
    {
	if(lkm->sock->destructor!=NULL)
	{
	    caller=lkm->sock->destructor;
	    lkm->sock->destructor=NULL;
	    lkm->sock->errorhandler=NULL;
	    rc=(*caller)(lkm->sock->param);
	    lkm=getpsocketbysock(syssock); /* if we are destroyed.. */
	    if(lkm==NULL) return 0x0;
	}
	lkm->sock->serverstoned=0; /* would loop infinitely if */
	lkm->sock->serversocket=0;
        for(i=0;i<MAX_SENDQ;i++)
		flushsendq(lkm->sock->syssock,Q_FORCED);
	free(lkm->sock->commbuf);
	free(lkm->sock);
	previous->next=lkm->next;
	free(lkm);
    }
    shutdown(syssock,2);
    close(syssock);
    return 0x0;
}

/* conntectto - builds a connection to a host and port using a given vhost */

int rsock;

int connectto(int sockt,char *host,int port, char *vhost)
{
    int l, error;
    struct socketnodes *lkm;
#ifdef IPV6
    struct sockaddr_in6 sin6;
#endif
    struct sockaddr_in sin;
    struct hostent *he;
#ifdef IPV6
    char myhost[60];
    char hishost[60];
#else
    char myhost[15];
    char hishost[15];
#endif
    int flags, ret;
    pcontext;
    /* we got a server and a port */
    memset( &sin, 0, sizeof(sin));
#ifdef IPV6
    memset( &sin6, 0, sizeof(sin6));
#endif
    rsock = sockt;
    strcpy(myhost,"*");
    myhost[0]=0;
    hishost[0]=0;
    if (rsock < 1) return 0x0;
    lkm=getpsocketbysock(rsock);
    if(lkm==NULL)
	return 0x0;
    if (vhost!=NULL) {
#ifdef IPV6
	if(lkm->sock->protocol==AF_INET6)
	{
	    if(!setjmp(alarmret))
	    {
		alarm(10);
#ifdef SUNOS
		he=getipnodebyname(vhost,AF_INET6,AI_DEFAULT,&error_num);
#else
	        he=gethostbyname2(vhost,AF_INET6);
#endif
		alarm(0);
	    } else
		he=NULL;
	    if(he) {
		memcpy(&sin6.sin6_addr,he->h_addr,he->h_length);
		sin6.sin6_family=he->h_addrtype;
		inet_ntop(AF_INET6,&sin6,myhost,sizeof(myhost));
		if(bind(rsock, (struct sockaddr *)&sin6, sizeof(sin6)) <0)
		{
		    /* ? */
		}
	    }
	} else {
#endif
	    if(!setjmp(alarmret))
	    {
		alarm(10);
		he=gethostbyname(vhost);
		alarm(0);
	    } else
		he=NULL;
	    if(he) {
		memcpy(&sin.sin_addr,he->h_addr,he->h_length);
		sin.sin_family = he->h_addrtype;
		strmncpy(myhost,inet_ntoa(sin.sin_addr),sizeof(myhost));
		if(bind(rsock, (struct sockaddr *)&sin, sizeof(sin)) <0)
		{
		/* ? */
		}
	    }
#ifdef IPV6
	}
#endif
    }
    memset(&sin,0,sizeof(sin));
#ifdef IPV6
    memset( &sin6, 0, sizeof(sin6));
    if(lkm->sock->protocol==AF_INET6)
    {
	sin6.sin6_port = htons(port);
	if(!setjmp(alarmret))
	{
	    alarm(10);
#ifdef SUNOS
	    he=getipnodebyname(host,AF_INET6,AI_DEFAULT,&error_num);
#else
	    he=gethostbyname2(host,AF_INET6);
#endif
	    alarm(0);
	} else
	    he=NULL;
	if(!he)
	    return 0x0;
	sin6.sin6_family=he->h_addrtype;
	memcpy(&sin6.sin6_addr,he->h_addr,he->h_length);
	inet_ntop(AF_INET6,&sin6,hishost,sizeof(hishost));
	ret=connect(rsock,(struct sockaddr *)&sin6, sizeof(sin6));
    } else {
#endif
	sin.sin_port = htons(port);
	if(!setjmp(alarmret))
	{
	    alarm(10);
	    he=gethostbyname(host);
	    alarm(0);
	} else
	    he=NULL;
	if(!he)
	    return 0x0;
	sin.sin_family=he->h_addrtype;
	memcpy(&sin.sin_addr,he->h_addr,he->h_length);
	strmncpy(hishost,inet_ntoa(sin.sin_addr),sizeof(hishost));
	ret =connect(rsock, (struct sockaddr *)&sin, sizeof(sin));
#ifdef IPV6
    }
#endif
    if (ret < 0) {
        if (errno != EINPROGRESS && ret != -EINPROGRESS)
	{
	    killsocket(rsock);
	    return 0x0;
	}
    }
    if(lkm!=NULL)
    {
	lkm->sock->flag=SOC_SYN;
	lkm->sock->delay=0;
	if(socketnode->sock!=NULL)
	    lkm->sock->sport=socketnode->sock->sport;
	lkm->sock->dport=port;
	replace(myhost,'%',255);
	replace(hishost,'%',255);
	strmncpy(lkm->sock->source,myhost,sizeof(lkm->sock->source));
	strmncpy(lkm->sock->dest,hishost,sizeof(lkm->sock->dest));
    }
    return rsock;
}

int urgent=0;

/* flush the queue */

int flushsendq(int socket, int forced)
{
    struct socketnodes *lkm;
    struct sendqt *msq;
    lkm=getpsocketbysock(socket);
    if(lkm==NULL) return 0x0; /* no socket, no queue */
    if(lkm->sock->dataflow==SD_STREAM) return 0x0; /* binary sockets get no queues */
    msq=lkm->sock->sendq;
    if(msq==NULL) return 0x0; /* nothing to flush */
    if(lkm->sock->flag<SOC_CONN) return 0x0; /* not yet connected.. no flush */
    if(forced!=Q_FORCED)
    {
	if(msq->delay>0)
	{
	    msq->delay-=delayinc;
	    return 0x0;
	}
    }
    if(lkm->sock->serversocket==1 && lkm->sock->flag==SOC_CONN)
    {
	if(lkm->sock->serverstoned>0) return 0x0;
	if(lkm->sock->serverbytes+strlen(msq->data)>700)
	{
	    if(strlen(msq->data)<700)
	    {
		lkm->sock->serverstoned=20;
		if(lkm->sock->flag==SOC_CONN)
		    write(socket,"PING :p\r\n",9);
		return 0x0;		
	    }
	}
	lkm->sock->serverbytes+=strlen(msq->data);
    }
    urgent=1;
    if(lkm->sock->flag==SOC_CONN)
	writesock(socket,msq->data);
    free(msq->data);
    msq=msq->next;
    free(lkm->sock->sendq);
    lkm->sock->sendq=msq;
    lkm->sock->entrys--;
    return 0x0;
}

/* add data to a queue */

int addq(int socket, char *data, int sqdelay)
{
    struct socketnodes *lkm;
    struct sendqt *msq;
    lkm=getpsocketbysock(socket);
    if(lkm==NULL) /* no socket descriptor, URGENT sent */
    {
	urgent=1;
	return writesock(socket,data);
    }
    lkm->sock->entrys++;
    if(lkm->sock->entrys>MAX_SENDQ && lkm->sock->serverstoned==0)
	flushsendq(socket,Q_FORCED); /* too many entries -> flushing */
    if (lkm->sock->sendq==NULL)
    {
	lkm->sock->sendq=(struct sendqt *)pmalloc(sizeof(struct sendqt));
	msq=lkm->sock->sendq;
    } else {
	msq=lkm->sock->sendq;
	while(msq->next!=NULL) msq=msq->next;
	msq->next=(struct sendqt *)pmalloc(sizeof(struct sendqt));
	msq=msq->next;
    }
    msq->data=(char *)pmalloc(strlen(data)+2);
    msq->delay=sqdelay;
    strmncpy(msq->data,data,strlen(data)+1);
    return 0x0;
}

/* write data to a binary socket */

int writesock_STREAM(int socket, unsigned char *data, unsigned int size)
{
    struct socketnodes *lkm;
    lkm=getpsocketbysock(socket);
    if(lkm==NULL) return 0x0;
    if(lkm->sock->flag==SOC_CONN)
	write(socket,data,size);
    lkm->sock->bytesout+=size;
    return 0x0;
}

/* write data to a socket */

int writesock (int socket, char *data)
{
    static char buf[8200];
    static char kbuf[30];
    char *po;
    struct socketnodes *lkm;
    lkm=getpsocketbysock(socket);
    if(lkm==NULL) return 0x0;
    if(lkm->sock->flag<SOC_CONN) urgent=0;
    if(urgent==0 && lkm->sock->nowfds !=1)
    {
	addq(socket,data,0);
	return 0x0;
    }
    if (socket == 0) return -1;
    strmncpy(buf,data,sizeof(buf));
    po=strchr(buf,'\n');
    if (po == NULL) strcat(buf,"\r\n");
    if(po!=NULL)
    {
        po--;
        if (*po!='\r')
        {
    	    po++;
	    *po='\r';
	    po++;
	    *po='\n';
	    po++;
	    *po=0;
	}
    }
    errn=0;
    if(lkm!=NULL)
    {
	if(lkm->sock!=NULL)
	{
#ifdef CRYPT
	    if(lkm->sock->encryption==SE_ENC)
	    {
#ifdef BLOWFISH
		po=BLOW_stringencrypt(buf,lkm->sock->outcrkey);
		ap_snprintf(buf,sizeof(buf),"[B]%s\r\n",po);
#endif
#ifdef IDEA
		po=IDEA_stringencrypt(buf,lkm->sock->outcrkey);
		ap_snprintf(buf,sizeof(buf),"[I]%s\r\n",po);
#endif
		free(po);
	    }
#endif
	    lkm->sock->bytesout+=strlen(buf);
	}
    }
    if(urgent==1 || lkm->sock->nowfds == 1)
    {
	replace(buf,255,'%');
	if(lkm->sock->flag==SOC_CONN)
	    write(socket,buf,strlen(buf));
	lkm->sock->delay=300;
#ifdef CRYPT
	if(lkm->sock->encryption==SE_ENC)
	{
	    lkm->sock->keydelay+=delayinc;
	    if(lkm->sock->keydelay>15)
	    {
		strmncpy(kbuf,randstring(16),sizeof(kbuf));
		ap_snprintf(buf,sizeof(buf),"ENC %s\r\n",kbuf);
#ifdef BLOWFISH
		po=BLOW_stringencrypt(buf,lkm->sock->outcrkey);
		ap_snprintf(buf,sizeof(buf),"[B]%s\r\n",po);
#else
		po=IDEA_stringencrypt(buf,lkm->sock->outcrkey);
		ap_snprintf(buf,sizeof(buf),"[I]%s\r\n",po);
#endif
		if(lkm->sock->flag==SOC_CONN)
		    write(socket,buf,strlen(buf));
		strmncpy(lkm->sock->outcrkey,kbuf,sizeof(lkm->sock->outcrkey));
		free(po);
		lkm->sock->keydelay=0;
	    }
	}
#endif
	urgent=0;	
    }
    if (errn == 1) {
       /* heavy error on writing */
       return -1;
    }
    return 0x0;
}

/* urgent writes */

int writesock_URGENT (int socket, char *data)
{
    urgent=1;
    return writesock(socket,data);
}

/* write in a delay */

int writesock_DELAY (int socket, char *data, int delay)
{
    return addq(socket,data,delay);
}

/* write with format */

int 
ssnprintf(va_alist)
    va_dcl
{
    va_list va;
    int sock;
    char *format;
    static char buf[8192];
    va_start(va);
    sock=va_arg(va,int);
    format=va_arg(va,char *);
    ap_vsnprintf(buf,sizeof(buf),format,va);
    writesock(sock,buf);
    va_end(va);
    return strlen(buf);
}

/* define the protocol based on a given host */

int getprotocol(char *host)
{
#ifndef IPV6
    return AF_INET;
#else
    struct hostent *he;
    if(!setjmp(alarmret))
    {
	alarm(10);
#ifdef SUNOS
	he=getipnodebyname(host,AF_INET6,AI_DEFAULT,&error_num);
#else
	he=gethostbyname2(host,AF_INET6);
#endif
	alarm(0);
    } else
	he=NULL;
    if(!he)
    {
	return AF_INET; /* we check no resolve on IPv4 and assume it, if v6 does not work */
    }
    return AF_INET6;
#endif
}

/* recv from a socket */

int receivesock(struct psockett *sock)
{
    int ret=1,i;
    int sz=8191;
    int rc;
    char *br,*ebr;
    int kln;
    int esck;
    char *puk,*pt,*eh;
    static char buf[4096],kbuf[20];
    pcontext;
    sz-=sock->bytesread;
    ircbuf[0]=0;
    if(sz>0)
    {
	errno=0;
	ret=recv(sock->syssock,sock->commbuf+sock->bytesread,sz,0);
	if (ret>0) sock->bytesread+=ret;
	if (ret==-1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) return 1;
	if (ret<=0) return ret;
    } else {
	*ircbuf=0;
	return 0x1;
    }
    if (ret>0) sock->bytesin+=ret;
    if(sock->dataflow==SD_STREAM)
    {
	if(ret<=0 || ret>sizeof(ircbuf)) return ret;
	memcpy(&ircbuf,sock->commbuf,ret);
	sock->datalen=ret;
	sock->bytesread=0;
	if (sock->handler!=NULL)
	{
	    rc=(*sock->handler)(sock->param);
	}
	return ret;
    }
    esck=sock->syssock;
    br=strchr(sock->commbuf,'\n');
    if(br==NULL) br=strchr(sock->commbuf,10); /* nulline, ignore */
    while(br!=NULL && getpsocketbysock(esck)!=NULL && ret>0)
    {
    	br++; /* :) */
	memset(ircbuf,0x0,sizeof(ircbuf));
	memcpy((void *)ircbuf,(void *)sock->commbuf,(br-sock->commbuf));
	memcpy((void *)sock->commbuf,(void *)br,8192-((br-sock->commbuf)));
	sock->bytesread-=((br-sock->commbuf));
	memset((void *)sock->commbuf+sock->bytesread,0x0,(8191-sock->bytesread));
	replace(ircbuf,'%',255);
	ebr=strchr(ircbuf,'\r');
	if(ebr==NULL) ebr=strchr(ircbuf,'\n');
#ifdef CRYPT
	if(sock->encryption==SE_ENC)
	{
	    if(strstr(ircbuf,"[I]")==ircbuf)
	    {
		*ebr=0;
		br=IDEA_stringdecrypt(ircbuf+3,sock->incrkey);
		replace(br,'%',255);
		strmncpy(ircbuf,br,sizeof(ircbuf));
		free(br);
	    } else
	    if(strstr(ircbuf,"[B]")==ircbuf)
	    {
		*ebr=0;
		br=BLOW_stringdecrypt(ircbuf+3,sock->incrkey);
		replace(br,'%',255);
		strmncpy(ircbuf,br,sizeof(ircbuf));
		free(br);
	    } else {
		log(LOG_ERROR,-1,"Warning, got nonecnrypted Data encrypted socket #%d. Disconnecting.",sock->syssock);
		killsocket(sock->syssock);
		return -1;		
	    }
	}
	if(strstr(ircbuf,"ENC ")==ircbuf)
	{
	    if(sock->encryption!=SE_ENC)
	    {
		*ircbuf=0;
		return 1;
	    } else {
		ebr=strchr(ircbuf,'\r');
		if(ebr==0) ebr=strchr(ircbuf,'\n');
		if(ebr!=NULL) *ebr=0;
		ebr=ircbuf+4;
		strmncpy(sock->incrkey,ebr,sizeof(sock->incrkey));
		for (i=0;i<MAX_SENDQ;i++)
		    flushsendq(sock->syssock,Q_FORCED);
		*ircbuf=0;
		return 1;
	    }
	}
#endif
	esck=sock->syssock;
	if (sock->serversocket==1)
	{
	    pt=strchr(ircbuf,' ');
	    if(pt!=NULL)
	    {
		pt++;
		if(strstr(pt,"PONG ")==pt) /* received PONG, resetting stone */
		{
		    sock->serverstoned=0;
		    sock->serverbytes=0;
		    ircbuf[0]=0;
		}
	    }
	}
	if (sock->handler!=NULL && strlen(ircbuf)>1)
	{
	    rc=(*sock->handler)(sock->param);
	}
        if (getpsocketbysock(esck)==NULL) { ret=-1;break; }
	br=strchr(sock->commbuf,'\n');
	if(br==NULL) br=strchr(sock->commbuf,10);
    }
    return ret;
}

unsigned long oldsec=0;

/* central socketdriver event routine */

unsigned long socketdriver()
{
    fd_set rfds;
    fd_set wfds;
    struct socketnodes *th,*par;
    int rc,altsock;
    int fdscnt=0,wfdscnt=0;
    int sockit=0,sockat=9999,ret,noadv,ln,opt;
    int tt,optbuf;
    unsigned long sec;
    int sck;
    long otm;
    struct tm *xtm;
    struct socketnodes *lkm;
    static struct timeval tv;
    int nowf=0;
    time_t tm,em;
    /* checking the advancing timer */
    delayinc=0;
    time(&tm);
    if (tm!=oldsec) delayinc=1;
    oldsec=tm;
    th=socketnode;
    par=th;
    /* constructors / socket set / selects */
    FD_ZERO( &rfds);
    FD_ZERO( &wfds);
    while(th!=NULL)
    {
	rc=0;noadv=0;
	if(th->sock!=NULL)
	{
	    if(th->sock->serversocket)
	    {
		if(th->sock->serverstoned>0)
		{
		    th->sock->serverstoned-=delayinc;
		    /* if the socket was stoned, we just go on sending */
		    if(th->sock->serverstoned==0) th->sock->serverbytes=0;
		}
	    }
	    if (th->sock->syssock>sockit) sockit=th->sock->syssock;
	    if (th->sock->syssock<sockat) sockat=th->sock->syssock;
	    if (th->sock->flag==SOC_NOUSE)
	    {
		currentsocket=th;
		altsock=th->sock->syssock;
		if(th->sock->constructor!=NULL)
		    rc=(*th->sock->constructor)(th->sock->param);
		th=par->next;
		if(th!=NULL)
		{
		    if(altsock==th->sock->syssock)
			th->sock->flag=SOC_SYN;    
		    else
			noadv=1;
		} else
		    noadv=1;
	    } else {
		if (th->sock->flag==SOC_SYN || th->sock->flag==SOC_CONN)
		{
		    FD_SET(th->sock->syssock, &rfds);
		    fdscnt++;
		    if(th->sock->flag==SOC_SYN || (th->sock->sendq!=NULL && th->sock->serverstoned==0))
		    {
			FD_SET(th->sock->syssock, &wfds);
			wfdscnt++;
		    }
		}
	    }
	}
	if(noadv==0)
	{
	    par=th;
	    th=th->next;
	}
    }
    if(sockat>sockit) sockat=sockit;
    /* selecting */
    if (fdscnt==0) 
    {
	sleep(1);
	return 0x0;
    }
    pcontext;
    tv.tv_usec = 0;
    tv.tv_sec = 3;
    if(wfdscnt>0)
    	ln=select(sockit +1, &rfds, &wfds,NULL,&tv);
    else
    	ln=select(sockit +1, &rfds, NULL,NULL,&tv);
    time(&em);
    if(ln<=0) return em-tm;
    th=socketnode;
    par=th;
    /* reading, connecting done, errorhandling */
    for(sck=sockit;sck>=sockat;sck--)
    {
	noadv=0;
	th=getpsocketbysock(sck);
	if(th!=NULL)
	{
	    currentsocket=th;
	    nowf=th->sock->nowfds;
	    if(th->sock->flag==SOC_SYN)
	    {
		if(FD_ISSET(sck,&rfds) || FD_ISSET(sck,&wfds))
		{
    		    opt=sizeof(optbuf);
    		    if (getsockopt(th->sock->syssock, SOL_SOCKET, SO_ERROR, &optbuf,&opt) >=0) 
		    {
			if(optbuf==0)
			{
			    /* connected */
			    th->sock->flag=SOC_CONN;
			    th->sock->delay=300;
			    if(th->sock->constructed!=NULL)
			    {
				pcontext;
				rc=(*th->sock->constructed)(th->sock->param);
				pcontext;
			    }
			}
		    } else {
		        optbuf==-1;
		    }
		    if(optbuf!=0) {
			/* error */
			th->sock->flag=SOC_ERROR;
			altsock=th->sock->syssock;
			if(th->sock->errorhandler!=NULL)
			{
			    pcontext;
			    rc=(*th->sock->errorhandler)(th->sock->param,SERR_REFUSED);
			    pcontext;
			}
			if(getpsocketbysock(altsock)!=NULL)
			{
			    killsocket(th->sock->syssock);
			}
		    }	
		} else {
		    if(th->sock->flag==SOC_SYN && th->sock->type==ST_CONNECT)
		    {
			th->sock->delay+=delayinc;
			if(th->sock->delay>SOC_TIMEOUT)
			{
			    altsock=th->sock->syssock;
			    /* timed out, terminating socket and calling error */
			    if(th->sock->errorhandler!=NULL)
			    {
				pcontext;
				rc=(*th->sock->errorhandler)(th->sock->param,SERR_TIMEOUT);
				pcontext;
			    }
			    if(getpsocketbysock(altsock)!=NULL)
			    {
				killsocket(th->sock->syssock);
			    }
			}
		    }
		}
	    } else 
	    if (th->sock->flag==SOC_CONN) {
		noadv=0;
		if(FD_ISSET(sck,&rfds))
		{
		    if (th->sock->flag==SOC_CONN)
		    {
			altsock=th->sock->syssock;
			rc=receivesock(th->sock);
			th=par->next;
			if (getpsocketbysock(altsock)==NULL) rc=-1;
			if (rc<=0)
			{
			    killsocket(altsock);
			    noadv=1;
			}
		    }
		} else {
		    /* disconnection sensing at outgoing sockets */
		    if(th->sock->flag==SOC_CONN && th->sock->type==ST_CONNECT && th->sock->dataflow!=SD_STREAM)
		    {
			th->sock->delay-=delayinc;
			if(th->sock->delay<=0)
			{
			    writesock_URGENT(th->sock->syssock,"\r\n");
			    th->sock->delay=300;
			}
		    }
		}
		if(FD_ISSET(sck,&wfds) && noadv==0 && th->sock->serverstoned==0)
		{
		    flushsendq(sck,Q_NEXT);
		}
	    }
	}
    }
    return em-tm;
}

