GOOGLE
/************************************************************************
* psybnc2.3, 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.3 2001/07/13 02:03:00 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,int ssl)
{
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,gettxt(790));
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 HAVE_SSL
th->ssl=ssl;
#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,gettxt(791));
exit(0x0);
}
return lsock;
}
/* kill a socket. used instead of close. possibly called iterative */
int killsocket(int syssock)
{
struct socketnodes *lkm;
struct socketnodes *siccur=currentsocket;
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;
currentsocket=lkm;
rc=(*caller)(lkm->sock->param);
currentsocket=siccur;
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);
#ifdef HAVE_SSL
if(lkm->sock->ssl==SSL_ON)
{
if(lkm->sock->sslfd!=0)
{
SSL_shutdown(lkm->sock->sslfd);
SSL_free(lkm->sock->sslfd);
lkm->sock->sslfd=0;
}
}
#endif
free(lkm->sock->commbuf);
free(lkm->sock);
previous->next=lkm->next;
free(lkm);
}
shutdown(syssock,2);
close(syssock);
return 0x0;
}
void gotalarm(int sig)
{
longjmp(alarmret,0x0);
}
/* create a single listener */
int createlistener(char *host,int listenport,int proto,int pending, int(*listenhandler)(int), int(*errorhandler)(int,int), int(*datahandler)(int), int(*closehandler)(int))
{
#ifdef IPV6
struct sockaddr_in6 listen_sa6;
#endif
struct hostent *he;
struct sockaddr_in listen_sa;
struct socketnodes *lkm;
int sopts = 1;
int issl=SSL_OFF;
char vsl[10];
int opt;
char *ho;
int listensocket;
int rc;
const char *pt;
vsl[0]=0;
#ifdef HAVE_SSL
if(strstr(host,"S=")==host)
{
ho=host+2;
issl=SSL_ON;
strcpy(vsl,gettxt(792));
} else
#endif
ho=host;
listensocket = socket (proto, SOCK_STREAM, IPPROTO_TCP);
listensocket = createsocket(listensocket,ST_LISTEN,0,NULL,listenhandler,errorhandler,datahandler,closehandler,proto,issl);
lkm=getpsocketbysock(listensocket);
if(lkm==NULL || listensocket==0)
{
if(pending==0)
log(LOG_ERROR,-1,gettxt(793),ho,listenport,vsl);
return 0;
}
strmncpy(lkm->sock->source,host,sizeof(lkm->sock->source));
strcpy(lkm->sock->dest,"*");
lkm->sock->sport=listenport;
lkm->sock->dport=0;
lkm->sock->flag=SOC_SYN; /* we are open */
highestsocket = listensocket;
opt=sizeof(int);
setsockopt (listensocket, SOL_SOCKET, SO_REUSEADDR, &sopts, opt);
#ifdef IPV6
if(proto==AF_INET6)
memset (&listen_sa6, 0, sizeof (struct sockaddr_in6));
else
#endif
memset (&listen_sa, 0, sizeof (struct sockaddr_in));
#ifdef IPV6
if(proto==AF_INET6)
{
listen_sa6.sin6_port = htons (listenport);
#ifdef SUNOS
he=getipnodebyname(ho,AF_INET6,AI_DEFAULT,&error_num);
#else
he=gethostbyname2(ho,AF_INET6);
#endif
if(*host=='*')
{
memcpy(&listen_sa6.sin6_addr,&in6addr_any,16);
listen_sa6.sin6_family=AF_INET6;
} else {
if(!he)
{
killsocket(listensocket);
if(pending==0)
log(LOG_ERROR,-1,gettxt(794),ho,listenport,vsl);
return 0x0;
}
listen_sa6.sin6_family=he->h_addrtype;
memcpy(&listen_sa6.sin6_addr,he->h_addr,he->h_length);
}
pt=inet_ntop(AF_INET6,&listen_sa6,lkm->sock->source,sizeof(lkm->sock->source));
/* ipv6 dcc not yet specified
if(dcc6host[0]==0) strmncpy(dcc6host,lkm->sock->source,sizeof(dcc6host)); */
} else {
#endif
listen_sa.sin_port = htons (listenport);
if(*ho=='*')
{
listen_sa.sin_addr.s_addr=htonl(INADDR_ANY);
listen_sa.sin_family=AF_INET;
} else {
he=gethostbyname(ho);
if(!he)
{
killsocket(listensocket);
if(pending==0)
log(LOG_ERROR,-1,gettxt(795),host,listenport,vsl);
return 0x0;
}
listen_sa.sin_family=he->h_addrtype;
memcpy(&listen_sa.sin_addr,he->h_addr,he->h_length);
}
strmncpy(lkm->sock->source,inet_ntoa(listen_sa.sin_addr),sizeof(lkm->sock->source));
if(dcchost[0]==0)
strmncpy(dcchost,lkm->sock->source,sizeof(dcchost));
#ifdef IPV6
}
if(proto==AF_INET6)
rc=bind(listensocket, (struct sockaddr *) &listen_sa6, sizeof(listen_sa6));
else
#endif
rc=bind(listensocket, (struct sockaddr *) &listen_sa, sizeof (struct sockaddr_in));
if (rc < 0)
{
killsocket(listensocket);
if(pending==0)
log(LOG_ERROR,-1,gettxt(796),ho,listenport,vsl);
return 0; /* cannot create socket */
}
if ((listen (listensocket, 1)) == -1)
{
killsocket(listensocket);
if(pending==0)
log(LOG_ERROR,-1,gettxt(797),ho,listenport,vsl);
return 0; /* cannot create socket */
}
if(pending==0)
{
printf(gettxt(798),lkm->sock->source,listenport,vsl);
log(LOG_INFO,-1,gettxt(799),lkm->sock->source,listenport,vsl);
}
return listensocket;
}
/* 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;
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)
{
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
#ifdef SUNOS
he=getipnodebyname(vhost,AF_INET6,AI_DEFAULT,&error_num);
#else
he=gethostbyname2(vhost,AF_INET6);
#endif
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
he=NULL;
signal(SIGALRM,SIG_IGN);
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
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
he=gethostbyname(vhost);
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
he=NULL;
signal(SIGALRM,SIG_IGN);
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);
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
#ifdef SUNOS
he=getipnodebyname(host,AF_INET6,AI_DEFAULT,&error_num);
#else
he=gethostbyname2(host,AF_INET6);
#endif
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
he=NULL;
signal(SIGALRM,SIG_IGN);
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);
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
he=gethostbyname(host);
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
he=NULL;
signal(SIGALRM,SIG_IGN);
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 */
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)
{
#ifdef HAVE_SSL
if(lkm->sock->ssl==SSL_ON)
SSL_write(lkm->sock->sslfd,gettxt(800),9);
else
#endif
write(socket,gettxt(801),9);
}
return 0x0;
}
}
lkm->sock->serverbytes+=strlen(msq->data);
}
urgent=1;
if(lkm->sock->dataflow==SD_STREAM)
writesock_STREAM(socket,msq->data,msq->len);
else
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, size_t len, 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(len+2);
msq->len=len;
msq->delay=sqdelay;
memcpy(msq->data,data,len);
return 0x0;
}
/* write data to a binary socket */
int writesock_STREAM(int socket, unsigned char *data, unsigned int size)
{
int rc;
struct socketnodes *lkm;
lkm=getpsocketbysock(socket);
if(lkm==NULL) return 0x0;
if(urgent!=0)
{
addq(socket,data,size,0);
return 0x0;
} else
if(lkm->sock->flag==SOC_CONN)
{
#ifdef HAVE_SSL
if(lkm->sock->ssl==SSL_ON)
{
rc=SSL_write(lkm->sock->sslfd,data,size);
switch(SSL_get_error(lkm->sock->sslfd,rc))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
/* back onto the queue */
addq(lkm->sock->syssock,data,size,0);
return 0x0;
default:
return -1;
}
} else
#endif
write(socket,data,size);
}
lkm->sock->bytesout+=size;
urgent=0;
return 0x0;
}
/* write data to a socket */
int writesock (int socket, char *data)
{
static char buf[8200];
char sbuf[8200];
static char kbuf[30];
char *po;
int rc;
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,strlen(data)+1,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)
{
lkm->sock->bytesout+=strlen(buf);
}
}
if(urgent==1 || lkm->sock->nowfds == 1)
{
replace(buf,255,'%');
if(lkm->sock->flag==SOC_CONN)
{
#ifdef HAVE_SSL
if(lkm->sock->ssl==SSL_ON)
{
strcpy(sbuf,buf);
rc=SSL_write(lkm->sock->sslfd,sbuf,strlen(sbuf));
switch(SSL_get_error(lkm->sock->sslfd,rc))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
/* put it back on the queue, dont block here */
addq(lkm->sock->syssock,sbuf,strlen(sbuf)+1,0);
break;
default:
break;
}
} else
#endif
write(socket,buf,strlen(buf));
}
lkm->sock->delay=300;
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,strlen(data)+1,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;
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
#ifdef SUNOS
he=getipnodebyname(host,AF_INET6,AI_DEFAULT,&error_num);
#else
he=gethostbyname2(host,AF_INET6);
#endif
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
he=NULL;
signal(SIGALRM,SIG_IGN);
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;
#ifdef HAVE_SSL
if(sock->ssl==SSL_ON)
{
if(sock->sslfd==NULL)
ret=0;
else
ret=SSL_read((SSL *)sock->sslfd,sock->commbuf+sock->bytesread,sz);
if(ret==-1 && (rc = SSL_get_error((SSL *)sock->sslfd,ret)) == SSL_ERROR_WANT_READ)
{
*ircbuf=0;
return 1;
}
if(ret<=0) return ret;
sock->bytesread+=ret;
} else {
#endif
ret=recv(sock->syssock,sock->commbuf+sock->bytesread,sz,0);
if (ret>0) sock->bytesread+=ret;
if (ret==-1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) { *ircbuf=0; return 1; }
if (ret<=0) return ret;
#ifdef HAVE_SSL
}
#endif
} else {
*ircbuf=0;
return 0x1;
}
if (ret>0) sock->bytesin+=ret;
if(sock->dataflow==SD_STREAM)
{
if(ret<=0 || ret>sizeof(ircbuf)) return 1;
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');
esck=sock->syssock;
if (sock->serversocket==1)
{
pt=strchr(ircbuf,' ');
if(pt!=NULL)
{
pt++;
if(strstr(pt,gettxt(802))==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;
int socket_connect()
{
int ern,rc;
ern=0;
#ifdef HAVE_SSL
if(currentsocket->sock->ssl==SSL_ON && currentsocket->sock->sslfd!=NULL)
{
rc=SSL_set_fd(currentsocket->sock->sslfd,currentsocket->sock->syssock);
if(rc==-1)
{
ern=SSL_get_error((SSL *)currentsocket->sock->sslfd,rc);
if(ern!=SSL_ERROR_WANT_READ && ern!=SSL_ERROR_WANT_WRITE && ern!=SSL_ERROR_NONE)
{
log(LOG_ERROR,-1,gettxt(803),currentsocket->sock->syssock,currentsocket->sock->param);
ern=-1;
}
else
ern=0;
}
if(ern==0)
{
/* blocking ? i hope not */
SSL_set_connect_state(currentsocket->sock->sslfd);
rc=SSL_connect(currentsocket->sock->sslfd);
if(rc==-1)
{
ern=SSL_get_error((SSL *)currentsocket->sock->sslfd,rc);
if(ern!=SSL_ERROR_WANT_READ && ern!=SSL_ERROR_WANT_WRITE && ern!=SSL_ERROR_NONE)
{
log(LOG_ERROR,-1,gettxt(804),currentsocket->sock->syssock,currentsocket->sock->param);
ern=-1;
}
else
ern=0;
}
}
}
#endif
return ern;
}
#ifdef HAVE_SSL
char sgcert[1024];
char *sslgetcert(SSL *ssls)
{
struct X509 *x5r;
char *str;
x5r=(struct X509 *)SSL_get_peer_certificate((SSL *)ssls);
if(x5r!=NULL)
{
str=X509_NAME_oneline(X509_get_subject_name((X509 *)x5r),0,0);
if(str!=NULL)
{
strmncpy(sgcert,str,sizeof(sgcert));
free(str);
str=X509_NAME_oneline(X509_get_issuer_name((X509 *)x5r),0,0);
if(str!=NULL)
{
strncat(sgcert," ",sizeof(sgcert));
sgcert[sizeof(sgcert)-1]=0;
strncat(sgcert,str,sizeof(sgcert));
sgcert[sizeof(sgcert)-1]=0;
free(str);
str=sgcert;
} else
str=NULL; /* no issuer */
} else
str=NULL; /* no subject */
X509_free((X509 *)x5r);
} else
str=NULL; /* no cert. */
return str;
}
/* check the cert
* logitem should be "Link #n" or "User FooBar"
*/
int sslcheckcert(int socket, char *cert, char *logitem)
{
struct socketnodes *ps;
char *ccert;
int rc;
ps=getpsocketbysock(socket);
if(ps!=NULL)
{
if(ps->sock->sslfd!=NULL)
{
ccert=sslgetcert(ps->sock->sslfd);
if(ccert==NULL)
{
if(SSLSEC==0)
{
log(LOG_INFO,-1,gettxt(805),logitem);
rc=1;
} else {
log(LOG_ERROR,-1,gettxt(806),logitem);
rc=0;
}
} else {
if(strmcmp(ccert,cert))
{
log(LOG_INFO,-1,gettxt(807),logitem);
rc=1;
} else {
if(SSLSEC==0)
{
log(LOG_INFO,-1,gettxt(810),logitem,ccert);
rc=1;
}
else
{
log(LOG_INFO,-1,gettxt(811),logitem,ccert);
rc=0;
}
}
}
} else
rc=-1;
} else
rc=-1;
return rc;
}
#endif
/* accept incoming call on listener */
int p_accept( int lsock)
{
#ifdef IPV6
struct sockaddr_in6 addr6;
#endif
struct sockaddr_in addr;
struct socketnodes *lkm;
int tm;
int str;
pcontext;
lkm=getpsocketbysock(lsock);
if(lkm==NULL)
return -1;
#ifdef IPV6
if(lkm->sock->protocol==AF_INET6)
{
tm = sizeof(addr6);
str = accept(lsock, ( struct sockaddr *)&addr6, &tm);
if(str==-1) return -1;
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
hostinfo = gethostbyaddr( (char * )&addr6.sin6_addr.s6_addr, 16, AF_INET6);
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
hostinfo=NULL;
signal(SIGALRM,SIG_IGN);
if(hostinfo)
strmncpy(accepthost,hostinfo->h_name,sizeof(accepthost));
else
inet_ntop(AF_INET6,&addr6,accepthost,sizeof(accepthost));
inet_ntop(AF_INET6,&addr6,acceptip,sizeof(acceptip));
acceptport=ntohs(addr6.sin6_port);
}
else
#endif
{
tm = sizeof(addr);
str = accept(lsock, ( struct sockaddr * )&addr, &tm);
if (str==-1) return -1;
signal(SIGALRM,gotalarm);
if(setjmp(alarmret)==0x0)
{
alarm(10);
hostinfo = gethostbyaddr( ( char * )&addr.sin_addr.s_addr, sizeof( struct in_addr), AF_INET);
signal(SIGALRM,SIG_IGN);
alarm(0);
} else
hostinfo=NULL;
signal(SIGALRM,SIG_IGN);
if (hostinfo)
strmncpy(accepthost,hostinfo->h_name,sizeof(accepthost));
else
strmncpy(accepthost,inet_ntoa( addr.sin_addr ),sizeof(accepthost));
strmncpy(acceptip,inet_ntoa(addr.sin_addr),sizeof(acceptip));
acceptport=ntohs(addr.sin_port);
}
return str;
}
int socket_accept()
{
int ret,rc;
int asocket;
int npeer;
int issl=0;
int listensocket;
int p_proto;
#ifdef HAVE_SSL
int sslerr;
char sebuf[1000];
#endif
pcontext;
p_proto=currentsocket->sock->protocol;
listensocket=currentsocket->sock->syssock;
asocket = p_accept(listensocket);
pcontext;
mastersocket=currentsocket;
if(mastersocket!=NULL)
{
mastersocket->sock->flag=SOC_SYN; /* resetting the listener to listen again */
#ifdef HAVE_SSL
issl=mastersocket->sock->ssl; /* inherit ssl flag */
#endif
} else {
log(LOG_ERROR,-1,gettxt(812));
shutdown(asocket,2);
close(asocket);
return -1;
}
if(checkhostallows(acceptip)==-1 && checkhostallows(accepthost)==-1)
{
log(LOG_ERROR,-1,gettxt(813),accepthost);
shutdown(asocket,2);
close(asocket);
return -1;
}
if(asocket<=0) return -1;
log(LOG_WARNING,-1,gettxt(814),accepthost);
/* inherit handlers */
asocket=createsocket(asocket,ST_LISTEN,0,NULL,NULL,mastersocket->sock->errorhandler,mastersocket->sock->handler,mastersocket->sock->destructor,p_proto,issl);
pcontext;
if (asocket==-1) return -1;
currentsocket=getpsocketbysock(asocket);
#ifdef HAVE_SSL
if(currentsocket!=NULL && mastersocket!=NULL) /* inherit ssl-setup */
{
currentsocket->sock->ssl=mastersocket->sock->ssl;
currentsocket->sock->sslfd=mastersocket->sock->sslfd;
mastersocket->sock->sslfd=NULL;
if(currentsocket->sock->ssl==SSL_ON && currentsocket->sock->sslfd!=NULL)
{
SSL_set_fd(currentsocket->sock->sslfd,asocket);
SSL_set_accept_state(currentsocket->sock->sslfd);
rc=SSL_accept(currentsocket->sock->sslfd);
if(rc==-1)
{
rc=SSL_get_error(currentsocket->sock->sslfd,rc);
switch(rc)
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
break;
default:
ERR_error_string(rc,sebuf);
log(LOG_ERROR,-1,gettxt(815),accepthost,sebuf);
killsocket(asocket);
return -1;
}
}
log(LOG_INFO,-1,gettxt(816),accepthost);
}
}
#endif
strmncpy(currentsocket->sock->source,acceptip,sizeof(currentsocket->sock->source));
strmncpy(currentsocket->sock->dest,mastersocket->sock->source,sizeof(currentsocket->sock->dest));
currentsocket->sock->param=mastersocket->sock->param;
currentsocket->sock->sport=acceptport;
currentsocket->sock->dport=mastersocket->sock->sport;
currentsocket->sock->flag=SOC_CONN;
return 0x0;
}
/* central socketdriver event routine */
unsigned long socketdriver()
{
fd_set rfds;
fd_set wfds;
struct socketnodes *th,*par;
int rc,altsock;
int goterr=0;
int fdscnt=0,wfdscnt=0;
int sockit=0,sockat=9999,ret,noadv,ln,opt;
int tt,optbuf;
unsigned long sec;
int sck,ssck;
#ifdef HAVE_SSL
SSL_CIPHER *c;
int bits;
#endif
long otm;
struct tm *xtm;
int issl=SSL_OFF;
int ssl_fd;
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 && th->sock->syssock>0)
{
#ifdef HAVE_SSL
if(th->sock->ssl==SSL_ON && th->sock->sslfd!=NULL)
{
ssl_fd=SSL_get_fd(th->sock->sslfd);
if(ssl_fd>0)
{
if(ssl_fd<sockat) sockat=ssl_fd;
if(ssl_fd>sockit) sockit=ssl_fd;
FD_SET(ssl_fd,&rfds);
}
} else
#endif
FD_SET(th->sock->syssock, &rfds);
fdscnt++;
if(th->sock->flag==SOC_SYN || (th->sock->sendq!=NULL && th->sock->serverstoned==0))
{
#ifdef HAVE_SSL
if(th->sock->ssl==SSL_ON && th->sock->sslfd!=NULL)
{
ssl_fd=SSL_get_fd(th->sock->sslfd);
if(ssl_fd>0)
{
if(ssl_fd<sockat) sockat=ssl_fd;
if(ssl_fd>sockit) sockit=ssl_fd;
FD_SET(ssl_fd,&wfds);
}
} else
#endif
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;
if(sockit<=0) sockit=1;
/* reading, connecting done, errorhandling */
for(sck=sockit;sck>=sockat;sck--)
{
noadv=0;
ssck=sck;
th=getpsocketbysock(sck);
if(th!=NULL)
{
currentsocket=th;
#ifdef HAVE_SSL
if(th->sock->ssl==SSL_ON && th->sock->sslfd!=NULL)
ssck=SSL_get_fd(th->sock->sslfd);
if(ssck<=0) ssck=sck;
#endif
altsock=ssck;
nowf=th->sock->nowfds;
if(th->sock->flag==SOC_SYN)
{
if(FD_ISSET(ssck,&rfds) || FD_ISSET(ssck,&wfds))
{
opt=sizeof(optbuf);
if (getsockopt(ssck, SOL_SOCKET, SO_ERROR, &optbuf,&opt) >=0)
{
if(optbuf==0)
{
/* connected */
altsock=th->sock->syssock;
#ifdef HAVE_SSL
if(th->sock->ssl==SSL_ON)
{
if(th->sock->type==ST_CONNECT)
th->sock->sslfd=SSL_new(clnctx);
else
th->sock->sslfd=SSL_new(srvctx);
if(th->sock->sslfd==0)
{
log(LOG_ERROR,-1,gettxt(817),th->sock->syssock,th->sock->param);
optbuf=-1;
}
}
#endif
if(optbuf==0)
{
if(th->sock->type==ST_CONNECT)
rc=socket_connect();
else
rc=socket_accept();
if(rc==0)
{
if(th->sock->type==ST_CONNECT)
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=getpsocketbysock(altsock);
if(th!=NULL)
{
if(th->sock->errorhandler!=NULL)
{
pcontext;
rc=(*th->sock->errorhandler)(th->sock->param,SERR_REFUSED);
pcontext;
}
goterr=1;
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;
}
goterr=1;
if(getpsocketbysock(altsock)!=NULL)
{
killsocket(th->sock->syssock);
}
}
}
}
} else
if (th->sock->flag==SOC_CONN) {
noadv=0;
if(FD_ISSET(ssck,&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(ssck,&wfds) && noadv==0 && th->sock->serverstoned==0)
{
#ifdef HAVE_SSL
if(th->sock->ssl==SSL_OFF)
flushsendq(sck,Q_NEXT);
else
if(th->sock->ssl==SSL_ON && th->sock->sslfd!=NULL)
{
c=SSL_get_current_cipher(th->sock->sslfd);
SSL_CIPHER_get_bits(c,&bits);
if(bits!=0) /* if handshake is done.. */
flushsendq(sck,Q_NEXT);
}
#else
flushsendq(sck,Q_NEXT);
#endif
}
}
}
if (goterr==1) break;
}
return em-tm;
}
Copyright © 1945 -