/* This is a TCP service used to send blacklisted connection. It does nothing with the connection by default (just hangup). This is experimental. It should do stuff like this Does some logging inexpensive logging to show how blacklisted IP are active. It could takes some time to hangup, slowing down the intruder. It could send total crap to cause problems to the intruder. The blackhole server has no way to discard connections. Well, it does it if there are no rules, but it does not have a way to ignore stuff. When a connection request is rejects, this goes in the reject list which may in turn produce an alarm. So having a way to simply route request to /dev/null may be useful, thus this service. */ #include #include #include #include #include #include #include #include #include #include #include #include "fdpass.h" #include #include #include using namespace std; enum CLIENT_TYPE{ TYPE_NULL, TYPE_CLIENT, TYPE_CONTROL, TYPE_IDLE }; struct HANDLE_INFO: public ARRAY_OBJ{ CLIENT_TYPE type; time_t start; // When this connection started unsigned long source; // Source IP unsigned short port; // Target port unsigned long count; // How many bytes received so far void init(){ type = TYPE_NULL; start = time(NULL); source = 0; port = 0; count = 0; } HANDLE_INFO(CLIENT_TYPE _type, unsigned long _source, unsigned short _port){ init(); type = _type; source = _source; port = _port; } HANDLE_INFO(CLIENT_TYPE _type){ init(); type = _type; } }; #include "proto/blackhole_devnull_control.protoh" struct IPPORT{ unsigned long addr; unsigned short port; IPPORT(unsigned long _addr, unsigned long _port){ addr = _addr; port = _port; } bool operator < (const IPPORT &n) const { bool ret = false; if (addr < n.addr){ ret = true; }else if (addr == n.addr){ if (port < n.port){ ret = true; } } return ret; } }; struct STATS{ long count; time_t last; STATS(){ last = (time_t)0; count = 0; } }; int main (int argc, char *argv[]) { glocal const char *bind = NULL; glocal vector ports; glocal const char *pidfile = "/var/run/blackhole-devnull.pid"; glocal const char *user = "blackhole"; glocal bool daemon = false; glocal const char *control = "/var/run/blackhole/blackhole-devnull.sock"; glocal const char *notifycmd = NULL; glocal int maxhandles = 3000; int ret = (argc, argv); setproginfo ("blackhole-devnull",VERSION ,"This is the blackhole /dev/null service. Discarded\n" "connections (matching blacklist IP) are sent to this service\n"); setarg ('b',"bind","bind on this IP",glocal.bind,false); setarg ('p',"port","Setup the service on these ports",glocal.ports,true); setarg (' ',"notify","Execute this command to record new IP numbers",glocal.notifycmd,false); setgrouparg ("Misc."); setarg (' ',"daemon","Run in background",glocal.daemon,false); setarg (' ',"pidfile","Write the daemon pid in this file",glocal.pidfile,false); setarg (' ',"user","Run as this user",glocal.user,false); setarg (' ',"maxhandles","Maximum number of file handle supported",glocal.maxhandles,false); glocal map stats; (glocal.bind,glocal.ports[0].c_str(),1); if (strncmp(info.port,"unix:",5)==0){ info.data = new HANDLE_INFO(TYPE_CONTROL); }else{ time_t now = time(NULL); unsigned short port = atoi(info.port); info.data = new HANDLE_INFO(TYPE_CLIENT,from,port); IPPORT ipport (from,port); map::iterator it = glocal.stats.find(ipport); bool notify = false; if (it != glocal.stats.end()){ it->second.count++; // Send a notify only once an hour to avoid // overloading the system if (it->second.last + 60*60 < now) notify = true; it->second.last = now; }else{ // New entry, we must notify notify = true; STATS &st = glocal.stats[ipport]; st.count++; st.last = now; } if (notify && glocal.notifycmd != NULL){ char ip[20]; ipnum_ip2a (from,ip); string cmd = string(glocal.notifycmd) + " " + ip + " " + info.port; system (cmd.c_str()); } setrawmode (true); } HANDLE_INFO *n = (HANDLE_INFO*)info.data; if (n->type == TYPE_CLIENT){ // We do nothing, just read what is sent n->count += info.linelen; }else if (n->type == TYPE_CONTROL){ (this,line,endserver,endclient,no,n); glocal.TCPSERVER.sendf ("blackhole-devnull version %s\n",VERSION); glocal.TCPSERVER.sendf ("maxhandles: %d\n",glocal.maxhandles); int nbcli = glocal.TCPSERVER.getnbclients()-2; glocal.TCPSERVER.sendf ("Connection number: %d\n",nbcli); int attempt = 0; set ips; for (map::iterator it=glocal.stats.begin(); it != glocal.stats.end(); it++){ attempt += it->second.count; ips.insert (it->first.addr); } glocal.TCPSERVER.sendf ("Attempt %d from %zu different ips\n",attempt,ips.size()); void *data; int fd = glocal.TCPSERVER.iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ char ip[20]; DATEASC start; ipnum_ip2a (n->source,ip); fdpass_asctime (n->start,start); glocal.TCPSERVER.sendf ("fd=%d from %s to %u rec %lu date %s\n",fd,ip,n->port,n->count,start.buf); } fd = glocal.TCPSERVER.iter_next (data); } for (map::iterator it=glocal.stats.begin(); it != glocal.stats.end(); it++){ char ip[20]; DATEASC last; ipnum_ip2a (it->first.addr,ip); fdpass_asctime (it->second.last,last); glocal.TCPSERVER.sendf ("%s %u %s %ld\n",ip,it->first.port,last.buf,it->second.count); } endserver = true; bool ison = atoi(on)==1; if (ison){ debug_seton(); }else{ debug_setoff(); } debug_setfdebug (filename); glocal.TCPSERVER.send ("Invalid command\n"); endclient = true; }else if (n->type == TYPE_IDLE){ void *data; int fd = iter_init(data); time_t now = time(NULL); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT && n->start + 60 < now){ closeclient (fd); } fd = iter_next (data); } }else{ tlmp_error ("Unknown client type, odd\n"); endclient = true; } if (s.is_ok()){ for (unsigned i=1; i return ret; }