├── .gitignore ├── Makefile ├── config_example ├── anti-ddos_local.json └── anti-ddos.json ├── filter_list.cpp ├── mod_antiddos.c ├── redis_interface_sync.cpp ├── README.md ├── char_list.cpp ├── filter.cpp ├── anti_ddos_worker.cpp └── config.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.slo 4 | *.out 5 | .libs 6 | 7 | test* 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prog: mod_antiddos.so 2 | 3 | mod_antiddos.so: mod_antiddos.c 4 | apxs -S CC=g++ -lm -c mod_antiddos.c -lhiredis -lstdc++ 5 | -------------------------------------------------------------------------------- /config_example/anti-ddos_local.json: -------------------------------------------------------------------------------- 1 | { 2 | "maxHits": 200, 3 | "blockTime": 3600, 4 | 5 | "filters": [ 6 | { 7 | "useRegex": false, 8 | "request": "blockme", 9 | "score": 1000 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /config_example/anti-ddos.json: -------------------------------------------------------------------------------- 1 | { 2 | "maxHits": 100, 3 | "tickDown": 3, 4 | 5 | "defaults": { 6 | "useRegex": false, 7 | "score": 1, 8 | "applyForCDN": false, 9 | "applyForAssets": false, 10 | "applyForBots": true, 11 | "whitelist": ["127.0.0.1", "::1"] 12 | }, 13 | 14 | "filters": [ 15 | { 16 | "useRegex": true, 17 | "applyForBots": false, 18 | "request": "/.*/", 19 | "score": 1, 20 | "method": "POST", 21 | "refeer": "" 22 | }, 23 | { 24 | "domain": [ "localhost", "example.com", "www.example.com" ], 25 | "score": 5 26 | }, 27 | { 28 | "userAgent": ".*", 29 | "useRegex": true, 30 | "statusCode": 403, 31 | "score": 20 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /filter_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "filter.cpp" 5 | 6 | // dynamic list class 7 | 8 | class FilterList{ 9 | public: 10 | void Add(Filter f){ 11 | count++; 12 | // if currently no entry 13 | if(!content.IsReady()){ 14 | content = f; 15 | content.SetReady(); 16 | return; 17 | } 18 | if(nextEntry == NULL){ 19 | nextEntry = new FilterList(); 20 | } 21 | 22 | nextEntry->Add(f); 23 | } 24 | 25 | Filter Get(int id){ 26 | if(id == 0) 27 | return content; 28 | return nextEntry->Get(--id); 29 | } 30 | 31 | int Count(){ 32 | return count; 33 | } 34 | 35 | private: 36 | int count = 0; 37 | FilterList* nextEntry; 38 | Filter content; 39 | }; -------------------------------------------------------------------------------- /mod_antiddos.c: -------------------------------------------------------------------------------- 1 | #include "anti_ddos_worker.cpp" 2 | 3 | #include "httpd.h" 4 | #include "http_config.h" 5 | #include "http_log.h" 6 | #include "http_request.h" 7 | 8 | static void module_register_hooks(apr_pool_t *p) 9 | { 10 | ap_hook_handler(AntiDDoSWorker::PreRequestHook, NULL, NULL, APR_HOOK_FIRST); 11 | ap_hook_log_transaction(AntiDDoSWorker::PostRequestHook , NULL, NULL, APR_HOOK_MIDDLE); 12 | //ap_register_output_filter("antiddos_output_filter", AntiDDoSWorker::PostContentHook, NULL, AP_FTYPE_RESOURCE); 13 | ap_hook_post_config(AntiDDoSWorker::PostConfigHook, NULL, NULL, APR_HOOK_MIDDLE); 14 | } 15 | 16 | 17 | static const command_rec antiddos_directives[] = 18 | { 19 | AP_INIT_TAKE1("AntiDDoSRedisConnectionType", (const char* (*)())Config::SetRedisConnectionType, NULL, RSRC_CONF, "Connection Type of the redis server (unix, tcp)"), 20 | AP_INIT_TAKE1("AntiDDoSRedisPath", (const char* (*)())Config::SetRedisPath, NULL, RSRC_CONF, "Path of the redis server"), 21 | AP_INIT_TAKE1("AntiDDoSRedisTimeout", (const char* (*)())Config::SetRedisTimeout, NULL, RSRC_CONF, "Timeout for reply from the redis server (in ms)"), 22 | AP_INIT_TAKE1("AntiDDoSRedisPort", (const char* (*)())Config::SetRedisPort, NULL, RSRC_CONF, "Path of the redis server"), 23 | AP_INIT_TAKE1("AntiDDoSConfig", (const char* (*)())Config::SetConfigHook, NULL, RSRC_CONF, "Path of the anti-ddos configuration"), 24 | AP_INIT_TAKE1("AntiDDoSConfigLocal", (const char* (*)())Config::SetConfigLocalHook, NULL, RSRC_CONF, "Path of the local anti-ddos configuration (overwrite)"), 25 | AP_INIT_TAKE1("AntiDDoSBlockCommand", (const char* (*)())Config::SetBlockCommandHook, NULL, RSRC_CONF, "Command to execute when ip is blocked"), 26 | { NULL } 27 | }; 28 | 29 | /* Dispatch list for API hooks */ 30 | module AP_MODULE_DECLARE_DATA antiddos_module = { 31 | STANDARD20_MODULE_STUFF, 32 | NULL, /* create per-dir config structures */ 33 | NULL, /* merge per-dir config structures */ 34 | NULL, /* create per-server config structures */ 35 | NULL, /* merge per-server config structures */ 36 | antiddos_directives, /* table of config file commands */ 37 | module_register_hooks /* register hooks */ 38 | }; -------------------------------------------------------------------------------- /redis_interface_sync.cpp: -------------------------------------------------------------------------------- 1 | // AF_INET 2 | #include 3 | 4 | // redisContext etc. 5 | #include 6 | 7 | class redis_interface_sync{ 8 | public: 9 | redis_interface_sync(int redisProtocol, char* redisUri, int redisPort, int t) { 10 | protocol = redisProtocol; 11 | port = redisPort; 12 | timeout = t; 13 | 14 | uri = (char*)malloc(strlen(redisUri) + 1); 15 | strcpy(uri, redisUri); 16 | 17 | connect(); 18 | } 19 | 20 | ~redis_interface_sync(){ 21 | redisFree(redis_context); 22 | } 23 | 24 | void Set(char* key, char* val){ 25 | redisCommand(redis_context, "SET %s %s", key, val); 26 | return; 27 | } 28 | 29 | void Del(char* key){ 30 | redisCommand(redis_context, "DEL %s", key); 31 | return; 32 | } 33 | 34 | void Inc(char* key){ 35 | redisCommand(redis_context, "INCR %s", key); 36 | return; 37 | } 38 | 39 | void Inc(char* key1, char* key2){ 40 | char* search = (char*)malloc(strlen(key1) + strlen(key2) + 1); 41 | 42 | strcpy(search, key1); 43 | strcat(search, key2); 44 | 45 | Inc(search); 46 | free(search); 47 | } 48 | 49 | void IncBy(char* key, int val){ 50 | redisCommand(redis_context, "INCRBY %s %i", key, val); 51 | return; 52 | } 53 | 54 | void IncBy(char* key1, char* key2, int val){ 55 | char* search = (char*)malloc(strlen(key1) + strlen(key2) + 1); 56 | 57 | strcpy(search, key1); 58 | strcat(search, key2); 59 | 60 | IncBy(search, val); 61 | free(search); 62 | } 63 | 64 | void Dec(char* key){ 65 | redisCommand(redis_context, "DECR %s", key); 66 | return; 67 | } 68 | 69 | void Dec(char* key1, char* key2){ 70 | char* search = (char*)malloc(strlen(key1) + strlen(key2) + 1); 71 | 72 | strcpy(search, key1); 73 | strcat(search, key2); 74 | 75 | Dec(search); 76 | free(search); 77 | } 78 | 79 | void DecBy(char* key, int val){ 80 | redisCommand(redis_context, "DECRBY %s %i", key, val); 81 | return; 82 | } 83 | 84 | void DecBy(char* key1, char* key2, int val){ 85 | char* search = (char*)malloc(strlen(key1) + strlen(key2) + 1); 86 | 87 | strcpy(search, key1); 88 | strcat(search, key2); 89 | 90 | DecBy(search, val); 91 | free(search); 92 | } 93 | 94 | void Del(char* key1, char* key2){ 95 | char* search = (char*)malloc(strlen(key1) + strlen(key2) + 1); 96 | 97 | strcpy(search, key1); 98 | strcat(search, key2); 99 | 100 | Del(search); 101 | free(search); 102 | } 103 | 104 | void SetExpire(char* key, int timeout){ 105 | redisCommand(redis_context, "EXPIRE %s %i", key, timeout); 106 | return; 107 | } 108 | 109 | void SetWithExpire(char* key, char* val, int timeout){ 110 | Set(key,val); 111 | SetExpire(key, timeout); 112 | } 113 | 114 | char* GetByKey(char * key){ 115 | redisReply *reply; 116 | reply = (redisReply*)redisCommand(redis_context, "GET %s", key); 117 | 118 | if(!reply->str){ 119 | freeReplyObject(reply); 120 | return NULL; 121 | } 122 | 123 | char* returnVal = (char*)malloc(strlen(reply->str) + 1); 124 | strcpy(returnVal, reply->str); 125 | freeReplyObject(reply); 126 | return returnVal; 127 | } 128 | 129 | char* GetByKey(char * key1, char * key2){ 130 | char* search = (char*)malloc(strlen(key1) + strlen(key2) + 1); 131 | 132 | strcpy(search, key1); 133 | strcat(search, key2); 134 | 135 | char* returnVal = GetByKey(search); 136 | 137 | free(search); 138 | return returnVal; 139 | } 140 | 141 | timeval GetTimeout(){ 142 | int sec = floor(timeout); 143 | struct timeval t = {sec, timeout - sec}; 144 | return t; 145 | } 146 | 147 | int GetPort(){ 148 | return port; 149 | } 150 | 151 | int GetProtocol(){ 152 | return protocol; 153 | } 154 | 155 | char* GetURI(){ 156 | return uri; 157 | } 158 | 159 | bool IsConnected(){ 160 | return isConnected; 161 | } 162 | 163 | private: 164 | bool isConnected = false; 165 | int protocol; 166 | int port; 167 | int timeout; 168 | char* uri; 169 | redisContext * redis_context; 170 | 171 | bool connect(){ 172 | if(IsConnected()){ 173 | return false; 174 | } 175 | 176 | redisReply *reply; 177 | 178 | if(GetProtocol() == AF_INET){ 179 | // TCP IP connect 180 | redis_context = redisConnectWithTimeout(GetURI(), GetPort(), GetTimeout()); 181 | } else { 182 | // Socket Connection 183 | redis_context = redisConnectUnixWithTimeout(GetURI(), GetTimeout()); 184 | } 185 | 186 | if ( redis_context == NULL || redis_context->err ) { 187 | if (redis_context) { 188 | redisFree(redis_context); 189 | return false; 190 | } 191 | else 192 | { 193 | return false; 194 | } 195 | } 196 | 197 | return true; 198 | } 199 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced DDoS Protection Module for Apache 2 | 3 | ## Bolstering Resilience with Dynamic Scoring 4 | 5 | Defend your online infrastructure against the relentless tide of Distributed Denial-of-Service (DDoS) attacks using our cutting-edge Advanced DDoS Protection Module. This sophisticated solution employs an intelligent scoring system that identifies and neutralizes potential threats in real-time. 6 | 7 | The benefit above solutions like fail2ban is simple - fail2ban parses the request and bans afterwards, causing hugh delays during DDoS situations. 8 | 9 | ### Tailored Scoring Intelligence 10 | 11 | Powered by an ingenious scoring system, our module meticulously assesses incoming requests based on a carefully curated set of filters. This intricate process assigns dynamic scores to each request, empowering you to distinguish benign traffic from potentially malicious activity with unparalleled accuracy. These filters can simply be extended as required. 12 | 13 | ### Reactive Defense Measures 14 | 15 | When the cumulative score of incoming requests breaches a user-defined threshold, our module springs into action. It promptly responds to subsequent requests with a HTTP 429 status code, blocking further expensive requests e.g. on php applications. 16 | 17 | ### Swift, Custom Ban Enforcement 18 | 19 | Elevating security measures, our module seamlessly enforces a custom ban command upon surpassing the threshold. This command serves as an active deterrent, swiftly blocking access from the suspected source and bolstering your system's resistance to sustained attacks. 20 | 21 | ## Granular Filtering Precision 22 | 23 | Experience granular control over your filtering strategy with a diverse array of customizable parameters, empowering you to tailor defenses to your unique environment. 24 | 25 | - `userAgent`: Filter requests based on user agents, a pivotal strategy to pinpoint potentially malicious clients. 26 | - `domain`: Employ domain-based filters to focus on specific sections of your application. Supports lists or single domains. 27 | - `referer`: Evaluate the source of incoming requests with referer-based filters. 28 | - `request`: Scrutinize request content with contextual filtering. 29 | - `method`: Filter requests by their HTTP methods (GET, POST, etc.). 30 | - `statusCode`: Utilize filters based on HTTP response status codes. (work in progress) 31 | - `score`: Assign predetermined scores to requests, fine-tuning the module's assessment. 32 | - `useRegex`: Opt for advanced pattern matching using regular expressions. 33 | - `applyForBots`: Tailor filtering strategies to include or exclude bot traffic. 34 | - `applyForCDN`: Fine-tune filtering policies for Content Delivery Network (CDN) traffic (work in progress). 35 | - `applyForAssets`: Extend filters to safeguard assets like images, scripts, and stylesheets 36 | 37 | Take command of your defense strategy by adjusting critical parameters that shape the module's responsiveness and resilience. These parameters empower you to customize the module's behavior according to your operational needs: 38 | 39 | - `maxHits`: Define the maximum number of hits / score from a single source. Once this threshold is reached, the module springs into action, enhancing protection against potential DDoS onslaughts. 40 | - `tickDown`: Fine-tune the time interval (in seconds) at which the module re-evaluates scores and gradually reduces them. This dynamic mechanism ensures that legitimate traffic can be smoothly reintegrated while maintaining vigilance against malicious activity. 41 | - `blockTime`: Establish the duration (in seconds) for which a source is blocked after surpassing the hit threshold. This strategic setting provides a brief yet impactful deterrence against sustained attacks. 42 | 43 | ## Vertical Scalability Through Redis Integration 44 | 45 | Incorporating a Redis backend, our solution offers more than robust security—it assures vertical scalability. This infrastructure enables effortless scaling of resources to gracefully handle varying traffic loads while sustaining top-notch protection. 46 | 47 | ## Configuration via Apache Directives 48 | 49 | Effortlessly configure the module's behavior with precision using these Apache directives: 50 | 51 | - `AntiDDoSBlockCommand`: Specify format for block command (e.g. `/opt/firewall block %s 3600`) 52 | - `AntiDDoSRedisConnectionType`: Select the Redis server's connection type (unix, tcp). 53 | - `AntiDDoSRedisPath`: Specify the path to the Redis Socket (when using unix as protocol). 54 | - `AntiDDoSRedisTimeout`: Define the timeout for Redis server replies (in ms). 55 | - `AntiDDoSRedisPort`: Configure the Redis server's port. 56 | - `AntiDDoSConfig`: Set the path for the primary anti-DDoS configuration. 57 | - `AntiDDoSConfigLocal`: Specify the path for the local anti-DDoS configuration, enabling strategic selective overwrites. 58 | 59 | ## Installation and Fortified Defense 60 | 61 | 1. Ensure the presence of "apxs", "hiredis" and "g++" utilities. 62 | 2. Clone the repository: `git clone https://github.com/Fabian123333/mod_antiddos.git` 63 | 3. Navigate to the module directory: `cd mod_antiddos` 64 | 4. Execute installation: `make` 65 | 5. Move Plugin to Apache Libdir `mv .libs/mod_antiddos.so /usr/lib/apache2/modules/` 66 | 6. Tailor module options in the Apache configuration file for a seamless integration, minimal config for apache2.conf: 67 | ``` 68 | # apache2.conf: 69 | LoadModule antiddos_module /usr/lib/apache2/modules/mod_antiddos.so 70 | AntiDDoSConfig /etc/apache2/anti-ddos.json 71 | AntiDDoSConfigLocal /etc/apache2/anti-ddos_local.json 72 | ``` 73 | 74 | ## Catalyze Contributions and Nurture Excellence 75 | 76 | We enthusiastically invite contributors to fortify our DDoS Protection Module. If you identify avenues for improvement or wish to contribute, initiate a dialogue through GitHub. Your expertise propels our relentless pursuit of excellence. 77 | 78 | ## Licensing for Empowerment 79 | 80 | This endeavor aligns with the ethos of the GPL3 License. For comprehensive details, peruse the [License file](LICENSE). 81 | -------------------------------------------------------------------------------- /char_list.cpp: -------------------------------------------------------------------------------- 1 | // std::cout 2 | #include 3 | #include 4 | 5 | // uint16_t 6 | #include 7 | 8 | // pow 9 | #include 10 | 11 | // dynamic list class 12 | 13 | class CharList{ 14 | public: 15 | int Count(){ 16 | return count; 17 | } 18 | 19 | char* Get(int id){ 20 | if(id-- == 0) 21 | return content; 22 | return nextEntry->Get(id); 23 | } 24 | 25 | void Add(char* c){ 26 | count++; 27 | // if currently no entry 28 | if(content == NULL){ 29 | content = (char*)malloc(strlen(c) + 1); 30 | strcpy(content, c); 31 | return; 32 | } 33 | if(nextEntry == NULL){ 34 | nextEntry = new CharList(); 35 | } 36 | 37 | nextEntry->Add(c); 38 | } 39 | 40 | bool ContainsIP(char* ip){ 41 | if(content == NULL){ 42 | return false; 43 | } else { 44 | if(ContainsChar(content, '/')) 45 | if(ContainsChar(content, '.')) 46 | if(IPv4CIDRMatch(ip, content)) 47 | return true; 48 | else 49 | if(IPv6CIDRMatch(ip, content)) 50 | return true; 51 | else 52 | if(strcmp(content, ip) == 0) 53 | return true; 54 | } 55 | 56 | if(nextEntry) 57 | return nextEntry->ContainsIP(ip); 58 | return false; 59 | } 60 | 61 | bool Contains(const char* c){ 62 | if(content == NULL){ 63 | return false; 64 | } 65 | if(strcmp(c, content) == 0){ 66 | return true; 67 | } 68 | if(nextEntry == NULL){ 69 | return false; 70 | } 71 | return nextEntry->Contains(c); 72 | } 73 | 74 | private: 75 | CharList* nextEntry; 76 | char* content; 77 | int count = 0; 78 | 79 | bool ContainsChar(const char* str, char c) { 80 | while (*str != '\0') { 81 | if (*str == c) { 82 | return true; 83 | } 84 | str++; 85 | } 86 | return false; 87 | } 88 | 89 | void Split(const char *s, char delimiter, char **tokens, int maxTokens) { 90 | int tokenCount = 0; 91 | while (*s && tokenCount < maxTokens) { 92 | while (*s && *s == delimiter) { 93 | ++s; 94 | } 95 | if (*s) { 96 | const char *start = s; 97 | while (*s && *s != delimiter) { 98 | ++s; 99 | } 100 | int tokenLength = s - start; 101 | tokens[tokenCount] = (char *)malloc(tokenLength + 1); 102 | strncpy(tokens[tokenCount], start, tokenLength); 103 | tokens[tokenCount][tokenLength] = '\0'; 104 | ++tokenCount; 105 | } 106 | } 107 | } 108 | 109 | std::bitset<32> IPv4ToBytes(const char* ip){ 110 | unsigned short a, b, c, d; 111 | sscanf(ip, "%hu.%hu.%hu.%hu", &a, &b, &c, &d); 112 | 113 | std::bitset<8> b1(a); 114 | std::bitset<8> b2(b); 115 | std::bitset<8> b3(c); 116 | std::bitset<8> b4(d); 117 | 118 | std::bitset<32> output_bitset(b1.to_string() + b2.to_string() + b3.to_string() + b4.to_string()); 119 | 120 | return output_bitset; 121 | } 122 | 123 | bool IPv4CIDRMatch(const char* ip, const char* cidr){ 124 | char *cidrParts[2]; 125 | Split(cidr, '/', cidrParts, 2); 126 | 127 | std::bitset<32> ip_bitset = IPv4ToBytes(ip); 128 | std::bitset<32> cidr_bitset = IPv4ToBytes(cidrParts[0]); 129 | 130 | int netmask = atoi(cidrParts[1]); 131 | std::bitset<32> netmask_bitset(pow(2, 32 - netmask) - 1); 132 | 133 | std::bitset<32> network_ip = ip_bitset | netmask_bitset; 134 | std::bitset<32> network_cidr = cidr_bitset | netmask_bitset; 135 | 136 | return network_ip == network_cidr; 137 | } 138 | 139 | std::bitset<128> IPv6ToBytes(const char* ip){ 140 | char* currentBuffer = (char*)malloc(5); 141 | strcpy(currentBuffer, ""); 142 | char lastCharacter; 143 | 144 | std::bitset<16> splitBitset[8]; 145 | int splitid = 0; 146 | 147 | for(int i = 0;; i++){ 148 | if(ip[i] == ':' || ip[i] == '\0'){ 149 | // add leading zeros 150 | while(strlen(currentBuffer) != 4){ 151 | char* newBuffer = (char*)malloc(strlen(currentBuffer) + 1); 152 | strcpy(newBuffer, "0"); 153 | strcat(newBuffer, currentBuffer); 154 | free(currentBuffer); 155 | currentBuffer = newBuffer; 156 | } 157 | 158 | if(lastCharacter == ':'){ 159 | int remainingParts = 0; 160 | 161 | for(int v = i; ip[v] != '\0'; v++){ 162 | if(ip[v] == ':') 163 | remainingParts++; 164 | } 165 | 166 | int skips = 7 - remainingParts - splitid; 167 | 168 | for(int v = 0; v < skips; v++){ 169 | uint16_t part_as_int16 = 0; 170 | splitBitset[splitid++] = part_as_int16; 171 | 172 | continue; 173 | } 174 | } 175 | 176 | char* endPtr; 177 | uint16_t part_as_int16 = static_cast(std::strtoul(currentBuffer, &endPtr, 16)); 178 | 179 | splitBitset[splitid++] = part_as_int16; 180 | 181 | free(currentBuffer); 182 | 183 | if(ip[i] == '\0') 184 | break; 185 | 186 | currentBuffer = (char*)malloc(5); 187 | strcpy(currentBuffer, ""); 188 | } 189 | else{ 190 | char newChar[2]; 191 | newChar[0] = ip[i]; 192 | newChar[1] = '\0'; 193 | strcat(currentBuffer, &newChar[0]); 194 | } 195 | lastCharacter = ip[i]; 196 | } 197 | 198 | std::bitset<128> out_bitset(splitBitset[0].to_string() + splitBitset[1].to_string() + splitBitset[2].to_string() + splitBitset[3].to_string() + splitBitset[4].to_string() + splitBitset[5].to_string() + splitBitset[6].to_string() + splitBitset[7].to_string()); 199 | 200 | return out_bitset; 201 | } 202 | 203 | bool IPv6CIDRMatch(const char* ip, const char* cidr){ 204 | char *cidrParts[2]; 205 | Split(cidr, '/', cidrParts, 2); 206 | 207 | std::bitset<128> ip_bitset = IPv6ToBytes(ip); 208 | std::bitset<128> cidr_bitset = IPv6ToBytes(cidrParts[0]); 209 | 210 | int netmask = atoi(cidrParts[1]); 211 | std::bitset<128> netmask_bitset(pow(2, 128 - netmask) - 1); 212 | 213 | std::bitset<128> network_ip = ip_bitset | netmask_bitset; 214 | std::bitset<128> network_cidr = cidr_bitset | netmask_bitset; 215 | 216 | return network_ip == network_cidr; 217 | } 218 | }; 219 | -------------------------------------------------------------------------------- /filter.cpp: -------------------------------------------------------------------------------- 1 | // std::regex 2 | #include 3 | 4 | #include "char_list.cpp" 5 | 6 | // request_rec 7 | #include 8 | 9 | // Anti DDoS Filter Class, conains individual Filters 10 | 11 | class Filter{ 12 | public: 13 | // Getter definition 14 | char* GetUserAgent(){ 15 | return userAgent; 16 | } 17 | char* GetRefeer(){ 18 | return refeer; 19 | } 20 | CharList GetDomains(){ 21 | return domains; 22 | } 23 | char* GetRequest(){ 24 | return request; 25 | } 26 | char* GetMethod(){ 27 | return method; 28 | } 29 | char* GetContent(){ 30 | return content; 31 | } 32 | int GetStatusCode(){ 33 | return statusCode; 34 | } 35 | int GetScore(){ 36 | return score; 37 | } 38 | bool IsUseRegex(){ 39 | return useRegex; 40 | } 41 | bool ApplyForBots(){ 42 | return applyForBots; 43 | } 44 | bool ApplyForCDN(){ 45 | return applyForCDN; 46 | } 47 | bool ApplyForAssets(){ 48 | return applyForAssets; 49 | } 50 | 51 | // Setter definition 52 | void SetUserAgent(char* value){ 53 | userAgent = (char*)malloc(strlen(value) + 1); 54 | strcpy(userAgent, value); 55 | } 56 | 57 | void AddDomain(char* value){ 58 | domains.Add(value); 59 | } 60 | 61 | void SetRefeer(char* value){ 62 | refeer = (char*)malloc(strlen(value) + 1); 63 | strcpy(refeer, value); 64 | } 65 | 66 | void SetRequest(char* value){ 67 | request = (char*)malloc(strlen(value) + 1); 68 | strcpy(request, value); 69 | } 70 | 71 | void SetMethod(char* value){ 72 | method = toLowerCase(value); 73 | } 74 | 75 | void SetContent(const char* c){ 76 | content = (char*)malloc(strlen(c)); 77 | strcpy(content, c); 78 | } 79 | 80 | void SetStatusCode(int value){ 81 | statusCode = value; 82 | } 83 | void SetScore(int value){ 84 | score = value; 85 | } 86 | void SetUseRegex(bool value){ 87 | useRegex = value; 88 | } 89 | void SetApplyForBots(bool value){ 90 | applyForBots = value; 91 | } 92 | void SetApplyForCDN(bool value){ 93 | applyForCDN = value; 94 | } 95 | void SetApplyForAssets(bool value){ 96 | applyForAssets = value; 97 | } 98 | 99 | bool IsReady(){ 100 | return isReady; 101 | } 102 | void SetReady(){ 103 | isReady = true; 104 | } 105 | 106 | bool UrlIsAsset(const char* url){ 107 | std::regex pattern(".*\\.(jpg|jpeg|png|gif|svg|css|js|ttf|otf|woff|woff2)(\\?.*)*$"); 108 | return std::regex_match(url, pattern); 109 | } 110 | 111 | bool UserAgentIsBot(const char* ua){ 112 | std::regex botPattern("bot|crawler|spider|yahoo|google|bing", std::regex_constants::icase); 113 | return std::regex_search(ua, botPattern); 114 | } 115 | 116 | int GetScore(request_rec *r){ 117 | if(!ApplyForAssets()){ 118 | if(UrlIsAsset(r->unparsed_uri)){ 119 | // std::cerr << "skipped asset request on " << r->unparsed_uri << "\n"; 120 | return 0; 121 | } 122 | } 123 | 124 | if(!ApplyForBots()){ 125 | const apr_array_header_t *fields; 126 | int i; 127 | apr_table_entry_t *e = 0; 128 | 129 | fields = apr_table_elts(r->headers_in); 130 | e = (apr_table_entry_t *) fields->elts; 131 | 132 | for(i = 0; i < fields->nelts; i++) { 133 | char* headerlowercase = toLowerCase(e[i].key); 134 | 135 | if(e[i].key && strcmp(headerlowercase, "user-agent") == 0) 136 | if(UserAgentIsBot(e[i].val)){ 137 | // std::cerr << "skipped bot request on " << r->unparsed_uri << "\n"; 138 | free(headerlowercase); 139 | return 0; 140 | } 141 | 142 | free(headerlowercase); 143 | } 144 | } 145 | 146 | if(GetMethod() != NULL){ 147 | char * m = toLowerCase(r->method); 148 | if(!(strcmp(method, m)) == 0){ 149 | // std::cerr << "skipped filter with 'wrong' method " << r->method << " filter would be " << method << "\n"; 150 | free(m); 151 | return 0; 152 | } 153 | free(m); 154 | } 155 | 156 | // removed domain check, as only filters are passed with matching domains 157 | 158 | if(GetRequest() != NULL){ 159 | if(!CompareValues(r->unparsed_uri, GetRequest(), IsUseRegex())){ 160 | // std::cerr << "skipped request does not match\n"; 161 | return 0; 162 | } 163 | } 164 | 165 | if(GetRefeer() != NULL){ 166 | const apr_array_header_t *fields; 167 | int i; 168 | apr_table_entry_t *e = 0; 169 | 170 | fields = apr_table_elts(r->headers_in); 171 | e = (apr_table_entry_t *) fields->elts; 172 | 173 | for(int i = 0; i < fields->nelts; i++) { 174 | char* headerlowercase = toLowerCase(e[i].key); 175 | 176 | if(e[i].key && strcmp(headerlowercase, "referer") == 0) 177 | if(!CompareValues(e[i].val, GetRefeer(), IsUseRegex())){ 178 | // std::cerr << "skipped bot referer does not match\n"; 179 | free(headerlowercase); 180 | return 0; 181 | } 182 | 183 | free(headerlowercase); 184 | } 185 | } 186 | 187 | if(GetUserAgent() != NULL){ 188 | if(!CompareValues(r->unparsed_uri, GetUserAgent(), IsUseRegex())){ 189 | // std::cerr << "skipped request useragent does not match\n"; 190 | return 0; 191 | } 192 | } 193 | 194 | return GetScore(); 195 | } 196 | 197 | private: 198 | char* userAgent; 199 | char* refeer; 200 | char* request; 201 | char* method; 202 | char* content; 203 | int statusCode = 0; 204 | int score = 1; 205 | bool useRegex = false; 206 | bool applyForBots = true; 207 | bool applyForCDN = true; // TODO implement CDN policies 208 | bool applyForAssets = true; 209 | 210 | CharList domains; 211 | CharList whitelist; 212 | 213 | bool isReady; 214 | 215 | bool CompareValues(const char* target, const char* pattern, bool useRegex){ 216 | if(useRegex == false){ 217 | return (strcmp(target, pattern) == 0); 218 | } 219 | 220 | std::regex p(pattern); 221 | return std::regex_match(target, p); 222 | } 223 | 224 | char* toLowerCase(const char* str) { 225 | size_t length = std::strlen(str); 226 | char* result = new char[length + 1]; 227 | 228 | std::transform(str, str + length, result, [](unsigned char c) { 229 | return std::tolower(c); 230 | }); 231 | 232 | result[length] = '\0'; // Null-Zeichen hinzufügen 233 | 234 | return result; 235 | } 236 | 237 | }; -------------------------------------------------------------------------------- /anti_ddos_worker.cpp: -------------------------------------------------------------------------------- 1 | // FilterList 2 | #include "filter_list.cpp" 3 | 4 | // Config 5 | #include "config.cpp" 6 | 7 | // redis_interface_sync 8 | #include "redis_interface_sync.cpp" 9 | 10 | // request_rec 11 | #include 12 | 13 | // unix timestamp 14 | #include 15 | 16 | #include "http_config.h" // Konfigurations-Header-Datei 17 | #include "http_core.h" // AP_CORE_MODULE_INDEX 18 | #include "http_log.h" // Error-Log-Header-Datei 19 | #include "http_protocol.h"// HTTP-Protokoll-Header-Datei 20 | #include "http_request.h" // Request-Header-Datei 21 | #include "ap_config.h" // Apache-Konfigurations-Header-Datei 22 | 23 | 24 | 25 | char REDIS_IS_BLOCKED_KEY[] = "mod_antiddos_blocked_"; 26 | char REDIS_SCORE_KEY[] = "mod_antiddos_score_"; 27 | char REDIS_CLEANUP_TIMESTAMP_KEY[] = "mod_antiddos_timestamp_"; 28 | 29 | void Log(char* line){ 30 | // std::cout << line << "\n"; 31 | } 32 | 33 | void Log(char* part1, char* part2){ 34 | char* msg = (char*)malloc(strlen(part1) + strlen(part2) + 1); 35 | strcpy(msg, part1); 36 | strcat(msg, part2); 37 | Log(msg); 38 | free(msg); 39 | } 40 | 41 | class AntiDDoSWorker{ 42 | public: 43 | ~AntiDDoSWorker(){ 44 | DisconnectRedisServer(); 45 | } 46 | 47 | AntiDDoSWorker(){ 48 | // LoadConfig(); 49 | ConnectRedisServer(); 50 | } 51 | 52 | static int PostContentHook(ap_filter_t* f, apr_bucket_brigade* bb) { 53 | // todo 54 | return DECLINED; 55 | } 56 | 57 | static int PostRequestHook(request_rec *r){ 58 | // ignore whitelist 59 | if(Config::Whitelist().ContainsIP(r->connection->client_ip)){ 60 | return DECLINED; 61 | } 62 | 63 | AntiDDoSWorker worker = AntiDDoSWorker(); 64 | 65 | int score = 0; 66 | 67 | for(int i = 0; i < Config::FiltersPostRequest().Count(); i++){ 68 | score += Config::FiltersPostRequest().Get(i).GetScore(r); 69 | } 70 | 71 | if(worker.CheckIfIsBlocked(r->connection->client_ip)){ 72 | r->status = 429; 73 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, 74 | "detect blocked access, too many requests: %s", r->connection->client_ip); 75 | 76 | return OK; 77 | } 78 | 79 | for(int i = 0; i < Config::DomainFiltersPostRequest(r->hostname).Count(); i++){ 80 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, 81 | "detect filters for domain from postrequest (%s)", r->hostname); 82 | 83 | score += Config::DomainFiltersPostRequest(r->hostname).Get(i).GetScore(r); 84 | } 85 | 86 | if(score > 0){ 87 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server, 88 | "add score for %s from postrequest (%i)", r->connection->client_ip, score); 89 | 90 | worker.IncreaseScore(r->connection->client_ip, score); 91 | 92 | if(worker.GetScore(r->connection->client_ip) > Config::MaxHits()) 93 | { 94 | worker.TickDownScore(r->connection->client_ip); 95 | if(worker.GetScore(r->connection->client_ip) > Config::MaxHits()){ 96 | worker.Block(r->connection->client_ip); 97 | 98 | r->status = 429; 99 | 100 | ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, 101 | "block access, client exceeded score limit: %s (postrequest)", r->connection->client_ip); 102 | 103 | return OK; 104 | } 105 | 106 | } 107 | } 108 | 109 | return DECLINED; 110 | } 111 | 112 | static int PreRequestHook(request_rec *r){ 113 | if(Config::Whitelist().ContainsIP(r->connection->client_ip)){ 114 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, 115 | "ignore whitelisted ip: %s", r->connection->client_ip); 116 | 117 | return DECLINED; 118 | } 119 | 120 | AntiDDoSWorker worker = AntiDDoSWorker(); 121 | 122 | if(worker.CheckIfIsBlocked(r->connection->client_ip)){ 123 | r->status = 429; 124 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, 125 | "detect blocked access, too many requests: %s", r->connection->client_ip); 126 | 127 | return OK; 128 | } 129 | 130 | int score = 0; 131 | 132 | for(int i = 0; i < Config::FiltersPreRequest().Count(); i++){ 133 | score += Config::FiltersPreRequest().Get(i).GetScore(r); 134 | } 135 | 136 | for(int i = 0; i < Config::DomainFiltersPreRequest(r->hostname).Count(); i++){ 137 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, 138 | "detect filters for domain %s", r->hostname); 139 | 140 | score += Config::DomainFiltersPreRequest(r->hostname).Get(i).GetScore(r); 141 | } 142 | 143 | if(score > 0){ 144 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server, 145 | "add score for %s (%i)", r->connection->client_ip, score); 146 | 147 | worker.IncreaseScore(r->connection->client_ip, score); 148 | 149 | if(worker.GetScore(r->connection->client_ip) > Config::MaxHits()) 150 | { 151 | worker.TickDownScore(r->connection->client_ip); 152 | if(worker.GetScore(r->connection->client_ip) > Config::MaxHits()){ 153 | worker.Block(r->connection->client_ip); 154 | 155 | r->status = 429; 156 | 157 | ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, 158 | "block access, client exceeded score limit: %s", r->connection->client_ip); 159 | 160 | return OK; 161 | } 162 | 163 | } 164 | } 165 | 166 | return DECLINED; 167 | } 168 | 169 | // Apache Post Config Module Hook 170 | static int PostConfigHook(apr_pool_t* pool_a, apr_pool_t* pool_b, apr_pool_t* pool_c, server_rec* s){ 171 | LoadConfig(); 172 | 173 | return OK; 174 | } 175 | 176 | private: 177 | void ConnectRedisServer(){ 178 | redisInterface = new redis_interface_sync(Config::RedisProtocol(), Config::RedisUrl(), Config::RedisPort(), Config::RedisTimeout()); 179 | } 180 | 181 | void DisconnectRedisServer(){ 182 | delete redisInterface; 183 | } 184 | 185 | int TickDownScore(char* ip){ 186 | int lastUpdate = GetTimestamp(ip); 187 | int now = GetCurrentUnixTimestamp(); 188 | 189 | if(lastUpdate == now) 190 | return 0; 191 | 192 | int reduce = (now - lastUpdate) * Config::TickDown(); 193 | DecreaseScore(ip, reduce); 194 | 195 | int newScore = GetScore(ip); 196 | 197 | if(newScore > 0){ 198 | SetTimestamp(ip, now); 199 | } else { 200 | DelScoring(ip); 201 | } 202 | 203 | return newScore; 204 | } 205 | 206 | int GetCurrentUnixTimestamp(){ 207 | std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); 208 | std::time_t timestamp = std::chrono::system_clock::to_time_t(now); 209 | return static_cast(timestamp); 210 | } 211 | 212 | void DelScoring(char* ip){ 213 | redisInterface->Del(REDIS_SCORE_KEY, ip); 214 | redisInterface->Del(REDIS_CLEANUP_TIMESTAMP_KEY, ip); 215 | } 216 | 217 | int GetScore(char* ip){ 218 | char* res = redisInterface->GetByKey(REDIS_SCORE_KEY, ip); 219 | if(res){ 220 | int ret = atoi(res); 221 | free(res); 222 | return ret; 223 | } 224 | 225 | IncreaseScore(ip, 0); 226 | SetTimestampToNow(ip); 227 | 228 | return 0; 229 | } 230 | 231 | int GetTimestamp(char* ip){ 232 | char* res = redisInterface->GetByKey(REDIS_CLEANUP_TIMESTAMP_KEY, ip); 233 | if(res){ 234 | int ret = atoi(res); 235 | free(res); 236 | return ret; 237 | } 238 | 239 | return GetCurrentUnixTimestamp(); 240 | } 241 | 242 | void SetTimestampToNow(char* ip){ 243 | int timestamp = GetCurrentUnixTimestamp(); 244 | 245 | SetTimestamp(ip, timestamp); 246 | } 247 | 248 | void SetTimestamp(char* ip, int timestamp){ 249 | char* redisKey = (char*)malloc(strlen(REDIS_CLEANUP_TIMESTAMP_KEY) + strlen(ip) + 1); 250 | 251 | strcpy(redisKey, REDIS_CLEANUP_TIMESTAMP_KEY); 252 | strcat(redisKey, ip); 253 | 254 | char* redisVal = (char*)malloc(11); 255 | strcpy(redisVal, std::to_string(timestamp).c_str()); 256 | 257 | redisInterface->Set(redisKey, redisVal); 258 | free(redisKey); 259 | free(redisVal); 260 | } 261 | 262 | bool CheckIfIsBlocked(char* ip){ 263 | if(redisInterface->GetByKey(REDIS_IS_BLOCKED_KEY, ip)) 264 | return true; 265 | return false; 266 | } 267 | 268 | void IncreaseScore(char* ip, int score){ 269 | redisInterface->IncBy(REDIS_SCORE_KEY, ip, score); 270 | } 271 | 272 | void DecreaseScore(char* ip, int score){ 273 | redisInterface->DecBy(REDIS_SCORE_KEY, ip, score); 274 | } 275 | 276 | void Block(char* ip){ 277 | char* msg = (char*)malloc(strlen("block ip: ") + strlen(ip) + 1); 278 | 279 | strcpy(msg, "block ip: "); 280 | strcat(msg, ip); 281 | Log(msg); 282 | free(msg); 283 | 284 | // block in redis 285 | char* redisKey = (char*)malloc(strlen(REDIS_IS_BLOCKED_KEY) + strlen(ip) + 1); 286 | 287 | strcpy(redisKey, REDIS_IS_BLOCKED_KEY); 288 | strcat(redisKey, ip); 289 | 290 | char* redisValue = (char*)malloc(2); 291 | strcpy(redisValue, "1"); 292 | 293 | redisInterface->SetWithExpire(redisKey, redisValue, Config::GetBlockTime()); 294 | 295 | free(redisValue); 296 | free(redisKey); 297 | 298 | // perform user defined block commands 299 | char* format = Config::GetBlockCommandFormat(); 300 | 301 | if(format != NULL){ 302 | char* cmd = (char*)malloc(strlen(format) + strlen(ip) - 1); 303 | sprintf(cmd, format, ip); 304 | 305 | /* begin log */ 306 | char* logEntry = (char*)malloc(strlen("execute cmd :") + strlen(cmd) + 1); 307 | strcpy(logEntry, "execute cmd :"); 308 | strcat(logEntry, cmd); 309 | 310 | Log(logEntry); 311 | free(logEntry); 312 | /* end log */ 313 | 314 | if(system(cmd)); 315 | free(cmd); 316 | 317 | } 318 | } 319 | 320 | void Unblock(char* ip){ 321 | char* msg = (char*)malloc(strlen("unblock ip: ") + strlen(ip) + 1); 322 | 323 | strcpy(msg, "unblock ip: "); 324 | strcat(msg, ip); 325 | Log(msg); 326 | free(msg); 327 | 328 | char* redisKey = (char*)malloc(strlen(REDIS_IS_BLOCKED_KEY) + strlen(ip) + 1); 329 | 330 | strcpy(redisKey, REDIS_IS_BLOCKED_KEY); 331 | strcat(redisKey, ip); 332 | 333 | redisInterface->Del(redisKey); 334 | 335 | free(redisKey); 336 | 337 | // perform user defined unblock commands 338 | char* format = Config::GetUnblockCommandFormat(); 339 | 340 | if(format != NULL){ 341 | char* cmd = (char*)malloc(strlen(format) + strlen(ip) - 1); 342 | sprintf(cmd, format, ip); 343 | 344 | /* begin log */ 345 | char* logEntry = (char*)malloc(strlen("execute cmd :") + strlen(cmd) + 1); 346 | strcpy(logEntry, "execute cmd :"); 347 | strcat(logEntry, cmd); 348 | 349 | Log(logEntry); 350 | free(logEntry); 351 | /* end log */ 352 | 353 | system(cmd); 354 | free(cmd); 355 | 356 | } 357 | } 358 | 359 | static void LoadConfig(){ 360 | Config::Parse(Config::ConfigPath()); 361 | Config::Parse(Config::ConfigPathLocal()); 362 | } 363 | 364 | redis_interface_sync* redisInterface; 365 | }; 366 | -------------------------------------------------------------------------------- /config.cpp: -------------------------------------------------------------------------------- 1 | // std::map 2 | #include 3 | 4 | // string 5 | #include 6 | 7 | // std::ifstream 8 | #include 9 | 10 | // nlohmann::json 11 | #include "json.hpp" 12 | 13 | #include "httpd.h" // Allgemeine Apache-Header-Datei 14 | #include "http_config.h" // Konfigurations-Header-Datei 15 | #include "http_core.h" // AP_CORE_MODULE_INDEX 16 | #include "http_log.h" // Error-Log-Header-Datei 17 | #include "http_protocol.h"// HTTP-Protokoll-Header-Datei 18 | #include "http_request.h" // Request-Header-Datei 19 | #include "ap_config.h" // Apache-Konfigurations-Header-Datei 20 | 21 | 22 | #undef APLOG_MODULE_INDEX 23 | #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX 24 | 25 | using json = nlohmann::json; 26 | 27 | class Config{ 28 | public: 29 | Config(){ 30 | filtersPreRequest = FilterList(); 31 | filtersPostRequest = FilterList(); 32 | filtersPostContent = FilterList(); 33 | whitelist = CharList(); 34 | } 35 | 36 | // Config Hooks 37 | 38 | static char* SetConfigLocalHook(cmd_parms *cmd, void *cfg, const char *arg){ 39 | configPathLocal = (char*)malloc(strlen(arg) + 1); 40 | strcpy(configPathLocal, arg); 41 | return NULL; 42 | } 43 | 44 | static char* SetConfigHook(cmd_parms *cmd, void *cfg, const char *arg){ 45 | configPath = (char*)malloc(strlen(arg) + 1); 46 | strcpy(configPath, arg); 47 | return NULL; 48 | } 49 | 50 | static char* SetRedisPath(cmd_parms *cmd, void *cfg, const char *arg){ 51 | redisUrl = (char*)malloc(strlen(arg) + 1); 52 | strcpy(redisUrl, arg); 53 | return NULL; 54 | } 55 | 56 | static char* SetBlockCommandHook(cmd_parms *cmd, void *cfg, const char *arg){ 57 | blockCommandFormat = (char*)malloc(strlen(arg) + 1); 58 | strcpy(blockCommandFormat, arg); 59 | return NULL; 60 | } 61 | 62 | static char* SetRedisConnectionType(cmd_parms *cmd, void *cfg, const char *arg){ 63 | if(strcmp(arg, "unix") == 0 || strcmp(arg, "UNIX") == 0 ) 64 | redisProtocol = AF_UNIX; 65 | redisProtocol = AF_INET; 66 | return NULL; 67 | } 68 | 69 | static char* SetRedisPort(cmd_parms *cmd, void *cfg, const char *arg){ 70 | redisPort = atoi(arg); 71 | return NULL; 72 | } 73 | 74 | static char* SetRedisTimeout(cmd_parms *cmd, void *cfg, const char *arg){ 75 | redisTimeout = atoi(arg); 76 | return NULL; 77 | } 78 | 79 | // Config File Parser 80 | 81 | static void Parse(char* configPath){ 82 | std::ifstream f(configPath); 83 | json data = json::parse(f); 84 | 85 | try{ 86 | data.at("maxHits").get_to(maxHits); 87 | }catch(const std::exception& e){} 88 | 89 | try{ 90 | data.at("blockTime").get_to(blockTime); 91 | }catch(const std::exception& e){} 92 | 93 | try{ 94 | data.at("tickDown").get_to(tickDown); 95 | }catch(const std::exception& e){} 96 | 97 | defaults = new Filter(); 98 | 99 | try{ 100 | defaults->SetUseRegex(data["defaults"]["useRegex"]); 101 | }catch(const std::exception& e){} 102 | 103 | try{ 104 | defaults->SetScore(data["defaults"]["score"]); 105 | }catch(const std::exception& e){} 106 | 107 | try{ 108 | defaults->SetApplyForCDN(data["defaults"]["applyForCDN"]); 109 | }catch(const std::exception& e){} 110 | 111 | try{ 112 | defaults->SetApplyForBots(data["defaults"]["applyForBots"]); 113 | }catch(const std::exception& e){} 114 | 115 | try{ 116 | defaults->SetApplyForAssets(data["defaults"]["applyForAssets"]); 117 | }catch(const std::exception& e){} 118 | 119 | 120 | for (auto& el : data["defaults"]["whitelist"].items()) 121 | { 122 | std::string ip_tmp = el.value().get(); 123 | char* ip = (char*)malloc(ip_tmp.length() + 1); 124 | strcpy(ip, ip_tmp.c_str()); 125 | 126 | whitelist.Add(ip); 127 | free(ip); 128 | } 129 | 130 | for (auto& el : data["filters"].items()) 131 | { 132 | Filter newFilter = Filter(); 133 | 134 | try{ 135 | char* tmp = (char*)malloc(el.value()["userAgent"].get().length()); 136 | strcpy(tmp, el.value()["userAgent"].get().c_str()); 137 | newFilter.SetUserAgent(tmp); 138 | free(tmp); 139 | }catch(const std::exception& e){} 140 | 141 | try{ 142 | for (auto& el2 : el.value()["domain"].items()) 143 | { 144 | std::string tmp = el2.value().get(); 145 | char* tmp2 = (char*)malloc(tmp.length() + 1); 146 | strcpy(tmp2, tmp.c_str()); 147 | 148 | newFilter.AddDomain(tmp2); 149 | free(tmp2); 150 | } 151 | }catch(const std::exception& e){} 152 | 153 | try{ 154 | char* tmp = (char*)malloc(el.value()["domain"].get().length()); 155 | strcpy(tmp, el.value()["domain"].get().c_str()); 156 | newFilter.AddDomain(tmp); 157 | free(tmp); 158 | }catch(const std::exception& e){} 159 | 160 | try{ 161 | char* tmp = (char*)malloc(el.value()["refeer"].get().length()); 162 | strcpy(tmp, el.value()["refeer"].get().c_str()); 163 | newFilter.SetRefeer(tmp); 164 | free(tmp); 165 | }catch(const std::exception& e){} 166 | 167 | try{ 168 | char* tmp = (char*)malloc(el.value()["content"].get().length()); 169 | strcpy(tmp, el.value()["content"].get().c_str()); 170 | newFilter.SetContent(tmp); 171 | free(tmp); 172 | }catch(const std::exception& e){} 173 | 174 | try{ 175 | char* tmp = (char*)malloc(el.value()["request"].get().length()); 176 | strcpy(tmp, el.value()["request"].get().c_str()); 177 | newFilter.SetRequest(tmp); 178 | free(tmp); 179 | }catch(const std::exception& e){} 180 | 181 | try{ 182 | char* tmp = (char*)malloc(el.value()["method"].get().length()); 183 | strcpy(tmp, el.value()["method"].get().c_str()); 184 | newFilter.SetMethod(tmp); 185 | free(tmp); 186 | }catch(const std::exception& e){} 187 | 188 | try{ 189 | newFilter.SetStatusCode(el.value()["statusCode"]); 190 | }catch(const std::exception& e){} 191 | 192 | try{ 193 | newFilter.SetUseRegex(el.value()["useRegex"]); 194 | }catch(const std::exception& e){} 195 | 196 | try{ 197 | newFilter.SetScore(el.value()["score"]); 198 | }catch(const std::exception& e){} 199 | 200 | try{ 201 | newFilter.SetApplyForCDN(el.value()["applyForCDN"]); 202 | }catch(const std::exception& e){} 203 | 204 | try{ 205 | newFilter.SetApplyForBots(el.value()["applyForBots"]); 206 | }catch(const std::exception& e){} 207 | 208 | try{ 209 | newFilter.SetApplyForAssets(el.value()["applyForAssets"]); 210 | }catch(const std::exception& e){} 211 | 212 | if(newFilter.GetContent() != NULL){ 213 | // PostContent 214 | if(newFilter.GetDomains().Count() != 0) 215 | { 216 | for(int i = 0; i < newFilter.GetDomains().Count(); i++){ 217 | domainFiltersPostContent[newFilter.GetDomains().Get(i)].Add(newFilter); 218 | } 219 | } 220 | else 221 | filtersPostContent.Add(newFilter); 222 | } 223 | else if(newFilter.GetStatusCode() != 0){ 224 | // PostProcessing 225 | if(newFilter.GetDomains().Count() != 0) 226 | { 227 | for(int i = 0; i < newFilter.GetDomains().Count(); i++){ 228 | domainFiltersPostRequest[newFilter.GetDomains().Get(i)].Add(newFilter); 229 | } 230 | } 231 | else 232 | filtersPostRequest.Add(newFilter); 233 | } else { 234 | // PreProcessing 235 | if(newFilter.GetDomains().Count() != 0) 236 | { 237 | for(int i = 0; i < newFilter.GetDomains().Count(); i++){ 238 | domainFiltersPreRequest[newFilter.GetDomains().Get(i)].Add(newFilter); 239 | } 240 | } 241 | else 242 | filtersPreRequest.Add(newFilter); 243 | } 244 | 245 | } 246 | 247 | f.close(); 248 | } 249 | 250 | static FilterList FiltersPreRequest(){ 251 | return filtersPreRequest; 252 | } 253 | 254 | static FilterList FiltersPostRequest(){ 255 | return filtersPostRequest; 256 | } 257 | 258 | static FilterList FiltersPostContent(){ 259 | return filtersPostContent; 260 | } 261 | 262 | static FilterList DomainFiltersPreRequest(const char* domain){ 263 | std::string str = domain; 264 | return domainFiltersPreRequest[domain]; 265 | } 266 | 267 | static FilterList DomainFiltersPostRequest(const char* domain){ 268 | std::string str = domain; 269 | return domainFiltersPostRequest[domain]; 270 | } 271 | 272 | static FilterList DomainFiltersPostContent(const char* domain){ 273 | std::string str = domain; 274 | return domainFiltersPostContent[domain]; 275 | } 276 | 277 | static int TickDown(){ 278 | return tickDown; 279 | } 280 | 281 | static int GetBlockTime(){ 282 | return blockTime; 283 | } 284 | 285 | static char* GetBlockCommandFormat(){ 286 | return blockCommandFormat; 287 | } 288 | 289 | static char* GetUnblockCommandFormat(){ 290 | return unblockCommandFormat; 291 | } 292 | 293 | static char* ConfigPath(){ 294 | return configPath; 295 | } 296 | 297 | static char* ConfigPathLocal(){ 298 | return configPathLocal; 299 | } 300 | 301 | static int MaxHits(){ 302 | return maxHits; 303 | } 304 | 305 | static char* RedisUrl(){ 306 | if(redisUrl == NULL){ 307 | redisUrl = (char*)malloc(strlen("127.0.0.1") + 1); 308 | strcpy(redisUrl, "127.0.0.1"); 309 | } 310 | return redisUrl; 311 | } 312 | 313 | static int RedisPort(){ 314 | return redisPort; 315 | } 316 | 317 | static int RedisProtocol(){ 318 | return redisProtocol; 319 | } 320 | 321 | static int RedisTimeout(){ 322 | return redisTimeout; 323 | } 324 | 325 | static CharList Whitelist(){ 326 | return whitelist; 327 | } 328 | 329 | private: 330 | static int maxHits; 331 | static int tickDown; 332 | static int blockTime; 333 | 334 | static FilterList filtersPreRequest; 335 | static FilterList filtersPostRequest; 336 | static FilterList filtersPostContent; 337 | 338 | static std::map domainFiltersPreRequest; 339 | static std::map domainFiltersPostRequest; 340 | static std::map domainFiltersPostContent; 341 | 342 | static Filter* defaults; 343 | static CharList whitelist; 344 | 345 | static char* blockCommandFormat; 346 | static char* unblockCommandFormat; 347 | static char* configPathLocal; 348 | static char* configPath; 349 | static char* redisUrl; 350 | static int redisPort; 351 | static int redisProtocol; 352 | static int redisTimeout; 353 | }; 354 | 355 | int Config::maxHits = 1000; 356 | int Config::tickDown = 5; 357 | int Config::blockTime = 60; 358 | Filter* Config::defaults; 359 | FilterList Config::filtersPreRequest; 360 | FilterList Config::filtersPostRequest; 361 | FilterList Config::filtersPostContent; 362 | 363 | std::map Config::domainFiltersPreRequest; 364 | std::map Config::domainFiltersPostRequest; 365 | std::map Config::domainFiltersPostContent; 366 | 367 | CharList Config::whitelist; 368 | 369 | char* Config::blockCommandFormat; 370 | char* Config::unblockCommandFormat; 371 | char* Config::configPathLocal; 372 | char* Config::configPath; 373 | 374 | char* Config::redisUrl; 375 | int Config::redisPort = 6379; 376 | int Config::redisProtocol = AF_INET; 377 | int Config::redisTimeout = 1500; 378 | --------------------------------------------------------------------------------