/* $Id: flood.c,v 1.6 2006/09/20 08:55:10 maxim Exp $ * */ #include #include "configure.h" #include "common/settings.h" #include "common/misc.h" #include "common/str.h" #include "config.h" #include "flood.h" #if SUPPORT_ANTIFLOOD #define LOW_SIZE 4096 #define IPS_STEP 1024 static IPInfo** ips = NULL; static IPInfo* freeIPs = NULL; static IPInfo* working = NULL; static IPInfo* workingTail = NULL; static IPInfo* denied = NULL; static IPInfo* deniedTail = NULL; static int count = 0; static int countOnline = 0; static int maxCount = 0; static int floods = 0; static int dropped = 0; void removeFromWorking(IPInfo* i) { if (i->old) i->old->recent = i->recent; else workingTail = i->recent; if (i->recent) i->recent->old = i->old; else working = i->old; } void removeFromDenied(IPInfo* i) { if (i->old) i->old->recent = i->recent; else deniedTail = i->recent; if (i->recent) i->recent->old = i->old; else denied = i->old; } void insertToWorking(IPInfo* i) { if ((i->old = working)) working->recent = i; else workingTail = i; working = i; i->recent = NULL; } void insertToDenied(IPInfo* i) { if ((i->old = denied)) denied->recent = i; else deniedTail = i; denied = i; i->recent = NULL; } void removeIPInfo(IPInfo* i) { unsigned low; if (i->next) i->next->prev = i->prev; if (i->prev) i->prev->next = i->next; low = i->ip & (LOW_SIZE - 1); if (ips[low] == i) ips[low] = i->next; if (i->dropped) { message(MESSAGE, "connection flood from %s, dropped: %d, accepted: %d", ip2string(i->ip), i->dropped, i->accepted); floods++; } if (i->denied) { message(MESSAGE, "request flood from %s: %d denied", ip2string(i->ip), i->denied); removeFromDenied(i); } else { removeFromWorking(i); } i->next = freeIPs; freeIPs = i; --count; } IPInfo* addIPConnection(unsigned ip) { unsigned low = ip & (LOW_SIZE - 1); IPInfo* i; if (!ips && !(ips = CALLOC(LOW_SIZE * sizeof(IPInfo*)))) return NULL; i = ips[low]; while(i) { if (i->ip == ip) { if (++(i->active) > config->connectionsPerIP) { i->dropped++; i->active--; dropped++; return NULL; } if (i->active == 1) ++countOnline; i->accepted++; return i; } i = i->next; } if (!(i = freeIPs)) { freeIPs = MALLOC(IPS_STEP * sizeof(IPInfo)); if (!freeIPs) return NULL; for (i = freeIPs; i < freeIPs + IPS_STEP - 1; i++) i->next = i + 1; i->next = NULL; i = freeIPs; } freeIPs = freeIPs->next; if ((i->next = ips[low])) i->next->prev = i; ips[low] = i; i->prev = NULL; i->ip = ip; i->dropped = i->requests = i->denied = 0; i->active = i->accepted = 1; i->stamp = now + config->antifloodPeriod; insertToWorking(i); if (++count > maxCount) maxCount = count; ++countOnline; return i; } void decIPConnection(IPInfo* i) { if (--(i->active) <= 0) { --countOnline; if (i->active < 0) message(MESSAGE, "strange: %s active < 0 (%d)", ip2string(i->ip), i->active); if (i->requests) return; removeIPInfo(i); } } void decIP(unsigned ip) { unsigned low = ip & (LOW_SIZE - 1); IPInfo* i = ips[low]; while(i) { if (i->ip == ip) { decIPConnection(i); return; } i = i->next; } } int addIPRequest(unsigned ip) { unsigned low; IPInfo* i; if (!config->requestsPerIP) return OK; low = ip & (LOW_SIZE - 1); i = ips[low]; while(i) { if (i->ip == ip) { if (i->denied) { i->denied++; dropped++; return FALSE; } i->requests++; if (now > i->stamp) { i->stamp = now + config->antifloodPeriod; i->requests = 1; if (working != i) { removeFromWorking(i); insertToWorking(i); } } else if (i->requests > config->requestsPerIP) { message(MESSAGE, "request flood from %s detected after %d minutes", ip2string(i->ip), (config->antifloodPeriod - (i->stamp - now) + 30)/60); i->stamp = now + config->antifloodExpired; i->denied = 1; floods++; removeFromWorking(i); insertToDenied(i); return FAILED; } return OK; } i = i->next; } message(MESSAGE, "strange: cannot find IP in list: %s", ip2string(ip)); return OK; } int getActive(unsigned ip) { IPInfo* i; unsigned low = ip & (LOW_SIZE - 1); if (!ips) return 0; i = ips[low]; while(i) { if (i->ip == ip) return i->active; i = i->next; } return 0; } void cleanupIP() { if (!(config->requestsPerIP && ips)) return; while(deniedTail && now > deniedTail->stamp) { if (!deniedTail->active) { removeIPInfo(deniedTail); } else { IPInfo* i = deniedTail; i->denied = 0; i->stamp = now + config->antifloodPeriod; removeFromDenied(i); insertToWorking(i); } } while(workingTail && now > workingTail->stamp) { if (!workingTail->active) { removeIPInfo(workingTail); } else { IPInfo* i = workingTail; i->stamp = now + config->antifloodPeriod; if (i != working) { removeFromWorking(i); insertToWorking(i); } } } } int getIPStats(char* buffer, int size, char show, char* uri) { int l = SNPRINTF(buffer, size, "\n" "Unique IPs (online/current/max): %s / %s / %s\n" "Floods: %s times, %s requests was dropped\n", uri, show ? "" : "?ips", itos(countOnline), itos(count), itos(maxCount), itos(floods), itos(dropped)); if (show) { if (config->requestsPerIP) { IPInfo* i; l += SNPRINTF(buffer + l, size - l, "%-16s %s %s %s %s %s %8s\n", "IP-address", "Active", "Accepted", "Dropped", "Requests", "Denied", "Till"); for (i = denied; i && l < size - 100; i = i->old) l += SNPRINTF(buffer + l, size - l, "%-16s %6d %8d %7d %8d %6d %8s\n", ip2string(i->ip), i->active, i->accepted, i->dropped, i->requests, i->denied, time2str(i->stamp, TODAY_TIME)); for (i = working; i && l < size - 100; i = i->old) l += SNPRINTF(buffer + l, size - l, "%-16s %6d %8d %7d %8d %6d %8s\n", ip2string(i->ip), i->active, i->accepted, i->dropped, i->requests, i->denied, time2str(i->stamp, TODAY_TIME)); } else { IPInfo* i; l += SNPRINTF(buffer + l, size - l, "%-16s %s %s %s\n", "IP-address", "Active", "Accepted", "Dropped"); for (i = working; i && l < size - 100; i = i->old) l += SNPRINTF(buffer + l, size - l, "%-16s %6d %8d %7d\n", ip2string(i->ip), i->active, i->accepted, i->dropped); } } return l; } void resetTodayFlood() { maxCount = count; floods = dropped = 0; } #endif /* SUPPORT_ANTIFLOOD */