- Cleaned up the ip rules/DDoS section of the code. (the allow,deny and deny,allow cases were switched)

- Updated the information about ip rules and DDoS protection in packet_athena.conf and commented out the line "allow: all" so connections are rejected when a DDoS is detected.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@9647 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
FlavioJS 2007-01-12 13:51:38 +00:00
parent 60f4e80cc1
commit a7ee9a9961
5 changed files with 231 additions and 190 deletions

View File

@ -4,6 +4,8 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
2007/01/12 2007/01/12
* Cleaned up the ip rules/DDoS section of the code. (the allow,deny and
deny,allow cases were switched)
* Fixed autotrading characters not being able to reconnect. Seams like I * Fixed autotrading characters not being able to reconnect. Seams like I
misunderstood how id_db and pc_db are used. [FlavioJS] misunderstood how id_db and pc_db are used. [FlavioJS]
2007/01/11 2007/01/11

View File

@ -1,5 +1,9 @@
Date Added Date Added
2007/01/12
* Updated the information about ip rules and DDoS protection in
packet_athena.conf and commented out the line "allow: all" so
connections are rejected when a DDoS is detected. [FlavioJS]
2007/01/08 2007/01/08
* Added the console plugin to plugin_athena.conf commented out. [FlavioJS] * Added the console plugin to plugin_athena.conf commented out. [FlavioJS]
2007/01/05 2007/01/05

View File

@ -17,40 +17,47 @@ mode_neg: yes
//----- IP Rules Settings ----- //----- IP Rules Settings -----
// Do we check IP's before allowing incoming connections? // If IP's are checked when connecting.
// This also enables DDoS protection.
enable_ip_rules: yes enable_ip_rules: yes
// Decide the order of access restriction (Same as apache?) // Order of the checks
// deny,allow Is the standard // deny,allow : Checks deny rules, then allow rules. Allows if no rules match.
// allow,deny : Checks allow rules, then deny rules. Allows if no rules match.
// mutual-failure : Allows only if an allow rule matches and no deny rules match.
// (default is deny,allow)
order: deny,allow order: deny,allow
// order: allow,deny // order: allow,deny
// order: mutual-failture // order: mutual-failture
// The IP list which it uses to access controls // IP rules
// allow : Allows access regardless of permissions // allow : Accepts connections from the ip range (even if flagged as DDoS)
// deny : Completely disallow // deny : Rejects connections from the ip range
// Žwè³µ : If the permission check encounters mutual-failure(whatever that means) it will disallow access // The rules are processed in order, the first matching rule of each list (allow and deny) is used
// allow: 127.0.0.1 // allow: 127.0.0.1
// allow: 192.168.0.0/16 // allow: 192.168.0.0/16
// allow: 10.0.0.0/255.0.0.0 // allow: 10.0.0.0/255.0.0.0
allow: all // allow: all
// deny: 127.0.0.1 // deny: 127.0.0.1
//---- Ddos Protection Settings ---- //---- DDoS Protection Settings ----
// If there is a connection request within ddos_interval msec for ddos_count number of times, it will assume it is a ddos attack // If ddos_count connection request are made within ddos_interval msec, it assumes it's a DDoS attack
// Consecutive intervals(msec) // Consecutive attempts interval (msec)
// (default is 3000 msecs, 3 seconds)
ddos_interval: 3000 ddos_interval: 3000
// Connection frequency // Consecutive attempts trigger
// (default is 10 attemps)
ddos_count: 5 ddos_count: 5
// The time interval after which the threat of ddos is assumed to be gone // The time interval after which the threat of DDoS is assumed to be gone. (msec)
// After this amount of time, the ddos restrictions are lifted. // After this amount of time, the DDoS restrictions are lifted.
// (default is 600000 msecs, 10 minutes)
ddos_autoreset: 600000 ddos_autoreset: 600000

View File

@ -73,25 +73,6 @@ int ip_rules = 1;
static int mode_neg=1; static int mode_neg=1;
static size_t frame_size=TCP_FRAME_LEN; static size_t frame_size=TCP_FRAME_LEN;
#ifndef MINICORE
enum {
ACO_DENY_ALLOW=0,
ACO_ALLOW_DENY,
ACO_MUTUAL_FAILTURE,
};
static struct _access_control *access_allow;
static struct _access_control *access_deny;
static int access_order=ACO_DENY_ALLOW;
static int access_allownum=0;
static int access_denynum=0;
static int access_debug=0;
static int ddos_count = 10;
static int ddos_interval = 3000;
static int ddos_autoreset = 600*1000;
#endif
// values derived from freya // values derived from freya
// a player that send more than 2k is probably a hacker without be parsed // a player that send more than 2k is probably a hacker without be parsed
// biggest known packet: S 0153 <len>.w <emblem data>.?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes) // biggest known packet: S 0153 <len>.w <emblem data>.?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes)
@ -283,17 +264,18 @@ static int connect_client(int listen_fd)
return -1; return -1;
} }
if(fd_max<=fd) fd_max=fd+1;
setsocketopts(fd); setsocketopts(fd);
set_nonblocking(fd, 1); set_nonblocking(fd, 1);
if (ip_rules && !connect_check(*(unsigned int*)(&client_address.sin_addr))) { if( ip_rules && !connect_check(*(uint32*)(&client_address.sin_addr)) ){
do_close(fd); do_close(fd);
return -1; return -1;
} else } else
FD_SET(fd,&readfds); FD_SET(fd,&readfds);
if( fd_max <= fd )
fd_max = fd + 1;
CREATE(session[fd], struct socket_data, 1); CREATE(session[fd], struct socket_data, 1);
CREATE(session[fd]->rdata, unsigned char, rfifo_size); CREATE(session[fd]->rdata, unsigned char, rfifo_size);
CREATE(session[fd]->wdata, unsigned char, wfifo_size); CREATE(session[fd]->wdata, unsigned char, wfifo_size);
@ -768,45 +750,72 @@ int do_parsepacket(void)
return 0; return 0;
} }
/* DDoS 攻撃対策 */ //////////////////////////////
#ifndef MINICORE #ifndef MINICORE
struct _access_control { //////////////////////////////
unsigned int ip; // IP rules and DDoS protection
unsigned int mask;
};
struct _connect_history { typedef struct _connect_history {
struct _connect_history *next; struct _connect_history* next;
struct _connect_history *prev; uint32 ip;
int status; uint32 tick;
int count; int count;
unsigned int ip; unsigned ddos : 1;
unsigned int tick; } ConnectHistory;
};
static struct _connect_history *connect_history[0x10000];
static int connect_check_(unsigned int ip);
// 接続できるかどうかの確認 typedef struct _access_control {
// false : 接続OK uint32 ip;
// true : 接続NG uint32 mask;
static int connect_check(unsigned int ip) { } AccessControl;
enum _aco {
ACO_DENY_ALLOW,
ACO_ALLOW_DENY,
ACO_MUTUAL_FAILURE
};
static AccessControl* access_allow = NULL;
static AccessControl* access_deny = NULL;
static int access_order = ACO_DENY_ALLOW;
static int access_allownum = 0;
static int access_denynum = 0;
static int access_debug = 0;
static int ddos_count = 10;
static int ddos_interval = 3*1000;
static int ddos_autoreset = 10*60*1000;
/// Connection history, an array of linked lists.
/// The array's index for any ip is ip&0xFFFF
static ConnectHistory* connect_history[0x10000];
static int connect_check_(uint32 ip);
/// Verifies if the IP can connect. (with debug info)
/// @see connect_check_()
static int connect_check(uint32 ip)
{
int result = connect_check_(ip); int result = connect_check_(ip);
if(access_debug) { if( access_debug ){
ShowMessage("connect_check: Connection from %d.%d.%d.%d %s\n", ShowMessage("connect_check: Connection from %d.%d.%d.%d %s\n",
CONVIP(ip),result ? "allowed." : "denied!"); CONVIP(ip),result ? "allowed." : "denied!");
} }
return result; return result;
} }
static int connect_check_(unsigned int ip) { /// Verifies if the IP can connect.
struct _connect_history *hist = connect_history[ip & 0xFFFF]; /// 0 : Connection Rejected
struct _connect_history *hist_new; /// 1 or 2 : Connection Accepted
int i,is_allowip = 0,is_denyip = 0,connect_ok = 0; static int connect_check_(uint32 ip)
{
ConnectHistory* hist = connect_history[ip&0xFFFF];
int i;
int is_allowip = 0;
int is_denyip = 0;
int connect_ok = 0;
// allow , deny リストに入っているか確認 // Search the allow list
for(i = 0;i < access_allownum; i++) { for( i=0; i < access_allownum; ++i ){
if((ip & access_allow[i].mask) == (access_allow[i].ip & access_allow[i].mask)) { if( (ip & access_allow[i].mask) == (access_allow[i].ip & access_allow[i].mask) ){
if(access_debug) { if( access_debug ){
ShowMessage("connect_check: Found match from allow list:%d.%d.%d.%d IP:%d.%d.%d.%d Mask:%d.%d.%d.%d\n", ShowMessage("connect_check: Found match from allow list:%d.%d.%d.%d IP:%d.%d.%d.%d Mask:%d.%d.%d.%d\n",
CONVIP(ip), CONVIP(ip),
CONVIP(access_allow[i].ip), CONVIP(access_allow[i].ip),
@ -816,9 +825,10 @@ static int connect_check_(unsigned int ip) {
break; break;
} }
} }
for(i = 0;i < access_denynum; i++) { // Search the deny list
if((ip & access_deny[i].mask) == (access_deny[i].ip & access_deny[i].mask)) { for( i=0; i < access_denynum; ++i ){
if(access_debug) { if( (ip & access_deny[i].mask) == (access_deny[i].ip & access_deny[i].mask) ){
if( access_debug ){
ShowMessage("connect_check: Found match from deny list:%d.%d.%d.%d IP:%d.%d.%d.%d Mask:%d.%d.%d.%d\n", ShowMessage("connect_check: Found match from deny list:%d.%d.%d.%d IP:%d.%d.%d.%d Mask:%d.%d.%d.%d\n",
CONVIP(ip), CONVIP(ip),
CONVIP(access_deny[i].ip), CONVIP(access_deny[i].ip),
@ -828,61 +838,55 @@ static int connect_check_(unsigned int ip) {
break; break;
} }
} }
// コネクト出来るかどうか確認 // Decide connection status
// connect_ok // 0 : Reject
// 0 : 無条件に拒否 // 1 : Accept
// 1 : 田代砲チェックの結果次第 // 2 : Unconditional Accept (accepts even if flagged as DDoS)
// 2 : 無条件に許可
switch(access_order) { switch(access_order) {
case ACO_DENY_ALLOW: case ACO_DENY_ALLOW:
default: default:
if(is_allowip) { if( is_denyip )
connect_ok = 2; connect_ok = 0; // Reject
} else if(is_denyip) { else if( is_allowip )
connect_ok = 0; connect_ok = 2; // Unconditional Accept
} else { else
connect_ok = 1; connect_ok = 1; // Accept
}
break; break;
case ACO_ALLOW_DENY: case ACO_ALLOW_DENY:
if(is_denyip) { if( is_allowip )
connect_ok = 0; connect_ok = 2; // Unconditional Accept
} else if(is_allowip) { else if( is_denyip )
connect_ok = 2; connect_ok = 0; // Reject
} else { else
connect_ok = 1; connect_ok = 1; // Accept
}
break; break;
case ACO_MUTUAL_FAILTURE: case ACO_MUTUAL_FAILURE:
if(is_allowip) { if( is_allowip && !is_denyip )
connect_ok = 2; connect_ok = 2; // Unconditional Accept
} else { else
connect_ok = 0; connect_ok = 0; // Reject
}
break; break;
} }
// 接続履歴を調べる // Inspect connection history
while(hist) { while( hist ) {
if(ip == hist->ip) { if( ip == hist->ip )
// 同じIP発見 {// IP found
if(hist->status) { if( hist->ddos )
// ban フラグが立ってる {// flagged as DDoS
return (connect_ok == 2 ? 1 : 0); return (connect_ok == 2 ? 1 : 0);
} else if(DIFF_TICK(gettick(),hist->tick) < ddos_interval) { } else if( DIFF_TICK(gettick(),hist->tick) < ddos_interval )
// ddos_interval秒以内にリクエスト有り {// connection within ddos_interval
hist->tick = gettick(); hist->tick = gettick();
if(hist->count++ >= ddos_count) { if( hist->count++ >= ddos_count )
// ddos 攻撃を検出 {// DDoS attack detected
hist->status = 1; hist->ddos = 1;
ShowWarning("connect_check: DDOS Attack detected from %d.%d.%d.%d!\n", ShowWarning("connect_check: DDoS Attack detected from %d.%d.%d.%d!\n", CONVIP(ip));
CONVIP(ip));
return (connect_ok == 2 ? 1 : 0); return (connect_ok == 2 ? 1 : 0);
} else {
return connect_ok;
} }
} else { return connect_ok;
// ddos_interval秒以内にリクエスト無いのでタイマークリア } else
{// not within ddos_interval, clear data
hist->tick = gettick(); hist->tick = gettick();
hist->count = 0; hist->count = 0;
return connect_ok; return connect_ok;
@ -890,80 +894,92 @@ static int connect_check_(unsigned int ip) {
} }
hist = hist->next; hist = hist->next;
} }
// IPリストに無いので新規作成 // IP not found, add to history
hist_new = (struct _connect_history *) aCalloc(1,sizeof(struct _connect_history)); CREATE(hist, ConnectHistory, 1);
hist_new->ip = ip; memset(hist, 0, sizeof(ConnectHistory));
hist_new->tick = gettick(); hist->ip = ip;
if(connect_history[ip & 0xFFFF] != NULL) { hist->tick = gettick();
hist = connect_history[ip & 0xFFFF]; hist->next = connect_history[ip&0xFFFF];
hist->prev = hist_new; connect_history[ip&0xFFFF] = hist;
hist_new->next = hist;
}
connect_history[ip & 0xFFFF] = hist_new;
return connect_ok; return connect_ok;
} }
static int connect_check_clear(int tid,unsigned int tick,int id,int data) { /// Timer function.
/// Deletes old connection history records.
static int connect_check_clear(int tid, unsigned int tick, int id, int data)
{
int i; int i;
int clear = 0; int clear = 0;
int list = 0; int list = 0;
struct _connect_history *hist , *hist2; ConnectHistory root;
for(i = 0;i < 0x10000 ; i++) { ConnectHistory* prev_hist;
hist = connect_history[i]; ConnectHistory* hist;
while(hist) {
if ((DIFF_TICK(tick,hist->tick) > ddos_interval * 3 && !hist->status) || for( i=0; i < 0x10000 ; ++i ){
(DIFF_TICK(tick,hist->tick) > ddos_autoreset && hist->status)) { prev_hist = &root;
// clear data root.next = hist = connect_history[i];
hist2 = hist->next; while( hist ){
if(hist->prev) { if( (!hist->ddos && DIFF_TICK(tick,hist->tick) > ddos_interval*3) ||
hist->prev->next = hist->next; (hist->ddos && DIFF_TICK(tick,hist->tick) > ddos_autoreset) )
} else { {// Remove connection history
connect_history[i] = hist->next; prev_hist->next = hist->next;
}
if(hist->next) {
hist->next->prev = hist->prev;
}
aFree(hist); aFree(hist);
hist = hist2; hist = prev_hist->next;
clear++; clear++;
} else { } else {
prev_hist = hist;
hist = hist->next; hist = hist->next;
} }
list++; list++;
} }
connect_history[i] = root.next;
} }
if(access_debug) { if( access_debug ){
ShowMessage("connect_check_clear: Cleared %d of %d from IP list.\n", clear, list); ShowMessage("connect_check_clear: Cleared %d of %d from IP list.\n", clear, list);
} }
return list; return list;
} }
// IPマスクチェック /// Parses the ip address and mask and puts it into acc.
int access_ipmask(const char *str,struct _access_control* acc) /// Returns 1 is successful, 0 otherwise.
int access_ipmask(const char* str, AccessControl* acc)
{ {
unsigned int mask=0,i=0,m,ip, a0,a1,a2,a3; uint32 ip;
if( !strcmp(str,"all") ) { uint32 mask;
unsigned int a[4];
unsigned int m[4];
int n;
if( strcmp(str,"all") == 0 ){
ip = 0; ip = 0;
mask = 0; mask = 0;
} else { } else {
if( sscanf(str,"%d.%d.%d.%d%n",&a0,&a1,&a2,&a3,&i)!=4 || i==0) { if( ((n=sscanf(str,"%u.%u.%u.%u/%u.%u.%u.%u",a,a+1,a+2,a+3,m,m+1,m+2,m+3)) != 8 && // not an ip + standard mask
ShowError("access_ipmask: Unknown format %s!\n",str); (n=sscanf(str,"%u.%u.%u.%u/%u",a,a+1,a+2,a+3,m)) != 5 && // not an ip + bit mask
(n=sscanf(str,"%u.%u.%u.%u",a,a+1,a+2,a+3)) != 4 ) || // not an ip
a[0] > 255 || a[1] > 255 || a[2] > 255 || a[3] > 255 || // invalid ip
(n == 8 && (m[0] > 255 || m[1] > 255 || m[2] > 255 || m[3] > 255)) || // invalid standard mask
(n == 5 && m[0] > 32) ){ // invalid bit mask
return 0; return 0;
} }
ip = (a3 << 24) | (a2 << 16) | (a1 << 8) | a0; ip = (uint32)(a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24));
if( n == 8 )
if(sscanf(str+i,"/%d.%d.%d.%d",&a0,&a1,&a2,&a3)==4 ){ {// standard mask
mask = (a3 << 24) | (a2 << 16) | (a1 << 8) | a0; mask = (uint32)(a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24));
} else if(sscanf(str+i,"/%d",&m) == 1) { } else if( n == 5 )
for(i=0;i<m;i++) { {// bit mask
mask = 0;
while( m[0] ){
mask = (mask >> 1) | 0x80000000; mask = (mask >> 1) | 0x80000000;
--m[0];
} }
mask = ntohl(mask); mask = ntohl(mask);
} else { } else
{// just this ip
mask = 0xFFFFFFFF; mask = 0xFFFFFFFF;
} }
} }
if(access_debug) { if( access_debug ){
ShowMessage("access_ipmask: Loaded IP:%d.%d.%d.%d mask:%d.%d.%d.%d\n", ShowMessage("access_ipmask: Loaded IP:%d.%d.%d.%d mask:%d.%d.%d.%d\n",
CONVIP(ip), CONVIP(mask)); CONVIP(ip), CONVIP(mask));
} }
@ -971,7 +987,9 @@ int access_ipmask(const char *str,struct _access_control* acc)
acc->mask = mask; acc->mask = mask;
return 1; return 1;
} }
//////////////////////////////
#endif #endif
//////////////////////////////
int socket_config_read(const char *cfgName) { int socket_config_read(const char *cfgName) {
int i; int i;
@ -992,39 +1010,46 @@ int socket_config_read(const char *cfgName) {
if(strcmpi(w1,"stall_time")==0){ if(strcmpi(w1,"stall_time")==0){
stall_time = atoi(w2); stall_time = atoi(w2);
#ifndef MINICORE #ifndef MINICORE
} else if(strcmpi(w1,"enable_ip_rules")==0){ } else if( strcmpi(w1,"enable_ip_rules") == 0 ){
if(strcmpi(w2,"yes")==0) if( strcmpi(w2,"yes") == 0 )
ip_rules = 1; ip_rules = 1;
else if(strcmpi(w2,"no")==0) else if( strcmpi(w2,"no") == 0 )
ip_rules = 0; ip_rules = 0;
else ip_rules = atoi(w2); else
} else if(strcmpi(w1,"order")==0){ ip_rules = atoi(w2);
access_order=atoi(w2); } else if( strcmpi(w1,"order") == 0 ){
if(strcmpi(w2,"deny,allow")==0) access_order=ACO_DENY_ALLOW; access_order = atoi(w2);
if(strcmpi(w2,"allow,deny")==0) access_order=ACO_ALLOW_DENY; if( strcmpi(w2,"deny,allow") == 0 )
if(strcmpi(w2,"mutual-failure")==0) access_order=ACO_MUTUAL_FAILTURE; access_order = ACO_DENY_ALLOW;
} else if(strcmpi(w1,"allow")==0){ else if( strcmpi(w2,"allow,deny") == 0 )
access_allow = (struct _access_control *) aRealloc(access_allow,(access_allownum+1)*sizeof(struct _access_control)); access_order=ACO_ALLOW_DENY;
if(access_ipmask(w2,&access_allow[access_allownum])) { else if( strcmpi(w2,"mutual-failure") == 0 )
access_allownum++; access_order=ACO_MUTUAL_FAILURE;
} } else if( strcmpi(w1,"allow") == 0 ){
} else if(strcmpi(w1,"deny")==0){ RECREATE(access_deny, AccessControl, access_denynum+1);
access_deny = (struct _access_control *) aRealloc(access_deny,(access_denynum+1)*sizeof(struct _access_control)); if( access_ipmask(w2,&access_allow[access_allownum]) )
if(access_ipmask(w2,&access_deny[access_denynum])) { ++access_allownum;
access_denynum++; else
} ShowError("socket_config_read: Invalid ip or ip range '%s'!\n", line);
} else if(!strcmpi(w1,"ddos_interval")){ } else if( strcmpi(w1,"deny") == 0 ){
RECREATE(access_deny, AccessControl, access_denynum+1);
if( access_ipmask(w2,&access_deny[access_denynum]) )
++access_denynum;
else
ShowError("socket_config_read: Invalid ip or ip range '%s'!\n", line);
} else if( strcmpi(w1,"ddos_interval") == 0){
ddos_interval = atoi(w2); ddos_interval = atoi(w2);
} else if(!strcmpi(w1,"ddos_count")){ } else if( strcmpi(w1,"ddos_count") == 0){
ddos_count = atoi(w2); ddos_count = atoi(w2);
} else if(!strcmpi(w1,"ddos_autoreset")){ } else if( strcmpi(w1,"ddos_autoreset") == 0){
ddos_autoreset = atoi(w2); ddos_autoreset = atoi(w2);
} else if(!strcmpi(w1,"debug")){ } else if( strcmpi(w1,"debug") == 0){
if(strcmpi(w2,"yes")==0) if( strcmpi(w2,"yes") == 0 )
access_debug = 1; access_debug = 1;
else if(strcmpi(w2,"no")==0) else if( strcmpi(w2,"no") == 0 )
access_debug = 0; access_debug = 0;
else access_debug = atoi(w2); else
access_debug = atoi(w2);
#endif #endif
} else if (strcmpi(w1, "mode_neg") == 0) } else if (strcmpi(w1, "mode_neg") == 0)
{ {
@ -1070,18 +1095,20 @@ void socket_final (void)
{ {
int i; int i;
#ifndef MINICORE #ifndef MINICORE
struct _connect_history *hist , *hist2; ConnectHistory* hist;
for(i = 0; i < 0x10000; i++) { ConnectHistory* next_hist;
for( i=0; i < 0x10000; ++i ){
hist = connect_history[i]; hist = connect_history[i];
while(hist) { while( hist ){
hist2 = hist->next; next_hist = hist->next;
aFree(hist); aFree(hist);
hist = hist2; hist = next_hist;
} }
} }
if (access_allow) if( access_allow )
aFree(access_allow); aFree(access_allow);
if (access_deny) if( access_deny )
aFree(access_deny); aFree(access_deny);
#endif #endif
@ -1235,20 +1262,21 @@ void socket_init(void)
func_parse_table[SESSION_RAW].func = default_func_parse; func_parse_table[SESSION_RAW].func = default_func_parse;
#ifndef MINICORE #ifndef MINICORE
// とりあえず5分ごとに不要なデータを削除する // Delete old connection history every 5 minutes
memset(connect_history, 0, sizeof(connect_history));
add_timer_func_list(connect_check_clear, "connect_check_clear"); add_timer_func_list(connect_check_clear, "connect_check_clear");
add_timer_interval(gettick()+1000,connect_check_clear,0,0,300*1000); add_timer_interval(gettick()+1000, connect_check_clear, 0, 0, 5*60*1000);
#endif #endif
} }
bool session_isValid(int fd) int session_isValid(int fd)
{ //End of Exam has pointed out that fd==0 is actually an unconnected session! [Skotlex] { //End of Exam has pointed out that fd==0 is actually an unconnected session! [Skotlex]
//But this is not so true, it is used... for... something. The console uses it, would this not cause problems? [Skotlex] //But this is not so true, it is used... for... something. The console uses it, would this not cause problems? [Skotlex]
return ( (fd>0) && (fd<FD_SETSIZE) && (NULL!=session[fd]) ); return ( (fd>0) && (fd<FD_SETSIZE) && (NULL!=session[fd]) );
} }
bool session_isActive(int fd) int session_isActive(int fd)
{ {
return ( session_isValid(fd) && !session[fd]->eof ); return ( session_isValid(fd) && !session[fd]->eof );
} }

View File

@ -143,8 +143,8 @@ extern int fd_max;
////////////////////////////////// //////////////////////////////////
// some checking on sockets // some checking on sockets
extern bool session_isValid(int fd); extern int session_isValid(int fd);
extern bool session_isActive(int fd); extern int session_isActive(int fd);
////////////////////////////////// //////////////////////////////////
// Function prototype declaration // Function prototype declaration