mirror of
https://github.com/EnergyMech/energymech.git
synced 2025-12-29 16:14:43 +00:00
Initial Import
This commit is contained in:
674
src/socket.c
Normal file
674
src/socket.c
Normal file
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
|
||||
EnergyMech, IRC bot software
|
||||
Parts Copyright (c) 1997-2009 proton
|
||||
|
||||
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 2 of the License, 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.
|
||||
|
||||
*/
|
||||
#define SOCKET_C
|
||||
#include "config.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "structs.h"
|
||||
#include "global.h"
|
||||
#include "h.h"
|
||||
|
||||
#ifndef RAWDNS
|
||||
|
||||
/*
|
||||
* only include this hack if rawdns isnt compiled in
|
||||
*/
|
||||
ulong get_ip(const char *host)
|
||||
{
|
||||
struct hostent *he;
|
||||
ulong ip;
|
||||
|
||||
if ((ip = inet_addr(host)) == INADDR_NONE)
|
||||
{
|
||||
if ((he = gethostbyname(host)) == NULL)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("(get_ip) unable to resolve %s\n",host);
|
||||
#endif /* DEBUG */
|
||||
return(-1);
|
||||
}
|
||||
ip = (ulong)((struct in_addr*)he->h_addr)->s_addr;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
debug("(get_ip) %s -> %s\n",host,inet_ntoa(*((struct in_addr*)&ip)));
|
||||
#endif /* DEBUG */
|
||||
return(ip);
|
||||
}
|
||||
|
||||
#endif /* ! RAWDNS */
|
||||
|
||||
/*
|
||||
* some default code for socket flags
|
||||
*/
|
||||
void SockFlags(int fd)
|
||||
{
|
||||
#ifdef ASSUME_SOCKOPTS
|
||||
fcntl(fd,F_SETFL,O_NONBLOCK|O_RDWR);
|
||||
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
||||
#else /* not ASSUME_SOCKOPTS */
|
||||
fcntl(fd,F_SETFL,O_NONBLOCK | fcntl(fd,F_GETFL));
|
||||
fcntl(fd,F_SETFD,FD_CLOEXEC | fcntl(fd,F_GETFD));
|
||||
#endif /* ASSUME_SOCKOPTS */
|
||||
}
|
||||
|
||||
int SockOpts(void)
|
||||
{
|
||||
struct { int onoff; int linger; } parm;
|
||||
int s;
|
||||
|
||||
if ((s = socket(AF_INET,SOCK_STREAM,0)) < 0)
|
||||
return(-1);
|
||||
|
||||
parm.onoff = parm.linger = 0;
|
||||
setsockopt(s,SOL_SOCKET,SO_LINGER,(char*)&parm,sizeof(parm));
|
||||
parm.onoff++;
|
||||
setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char*)&parm.onoff,sizeof(int));
|
||||
|
||||
SockFlags(s);
|
||||
|
||||
return(s);
|
||||
}
|
||||
|
||||
int SockListener(int port)
|
||||
{
|
||||
struct sockaddr_in sai;
|
||||
int s;
|
||||
|
||||
if ((s = SockOpts()) < 0)
|
||||
return(-1);
|
||||
memset((char*)&sai,0,sizeof(sai));
|
||||
sai.sin_family = AF_INET;
|
||||
sai.sin_addr.s_addr = INADDR_ANY;
|
||||
sai.sin_port = htons(port);
|
||||
if ((bind(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) || (listen(s,1) < 0))
|
||||
{
|
||||
close(s);
|
||||
return(-1);
|
||||
}
|
||||
return(s);
|
||||
}
|
||||
|
||||
#ifdef RAWDNS
|
||||
#define get_ip rawdns_get_ip
|
||||
#endif /* RAWDNS */
|
||||
|
||||
int SockConnect(char *host, int port, int use_vhost)
|
||||
{
|
||||
struct sockaddr_in sai;
|
||||
int s;
|
||||
#ifdef IDWRAP
|
||||
char *id,identfile[64];
|
||||
int t = FALSE;
|
||||
#endif /* IDWRAP */
|
||||
|
||||
#ifdef DEBUG
|
||||
debug("(SockConnect) %s %i%s\n",nullstr(host),port,(use_vhost) ? " [VHOST]" : "");
|
||||
#endif /* DEBUG */
|
||||
|
||||
if ((s = SockOpts()) < 0)
|
||||
return(-1);
|
||||
|
||||
memset((char*)&sai,0,sizeof(sai));
|
||||
sai.sin_family = AF_INET;
|
||||
|
||||
/*
|
||||
* special case, BOUNCE feature may call SockConnect()
|
||||
* to create the IDWRAP symlink, using special use_vhost value == 2
|
||||
*/
|
||||
#if defined(BOUNCE) && defined(IDWRAP)
|
||||
if ((use_vhost == TRUE)
|
||||
#else /* not ... */
|
||||
if (use_vhost
|
||||
#endif /* ... */
|
||||
&& ((current->vhost_type & VH_IPALIAS_FAIL) == 0)
|
||||
&& current->setting[STR_VIRTUAL].str_var)
|
||||
{
|
||||
current->vhost_type |= VH_IPALIAS_BOTH;
|
||||
if ((sai.sin_addr.s_addr = get_ip(current->setting[STR_VIRTUAL].str_var)) != -1)
|
||||
{
|
||||
if (bind(s,(struct sockaddr *)&sai,sizeof(sai)) >= 0)
|
||||
{
|
||||
current->vhost_type &= ~(VH_IPALIAS_FAIL|VH_WINGATE);
|
||||
#ifdef WINGATE
|
||||
use_vhost++;
|
||||
#endif /* WINGATE */
|
||||
#ifdef IDWRAP
|
||||
t = TRUE;
|
||||
#endif /* IDWRAP */
|
||||
#ifdef DEBUG
|
||||
debug("(SockConnect) IP Alias virtual host bound OK\n");
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef IDWRAP
|
||||
/*
|
||||
* do a blank bind to get a port number
|
||||
*/
|
||||
if (!t)
|
||||
{
|
||||
sai.sin_addr.s_addr = INADDR_ANY;
|
||||
bind(s,(struct sockaddr *)&sai,sizeof(sai));
|
||||
}
|
||||
#endif /* IDWRAP */
|
||||
|
||||
memset((char*)&sai,0,sizeof(sai));
|
||||
sai.sin_family = AF_INET;
|
||||
|
||||
#ifdef WINGATE
|
||||
/*
|
||||
* bouncer connects (WinGate, ...)
|
||||
*/
|
||||
if ((use_vhost == TRUE) && ((current->vhost_type & VH_WINGATE_FAIL) == 0)
|
||||
&& current->setting[STR_WINGATE].str_var)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("(SockConnect) Trying wingate @ %s:%i\n",
|
||||
current->setting[STR_WINGATE].str_var,
|
||||
current->setting[INT_WINGPORT].int_var);
|
||||
#endif /* DEBUG */
|
||||
current->vhost_type |= VH_WINGATE_BOTH;
|
||||
sai.sin_port = htons(current->setting[INT_WINGPORT].int_var);
|
||||
if ((sai.sin_addr.s_addr = get_ip(current->setting[STR_WINGATE].str_var)) == -1)
|
||||
{
|
||||
close(s);
|
||||
return(-1);
|
||||
}
|
||||
current->vhost_type &= ~(VH_WINGATE_FAIL|VH_IPALIAS);
|
||||
#ifdef DEBUG
|
||||
debug("(SockConnect) WINGATE host resolved OK\n");
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
else
|
||||
#endif /* WINGATE */
|
||||
{
|
||||
/*
|
||||
* Normal connect, no bounces...
|
||||
*/
|
||||
#ifdef IDWRAP
|
||||
if (use_vhost)
|
||||
{
|
||||
t = sizeof(sai);
|
||||
if (getsockname(s,(struct sockaddr*)&sai,&t) == 0)
|
||||
{
|
||||
if (current->identfile)
|
||||
Free((char**)¤t->identfile);
|
||||
sprintf(identfile,IDWRAP_PATH "%i.%i",ntohs(sai.sin_port),port);
|
||||
id = current->setting[STR_IDENT].str_var;
|
||||
if (symlink((id) ? id : BOTLOGIN,identfile) == 0)
|
||||
{
|
||||
set_mallocdoer(SockConnect);
|
||||
current->identfile = Strdup(identfile);
|
||||
#ifdef DEBUG
|
||||
debug("(SockConnect) symlink: %s -> %s\n",identfile,(id) ? id : BOTLOGIN);
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
}
|
||||
memset((char*)&sai,0,sizeof(sai));
|
||||
sai.sin_family = AF_INET;
|
||||
}
|
||||
#endif /* IDWRAP */
|
||||
sai.sin_port = htons(port);
|
||||
if ((sai.sin_addr.s_addr = get_ip(host)) == -1)
|
||||
{
|
||||
close(s);
|
||||
return(-1);
|
||||
}
|
||||
sai.sin_family = AF_INET;
|
||||
}
|
||||
|
||||
if ((connect(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) && (errno != EINPROGRESS))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("[CbN] unable to connect. errno = %i\n",errno);
|
||||
#endif /* DEBUG */
|
||||
close(s);
|
||||
return(-1);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
debug("(SockConnect) {%i} %s %i%s\n",s,nullstr(host),port,(use_vhost) ? " [VHOST]" : "");
|
||||
#endif /* DEBUG */
|
||||
return(s);
|
||||
}
|
||||
|
||||
int SockAccept(int sock)
|
||||
{
|
||||
struct sockaddr_in sai;
|
||||
int s,sz;
|
||||
|
||||
sz = sizeof(sai);
|
||||
s = accept(sock,(struct sockaddr*)&sai,&sz);
|
||||
if (s >= 0)
|
||||
{
|
||||
SockFlags(s);
|
||||
}
|
||||
return(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Format text and send to a socket or file descriptor
|
||||
*/
|
||||
int to_file(int sock, const char *format, ...)
|
||||
{
|
||||
va_list msg;
|
||||
#ifdef DEBUG
|
||||
char *line,*rest;
|
||||
int i;
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (sock == -1)
|
||||
return(-1);
|
||||
|
||||
va_start(msg,format);
|
||||
vsprintf(gsockdata,format,msg);
|
||||
va_end(msg);
|
||||
|
||||
#ifdef DEBUG
|
||||
i = write(sock,gsockdata,strlen(gsockdata));
|
||||
rest = gsockdata;
|
||||
while((line = get_token(&rest,"\n"))) /* rest cannot be NULL */
|
||||
debug("(out) {%i} %s\n",sock,nullstr(line));
|
||||
if (i < 0)
|
||||
debug("(out) {%i} errno = %i\n",sock,errno);
|
||||
return(i);
|
||||
#else /* DEBUG */
|
||||
return(write(sock,gsockdata,strlen(gsockdata)));
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
/*
|
||||
* Format a message and send it to the current bots server
|
||||
* to_server needs a newline (\n) it wont manufacture it itself.
|
||||
*/
|
||||
void to_server(char *format, ...)
|
||||
{
|
||||
va_list msg;
|
||||
#ifdef DEBUG
|
||||
char *line,*rest;
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (current->sock == -1)
|
||||
return;
|
||||
|
||||
va_start(msg,format);
|
||||
vsprintf(gsockdata,format,msg);
|
||||
va_end(msg);
|
||||
|
||||
/*
|
||||
* each line we send to the server pushes the sendq time
|
||||
* forward *two* seconds
|
||||
*/
|
||||
current->sendq_time += 2;
|
||||
|
||||
if (write(current->sock,gsockdata,strlen(gsockdata)) < 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("[StS] {%i} errno = %i\n",current->sock,errno);
|
||||
#endif /* DEBUG */
|
||||
close(current->sock);
|
||||
current->sock = -1;
|
||||
current->connect = CN_NOSOCK;
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
rest = gsockdata;
|
||||
while((line = get_token(&rest,"\n"))) /* rest cannot be NULL */
|
||||
debug("[StS] {%i} %s\n",current->sock,line);
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
void to_user_q(const char *target, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
Client *client;
|
||||
Strp *new,**pp;
|
||||
char message[MAXLEN];
|
||||
char nick[MAXHOSTLEN];
|
||||
char *fmt;
|
||||
|
||||
va_start(args,format);
|
||||
vsprintf(message,format,args);
|
||||
va_end(args);
|
||||
|
||||
Strcat(message,"\n");
|
||||
|
||||
fmt = "NOTICE %s :%s";
|
||||
if (CurrentChan)
|
||||
{
|
||||
target = CurrentChan->name;
|
||||
fmt = "PRIVMSG %s :%s";
|
||||
}
|
||||
|
||||
if (!ischannel(target))
|
||||
target = nickcpy(nick,target);
|
||||
|
||||
if ((client = find_client(target)))
|
||||
{
|
||||
if (write(client->sock,message,strlen(message)) < 0)
|
||||
client->flags = DCC_DELETE;
|
||||
#ifdef DEBUG
|
||||
debug("(to_user) {%i} [%s] %s",client->sock,target,message);
|
||||
#endif /* DEBUG */
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef REDIRECT
|
||||
if (redirect.to && target == redirect.to)
|
||||
{
|
||||
send_redirect(message);
|
||||
return;
|
||||
}
|
||||
#endif /* REDIRECT */
|
||||
|
||||
pp = ¤t->sendq;
|
||||
while(*pp)
|
||||
pp = &(*pp)->next;
|
||||
|
||||
set_mallocdoer(to_user_q);
|
||||
*pp = new = (Strp*)Calloc(sizeof(Strp) + Strlen(fmt,target,message,NULL));
|
||||
/* Calloc sets to zero new->next = NULL; */
|
||||
sprintf(new->p,fmt,target,message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Format a message and send it either through DCC if the user is
|
||||
* connected to the partyline, or send it as a NOTICE if he's not
|
||||
*/
|
||||
void to_user(const char *target, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
Client *client;
|
||||
char message[MAXLEN];
|
||||
char *s;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!*target)
|
||||
return;
|
||||
#endif /* DEBUG */
|
||||
|
||||
s = message;
|
||||
|
||||
#ifdef REDIRECT
|
||||
|
||||
if (redirect.to)
|
||||
client = NULL;
|
||||
else
|
||||
client = find_client(target);
|
||||
|
||||
if (!redirect.to && !client)
|
||||
|
||||
#else /* REDIRECT */
|
||||
|
||||
if ((client = find_client(target)) == NULL)
|
||||
|
||||
#endif /* REDIRECT */
|
||||
|
||||
{
|
||||
if (current->sock == -1)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("(to_user) [%s] current->sock == -1\n",target);
|
||||
#endif /* DEBUG */
|
||||
return;
|
||||
}
|
||||
s = Strcpy(message,"NOTICE ");
|
||||
if (ischannel(target))
|
||||
Strcat(message,target);
|
||||
else
|
||||
nickcpy(s,target);
|
||||
s = Strcat(message," :");
|
||||
}
|
||||
|
||||
va_start(args,format);
|
||||
vsprintf(s,format,args);
|
||||
va_end(args);
|
||||
|
||||
#ifdef REDIRECT
|
||||
if (redirect.to)
|
||||
{
|
||||
send_redirect(message);
|
||||
return;
|
||||
}
|
||||
#endif /* REDIRECT */
|
||||
|
||||
/*
|
||||
* tag on a newline for DCC or server
|
||||
*/
|
||||
s = Strcat(message,"\n");
|
||||
|
||||
#ifdef DEBUG
|
||||
*s = 0;
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (client)
|
||||
{
|
||||
if (write(client->sock,message,(s - message)) < 0)
|
||||
client->flags = DCC_DELETE;
|
||||
#ifdef DEBUG
|
||||
debug("(to_user) {%i} [%s] %s",client->sock,target,message);
|
||||
#endif /* DEBUG */
|
||||
return;
|
||||
}
|
||||
|
||||
current->sendq_time += 2;
|
||||
|
||||
if (write(current->sock,message,(s - message)) < 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("(to_user) {%i} [%s] errno = %i\n",current->sock,target,errno);
|
||||
#endif /* DEBUG */
|
||||
close(current->sock);
|
||||
current->sock = -1;
|
||||
current->connect = CN_NOSOCK;
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
debug("(to_user) {%i} [%s] %s",current->sock,target,message);
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
/*
|
||||
* Read any data waiting on a socket or file descriptor
|
||||
* and return any complete lines to the caller
|
||||
*/
|
||||
char *sockread(int s, char *rest, char *line)
|
||||
{
|
||||
char *src,*dst,*rdst;
|
||||
int n;
|
||||
|
||||
errno = EAGAIN;
|
||||
|
||||
src = rest;
|
||||
dst = line;
|
||||
|
||||
while(*src)
|
||||
{
|
||||
if (*src == '\n' || *src == '\r')
|
||||
{
|
||||
gotline:
|
||||
while(*src == '\n' || *src == '\r')
|
||||
src++;
|
||||
*dst = 0;
|
||||
dst = rest;
|
||||
while(*src)
|
||||
*(dst++) = *(src++);
|
||||
*dst = 0;
|
||||
#ifdef DEBUG
|
||||
debug("(in) {%i} %s\n",s,line);
|
||||
#endif /* DEBUG */
|
||||
return((*line) ? line : NULL);
|
||||
}
|
||||
*(dst++) = *(src++);
|
||||
}
|
||||
rdst = src;
|
||||
|
||||
n = read(s,gsockdata,MSGLEN-2);
|
||||
switch(n)
|
||||
{
|
||||
case 0:
|
||||
errno = EPIPE;
|
||||
case -1:
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
gsockdata[n] = 0;
|
||||
src = gsockdata;
|
||||
|
||||
while(*src)
|
||||
{
|
||||
if (*src == '\r' || *src == '\n')
|
||||
goto gotline;
|
||||
if ((dst - line) >= (MSGLEN-2))
|
||||
{
|
||||
/*
|
||||
* line is longer than buffer, let the wheel spin
|
||||
*/
|
||||
src++;
|
||||
continue;
|
||||
}
|
||||
*(rdst++) = *(dst++) = *(src++);
|
||||
}
|
||||
*rdst = 0;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void readline(int fd, int (*callback)(char *))
|
||||
{
|
||||
char linebuf[MSGLEN],readbuf[MSGLEN];
|
||||
char *ptr;
|
||||
int oc;
|
||||
|
||||
*readbuf = 0;
|
||||
|
||||
do
|
||||
{
|
||||
ptr = sockread(fd,readbuf,linebuf);
|
||||
oc = errno;
|
||||
if (ptr && *ptr)
|
||||
{
|
||||
if (callback(ptr) == TRUE)
|
||||
oc = EPIPE;
|
||||
}
|
||||
}
|
||||
while(oc == EAGAIN);
|
||||
|
||||
close(fd);
|
||||
|
||||
#ifdef DEBUG
|
||||
debug("(readline) done reading lines\n");
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
void remove_ks(KillSock *ks)
|
||||
{
|
||||
KillSock *ksp;
|
||||
|
||||
close(ks->sock);
|
||||
if (ks == killsocks)
|
||||
{
|
||||
killsocks = ks->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ksp = killsocks;
|
||||
while(ksp && (ksp->next != ks))
|
||||
ksp = ksp->next;
|
||||
if (ksp)
|
||||
{
|
||||
ksp->next = ks->next;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
debug("(killsock) {%i} removing killsocket\n",ks->sock);
|
||||
#endif /* DEBUG */
|
||||
Free((char**)&ks);
|
||||
}
|
||||
|
||||
int killsock(int sock)
|
||||
{
|
||||
KillSock *ks,*ksnext;
|
||||
struct timeval t;
|
||||
fd_set rd,wd;
|
||||
char bitbucket[MSGLEN];
|
||||
int hi,n;
|
||||
|
||||
if (sock >= 0)
|
||||
{
|
||||
set_mallocdoer(killsock);
|
||||
ks = (KillSock*)Calloc(sizeof(KillSock));
|
||||
ks->time = now;
|
||||
ks->sock = sock;
|
||||
ks->next = killsocks;
|
||||
killsocks = ks;
|
||||
#ifdef DEBUG
|
||||
debug("(killsock) {%i} added killsocket\n",ks->sock);
|
||||
#endif /* DEBUG */
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
if (killsocks == NULL)
|
||||
return(FALSE);
|
||||
|
||||
if (sock == -1)
|
||||
t.tv_sec = 0;
|
||||
else
|
||||
t.tv_sec = 1;
|
||||
t.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&rd);
|
||||
FD_ZERO(&wd);
|
||||
|
||||
hi = -1;
|
||||
for(ks=killsocks;ks;ks=ks->next)
|
||||
{
|
||||
if (ks->sock > hi)
|
||||
hi = ks->sock;
|
||||
FD_SET(ks->sock,&rd);
|
||||
FD_SET(ks->sock,&wd);
|
||||
}
|
||||
|
||||
if (select(hi+1,&rd,&wd,NULL,&t) == -1)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EINTR:
|
||||
/* have to redo the select */
|
||||
case ENOMEM:
|
||||
/* should never happen, but still */
|
||||
return(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
for(ks=killsocks;ks;)
|
||||
{
|
||||
ksnext = ks->next;
|
||||
if (FD_ISSET(ks->sock,&rd))
|
||||
{
|
||||
n = read(ks->sock,&bitbucket,MSGLEN);
|
||||
if ((n == 0) || ((n == -1) && (errno != EAGAIN)))
|
||||
remove_ks(ks);
|
||||
}
|
||||
if ((now - ks->time) > KILLSOCKTIMEOUT)
|
||||
remove_ks(ks);
|
||||
ks = ksnext;
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
Reference in New Issue
Block a user