diff options
author | nico <nico@magicbroccoli.de> | 2019-07-17 17:56:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-17 17:56:15 +0200 |
commit | 47e577ebaf07e4785a7a7cabfa402c36087fa1eb (patch) | |
tree | 6eea0eefd3b31d56c5530355f925251528131341 /report.py | |
parent | 4e3d7d431d22cdb627943ea872a3c06650b052a3 (diff) |
Code cleanup (#2)
* config class revamp
+ add get_at method
+ add set_at method
+ add unset_at method
new config class to interactively get/set/unset config parameters from within other methods
* move report function to new file
+ move all report functions to new report class
the new report class works exactly like the internal one, but it is easier to maintain
* + implement the new config functions
+ implement the changed report functions
+ update docstrings
+ update code comments
* code cleanup
* further code cleanup
+ add variable type to all methods
* many indentation fixes
Diffstat (limited to 'report.py')
-rw-r--r-- | report.py | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/report.py b/report.py new file mode 100644 index 0000000..6357db5 --- /dev/null +++ b/report.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +import dns.resolver as dns +import tabulate + + +class ReportDomain: + def __init__(self, config, conn): + """ + :param config: configuration object + :param conn: sqlite connection object + """ + self.config = config + self.conn = conn + + def template(self, template: str, domain: str, query: list): + """ + method to retrieve and format the template file + :param template: string containing the abuse report template + :param domain: string containing a domain name + :param query: list of tuples containing the query results for the specified domain/s + :return: string containing the fully formatted abuse report + """ + name = self.config.get_at("name") + + # lookup and format srv target and ip + srv, ips = self.srv(domain) + summary = tabulate.tabulate(query, headers=["messages", "bots", "domain", "first seen", "last seen"], + tablefmt="github") + + report_out = template.format(name=name, domain=domain, srv=srv, ips=ips, summary=summary) + + return report_out + + def jids(self, domain: str): + """ + method to collect all involved jids from the database + :param domain: string containing a domain name + :return: formatted result string + """ + + jids = self.conn.execute('''SELECT user || '@' || domain as jid FROM spam WHERE domain=:domain GROUP BY user + ORDER BY 1;''', {"domain": domain}).fetchall() + + return tabulate.tabulate(jids, tablefmt="plain") + + def logs(self, domain: str): + """ + method to collect all messages grouped by frequency + :param domain: string containing a domain name + :return: formatted string containing the result + """ + logs = self.conn.execute('''SELECT CHAR(10) || MIN(ts) || ' - ' || MAX(ts) || char(10) || COUNT(*) || + 'messages:' || char(10) ||'========================================================================' || + char(10) || message || char(10) || '========================================================================' + FROM spam WHERE domain=:domain GROUP BY message ORDER BY COUNT(*) DESC LIMIT 10;''', {"domain": domain}).fetchall() + + return tabulate.tabulate(logs, tablefmt="plain") + + def srv(self, domain: str, only_highest: bool = True): + info = self._srvlookup(domain) + + if only_highest: + target = info[0]["host"] + ips = info[0]["ip"] + + return target, ips + + return info + + @staticmethod + def _getip(domain: str): + """ + method to query the a / aaaa record of a specified domain + :param domain: valid domain target + :return: filtered list of all a/ aaaa records + """ + # init records + a, a4 = None, None + + try: + # query and join both a and aaaa records + a = ", ".join([ip.address for ip in dns.query(domain, "A")]) + a4 = ", ".join([ip.address for ip in dns.query(domain, "AAAA")]) + + except (dns.NXDOMAIN, dns.NoAnswer): + # catch NXDOMAIN and NoAnswer tracebacks not really important + pass + + return list(filter(None.__ne__, [a, a4])) + + def _srvlookup(self, domain: str): + """ + srv lookup method for the domain provided, if no srv record is found the base domain is used + :param domain: provided domain to query srv records for + :return: sorted list of dictionaries containing host and ip info + """ + # init + results = list() + srv_records = None + + try: + srv_records = dns.query('_xmpp-client._tcp.{}'.format(domain), 'SRV') + + except (dns.NXDOMAIN, dns.NoAnswer): + # catch NXDOMAIN and NoAnswer tracebacks + pass + + # extract record + if srv_records is not None: + # extract all available records + for record in srv_records: + info = dict() + + # gather necessary info from srv records + info["host"] = record.target.to_text().rstrip('.') + info["port"] = record.port + info["weight"] = record.weight + info["priority"] = record.priority + info["ip"] = ", ".join(self._getip(record.target.to_text())) + results.append(info) + + # return list sorted by priority and weightre + return sorted(results, key=lambda i: (i['priority'], i["weight"])) + + # prevent empty info when srv records are not present + info = dict() + + # gather necessary info from srv records + info["host"] = domain + info["ip"] = ", ".join(self._getip(domain)) + results.append(info) + + return results |