////////////////////////////////////////////////////////////////////// // IP traffic analyzer - data objects // Written by Jonathan A. Foster // Started April 23rd, 2021 // Copyright JF Possibilities, Inc. All rights reserved. ////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include "data.h" ////////////////////////////////////////////////////////////////////// // Utils ////////////////////////////////////////////////////////////////////// std::string ipv6opt(const std::string &addr) { in6_addr buf; char s[256]; if(inet_pton(AF_INET6, addr.c_str(), &buf)<1) throw std::runtime_error("ipv6opt: inet_pton() says '"+addr+"' is not a valid IPv6 address"); if(!inet_ntop(AF_INET6, &buf, s, 255)) throw // should never happen std::runtime_error("ipv6opt: inet_ntop() refused to convert the address back"); return std::string(s); } int addr_wild_comp(const std::string &str1, const std::string &str2) { int spre1=0, spre2=0; if(str1=="*" || str2=="*") return 0; if(str1!="" && (str1.end()[-1]=='.' || str1.end()[-1]==':')) spre1=str1.size(); if(str2!="" && (str2.end()[-1]=='.' || str2.end()[-1]==':')) spre2=str2.size(); if(spre2>spre1) spre1=spre2; if(spre1) return strncmp(str1.c_str(), str2.c_str(), spre1); else if(str1str2) return 1; return 0; } ////////////////////////////////////////////////////////////////////// // Conn ////////////////////////////////////////////////////////////////////// void Conn::clear() { us = them = name = protocol = ""; in=false; us_port = them_port = 0; } void Conn::compact() { if(us.find(':')!=us.npos) us=ipv6opt(us); if(them.find(':')!=us.npos) them=ipv6opt(them); } void Conn::swap() { std::string s; int x; s = us; us = them; them =s; x = us_port; us_port = them_port; them_port = x; in=!in; } Conn &Conn::operator=(const Splits &sp) { int x; clear(); for(x=0; xgtr.name) return 1; } if(protocolgtr.protocol) return 1; if(ingtr.in) return 1; return 0; } std::ostream &operator<<(std::ostream &out, const Conn &c) { out << c.us << ( c.in ? " <- " : " -> " ) << c.them << " " << c.protocol << "[" << ( c.in ? c.us_port : c.them_port ) << "] " << c.name; return out; } const Splits &operator>>(const Splits &tsv, Conn &conn) { if(tsv.count<7) throw std::runtime_error("Conn=TSV: too few columns"); conn.clear(); conn.us = tsv[0]; conn.us_port = atoi(tsv.fields[1]); conn.them = tsv[2]; conn.them_port = atoi(tsv.fields[3]); conn.name = tsv[4]; conn.protocol = tsv[5]; conn.in = tsv[6]=="1"; return tsv; } ////////////////////////////////////////////////////////////////////// // ConnList ////////////////////////////////////////////////////////////////////// int ConnList::find(Conn &needle) { int r; for(r=0; r8 && strncmp(ln.fields[4], "dnsmasq[", 8)==0) { if(ln[5]=="reply" || ln[5]=="cached") { name = ln[6]; address = ln[8]; /* NOTE: CNAME resolution seems to follow this order in logs: 1. A result line (reply/cached) with an address of 2. One or more consecutive result lines for the canonical name Looking over the logs it doesn't appear that dnsmasq will log anything between the original and CNAME resolutions. The exception is if a CNAME record is cached and it has to resolve what it points to. In this case there would be a "cached" and then a "forwarded" record eventually followed by "reply ... ". In that case we want to operate on the reply. */ /* record we're handling a CNAME cycle */ if(address=="") { alias = name; cname = ""; return 0; } /* If in cname _mode_: */ if(alias!="") { if(cname=="") { cname = name; /* This is our target name */ name = alias; /* substitute the alias */ } else if(cname==name) { name = alias; /* substitute the alias */ } else { cname = ""; /* These are different records reset */ name = ""; } } // Hmm... is this reply an address? if(pre_match(dns_ignore, address)) return 0; // nope if(pre_match(dns_del, address)) return 0; // does not exist reply if((nvp=rdns.find(address))!=rdns.end()) { if(nvp->second==name) return 0; //dlog("WARN: DNS address overlap "+address+": "+nvp->second+" : "+name); } rdns[address] = name; //dlog("Added "+address+" = "+name); return 0; } else if(alias!="") { alias = ""; /* we've fallen out of CNAME resolution. */ cname = ""; } } /// process connections /// if((ln.count>5 // old Linux style && ln[4]=="kernel:" && ln[5]=="ACCEPT" ) || (ln.count>6 // new Linux style && ln[4]=="vmunix:" && ln[6]=="ACCEPT") ) { conn = ln; conn.compact(); if(!pre_match(*us, conn.us)) conn.swap(); if((nvp=rdns.find(conn.them))!=rdns.end()) conn.name = nvp->second; return 1; } return 0; }