From e63d932ab83d69dbd803e2131559baa02a78d74f Mon Sep 17 00:00:00 2001 From: Jon Foster Date: Fri, 14 May 2021 17:28:48 -0700 Subject: [PATCH] Wildcard ignore match & C++ < 11 --- Makefile | 10 +++- iptraffic.cpp | 144 ++++++++++++++++++++++++++-------------------------------- 2 files changed, 73 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index 06d0c7b..6ffb7d7 100644 --- a/Makefile +++ b/Makefile @@ -3,4 +3,12 @@ run: iptraffic ./iptraffic 2> log # 2>&1 | head -n 20 iptraffic: iptraffic.cpp - j++ -o $@ $@.cpp + g++ -o $@ $@.cpp + + + +.PHONY: clean distclean +clean: + rm *.o || true +distclean: clean + rm iptraffic || true diff --git a/iptraffic.cpp b/iptraffic.cpp index 43fb132..dee0d3b 100644 --- a/iptraffic.cpp +++ b/iptraffic.cpp @@ -6,12 +6,18 @@ // The idea is to analyze iptables LOG entries in combination with // DNSmasq's query log entries and combine them to list the hosts // that were accessed. The main reasons for not just inspecting HTTP -// packets through a netfilter socket is due to httpS hiding the +// packets through a netfilter socket is due to HTTPS hiding the // "host" field. So I'm deducing based on DNS query timing. +// +// NOTE: its assumed that the log being processed is in chronological +// order. This is the usual way things get logged. ;-) +// +// 2021-05-14 +// Dumbed down for C++ < 11. ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// -// AAdditional Router setup: +// Additional Router setup: // // ipset -N evilhosts iphash // ipset -N evilnets nethash @@ -23,10 +29,10 @@ // 10.10.10.1 -> 134.215.160.1 ICMP[8] // ////////////////////////////////////////////////////////////////////// -// TODO: wildcard for name matching // TODO: map names according to time and host. time is probably automatic #include +#include #include #include #include @@ -41,6 +47,7 @@ using namespace std; // Splits: a util class to devide a line into space sep pieces ////////////////////////////////////////////////////////////////////// // TODO: implement begin() + end() to make "for( : )" work +// TODO: implement field enclosing & escaping chars struct Splits { @@ -106,20 +113,28 @@ struct TSV: public Splits { ////////////////////////////////////////////////////////////////////// -// Subclass to match a list of prefixes against a string +// Function to match a list of prefixes against a string // -// This is not a subclass per-se since I don't know how to expose the -// inherited { } list based constructor and I just want to knock this -// out. So this is Q&D: typedef + operator. +// Since C++ < 11 doesn't support constant vector initialization we'll +// do this the old fashioned way with a null terminated char*[]. ////////////////////////////////////////////////////////////////////// -typedef vector PreMatch; -bool operator==(const PreMatch &list, const string &s) { - for(auto p: list) if(s.substr(0, p.size())==p) return true; +bool pre_match(char **list, const string &s) { + const char *p = s.c_str(); + for(; *list; list++) + if(!strncmp(*list, p, strlen(*list))) return true; return false; } -inline bool operator!=(const PreMatch &list, const string &s) { return !(list==s); } - +/* And because I think this may be useful in the future I'll hang on to it. +bool pre_match(const vector &list, const string &s) { + for( + vector::const_iterator p=list.begin(); + p!=list.end(); + p++ + ) + if(s.substr(0, p->size())==*p) return true; + return false; +}*/ ////////////////////////////////////////////////////////////////////// @@ -184,21 +199,25 @@ struct Conn { // TODO: does < > have any actual meaning in this context? int cmp(const Conn >r) const { - if(usgtr.us) return 1; + if(us!="*" && gtr.us!="*") { + if(usgtr.us) return 1; + } // TODO: auto-wildcard port based on in? if(us_port && gtr.us_port) { // 0 = no comparison wildcard if(us_portgtr.us_port) return 1; } - if(themgtr.them) return 1; + if(them!="*" && gtr.them!="*") { + if(themgtr.them) return 1; + } if(them_port && gtr.them_port) { // 0 = no comparison wildcard if(them_portgtr.them_port) return 1; } // TODO: do we want to consider the name? - if(name!="") { + if(name!="*" && gtr.name!="*") { if(namegtr.name) return 1; } @@ -264,10 +283,10 @@ struct ConnList: public vector { ////////////////////////////////////////////////////////////////////// struct LiveBug { - string seq = "-\\|/"; - char pre = '\r'; - int p; - LiveBug(): p(0) {} + string seq; + char pre; + int p; + LiveBug(): seq("-\\|/"), pre('\r'), p(0) {} inline char next() { if(p>=seq.size()) p=0; return seq[p++]; } }; ostream &operator<<(ostream &o, LiveBug &bug) { @@ -283,16 +302,17 @@ ostream &operator<<(ostream &o, LiveBug &bug) { typedef map NameVal; -const PreMatch us = { "10.10.10.", "192.168.255.", "2001:470:a:169:" }; -const PreMatch dns_ignore = { "v=spf1", "https:" }; -const PreMatch dns_del = { "NODATA-", "NXDOMAIN-" }; +// TODO: define us[] in conf file +char *us[] = { (char *)"10.10.10.", (char *)"192.168.255.", (char *)"2001:470:a:169:", 0 }; +char *dns_ignore[] = { (char *)"v=spf1", (char *)"https:", 0 }; +char *dns_del[] = { (char *)"NODATA-", (char *)"NXDOMAIN-", 0 }; #define PATH "/srv/backups/iptraffic" ifstream log(PATH "/test.log"); ofstream out(PATH "/processed.log"); Splits ln; int lnno = 0, ict = 0; LiveBug bug; -NameVal rdns, queries; +NameVal rdns; NameVal::iterator nvp; string name, address, s; Conn conn; @@ -302,13 +322,19 @@ ConnList ignores; void dlog(const string msg) { +#ifdef DEBUG cerr << "\r" << lnno << ": " << msg << endl; +#endif } int main(int argc, char **argv) { + /// Load lists /// + + + /// Read in ignore list /// { @@ -332,60 +358,21 @@ int main(int argc, char **argv) { // TODO: need to get more specific on tying us + them + time to DNS if(ln.count>8 && strncmp(ln.fields[4], "dnsmasq[", 8)==0) { - - /// Query send /// - - if(strncmp(ln.fields[5], "query[", 6)==0) { - s=ln[5].substr(6, ln[5].size()-7); - if(s!="A" && s!="AAAA") continue; // we're only concerned with addresses. - name = ln[6]; - address = ln[8]; - dlog("Query["+s+"] '"+name+"' for "+address); - name+=':'+s; - if(queries.find(name)==queries.end()) - queries[name]=address; - else - dlog("WARN: Query already exists!"); - - /// Query reply /// - - } else if(ln[5]=="reply") { + if(ln[5]=="reply") { name = ln[6]; address = ln[8]; // Hmm... is this reply an address? - if(dns_ignore==address) continue; // nope - if(dns_del==address) { - // "no exist" reply so just drop them. - if(*(address.end()-1)=='4') name+=":A"; - else if(*(address.end()-1)=='6') name+=":AAAA"; - else continue; - dlog("drop query '"+name+"'"); - if((nvp=queries.find(name))!=queries.end()) queries.erase(nvp); - continue; + if(pre_match(dns_ignore, address)) continue; // nope + if(pre_match(dns_del, address)) continue; // does not exist reply + if((nvp=rdns.find(address))!=rdns.end()) { + if(nvp->second==name) continue; + dlog("WARN: DNS address overlap "+address+": "+nvp->second+" : "+name); } - // IPv6 or v4 query? - if(address.find(':')==name.npos) - s=name+":AAAA"; - else - s=name+":A"; - // now make source dest couplet - if((nvp=queries.find(s))!=queries.end()) { - address+=':'+nvp->second; - //queries.erase(nvp); // remove from active query list - if((nvp=rdns.find(address))!=rdns.end()) { - if(nvp->second==name) continue; + rdns[address] = name; + dlog("Added "+address+" = "+name); #ifdef DEBUG - dlog("WARN: DNS address overlap "+address+": "+rdnsp->second+" : "+name); + cout << '\r' << lnno << ": " << name << endl; #endif - } - rdns[address] = name; - dlog("Added "+address+" = "+name); -#ifdef DEBUG - cout '\r' << lnno << ": " << name << endl; -#endif - continue; - } - dlog("WARN: reply '"+name+"' skipped due to lack of matching query"); continue; } } @@ -397,19 +384,16 @@ int main(int argc, char **argv) { && ln[5]=="ACCEPT" ) { conn = ln; - if(us!=conn.us) conn.swap(); - if((nvp=rdns.find(conn.them+':'+conn.us))!=rdns.end()) + if(!pre_match(us, conn.us)) conn.swap(); + if((nvp=rdns.find(conn.them))!=rdns.end()) conn.name = nvp->second; if(ignores.find(conn)<0) - out << conn << "\n"; + out << ln[0] << " " << ln[1] << " " << ln[2] << " " << conn << "\n"; else ict++; } } cout << "\nIgnored: " << ict << endl; - -#ifdef DEBUG - cout << "\n\n" << "Total rDNS: " << rdns.size() << "\n"; -#endif + cout << "Total rDNS: " << rdns.size() << "\n"; return 0; }