checkdos.cc (4225B)
1 #include "dispatcher" 2 3 typedef map < long, std::queue<time_t> > AccessMap; 4 static AccessMap accesslog; 5 static time_t accesslog_lastclean = 0; 6 7 // Execute an external program upon excess of hard/soft rates 8 static void run_excess(string const &prog, char const *ip) { 9 ostringstream o; 10 o << prog << ' ' << ip; 11 msg("Max connection rate exceeded, invoking '" << o.str() << '\n'); 12 int ret = sysrun(o.str()); 13 if (ret == -1) { 14 throw Error(string("Failed to start system call: ") + 15 strerror(errno)); 16 } else if (ret) { 17 warnmsg("Program '" << o.str() << 18 "' exited with exit status " << ret << '\n'); 19 } else { 20 msg("Program '" << o.str() << "' finished.\n"); 21 } 22 } 23 24 bool Dispatcher::check_dos() { 25 msg ("Verifying DOS protection\n"); 26 Threadlist::desc("Verifying"); 27 28 static int lock; 29 30 // Check 'softmaxconnrate' and 'hardmaxconnrate' now! 31 // Descend into this block if connrate_time() is set, AND 32 // either hardmaxconnrate() is set, 33 // or both softmaxconnrate() and defertime() are set. 34 if (config.connrate_time() && 35 (config.hardmaxconnrate() || 36 (config.softmaxconnrate() && config.defertime()))) { 37 38 // The map lookup/insert key. 39 struct sockaddr_in sa = clientfd().clientaddr(); 40 long key; 41 memset(&key, 0, sizeof(key)); 42 memcpy(&key, &(sa.sin_addr), sizeof(sa.sin_addr)); 43 44 time_t now, min_ts; 45 now = time(0); 46 min_ts = now - config.connrate_time(); 47 unsigned max_conns = max(config.hardmaxconnrate(), 48 config.softmaxconnrate()); 49 50 mutex_lock (&lock); 51 accesslog[key].push(now); 52 mutex_unlock (&lock); 53 54 if (accesslog_lastclean < min_ts) { 55 // Clean the entire access log, it's been a while... 56 57 mutex_lock(&lock); 58 accesslog_lastclean = now; 59 mutex_unlock(&lock); 60 61 bool done = false; 62 while (!done) { 63 done = true; 64 for (AccessMap::iterator i = accesslog.begin(); 65 i != accesslog.end(); 66 i++ ) { 67 if (accesslog[i->first].back() < min_ts) { 68 // This IP hasn't made ANY connections in a while 69 // -- erase! Also restart loop, i++ won't be valid 70 // after erase(i) 71 accesslog.erase(i); 72 done = false; 73 break; 74 } else { 75 // Keep popping off this IP's oldest connection 76 // until we have only "recent" connections left. 77 mutex_lock(&lock); 78 while ( accesslog[i->first].front() < min_ts 79 || accesslog[i->first].size() > max_conns ) 80 accesslog[i->first].pop(); 81 mutex_unlock(&lock); 82 } 83 } 84 } 85 } else { 86 // The "big log" doesn't need to be fully cleaned, 87 // but this particular IP should be! 88 mutex_lock(&lock); 89 while ( (accesslog[key].front() < min_ts) 90 || 91 (accesslog[key].size() > max_conns) 92 ) { 93 accesslog[key].pop(); 94 } 95 mutex_unlock(&lock); 96 } 97 98 if ( config.hardmaxconnrate() && 99 (accesslog[key].size() >= config.hardmaxconnrate()) ) { 100 // This IP has violated the "HARD" limit! Reject the connection 101 ostringstream o; 102 o << "Client " << inet2string(clientfd().clientaddr().sin_addr) 103 << " has hit the HARD maximum number of connections (" 104 << config.hardmaxconnrate() << " conections in " 105 << config.connrate_time() << " seconds; " 106 << accesslog[key].size() 107 << " connections recorded). Client is refused.\n"; 108 warnmsg (o.str()); 109 run_excess(config.hardmaxconnexcess(), 110 inet2string(clientfd().clientaddr().sin_addr).c_str()); 111 return false; 112 } else if ( config.softmaxconnrate() && 113 (accesslog[key].size() >= config.softmaxconnrate()) ) { 114 // This IP has violated the "SOFT" Limit. Go to sleep for a while. 115 ostringstream o; 116 o << "Client " << inet2string(clientfd().clientaddr().sin_addr) 117 << " has hit the SOFT maximum number of connections (" 118 << config.softmaxconnrate() << " connections in " 119 << config.connrate_time() << " sedonds; " 120 << accesslog[key].size() 121 << " connections recorded). Client is deferred for " 122 << config.defertime() << " microseconds.\n"; 123 warnmsg (o.str()); 124 run_excess(config.softmaxconnexcess(), 125 inet2string(clientfd().clientaddr().sin_addr).c_str()); 126 usleep(config.defertime()); 127 } 128 } 129 130 return true; 131 }