From 446baea9b316b0d00ed947280ac712d70bbb7443 Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Sun, 18 Dec 2016 21:31:26 +0000 Subject: Init commit --- dmarcpipe.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100755 dmarcpipe.py (limited to 'dmarcpipe.py') diff --git a/dmarcpipe.py b/dmarcpipe.py new file mode 100755 index 0000000..5e26d34 --- /dev/null +++ b/dmarcpipe.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +from dmarc import parse_dmarc +from getopt import gnu_getopt +from gzip import decompress as gz_decompress +from io import BytesIO +from re import compile as re_compile +from sql import store_dmarc +from sys import stdin, stdout, argv +from zipfile import ZipFile +import email + +default_enc = 'US-ASCII' +policy_domain = 'the-tk.com' +re_valid_filename = re_compile('^[^\\s!]+![^\\s!]+![0-9]+![0-9]+(![^\\s!]+)?.(xml(.gz)?|zip)$') +logfile = None +dbfile = 'dmarc.db' + +class FalseReportException(Exception): + pass + +def is_valid_filename(f): + if re_valid_filename.match(f) is not None: + return True + return False + +def zip2xml(data): + z = ZipFile(BytesIO(data)) + n = z.namelist() + if len(n) != 1 or not is_valid_filename(n[0]): + raise FalseReportException('zip2xml: broken zip len({})'.format(len(n))) + return z.open(n[0]).read() + +gzip2xml = lambda data: gz_decompress(data) + +decoders = { + 'application/zip': zip2xml, + 'application/x-zip': zip2xml, + 'application/x-zip-compressed': zip2xml, + 'application/zlib': gzip2xml, + 'application/gzip': gzip2xml, + 'text/xml': lambda data: data, + } + +def process_message(m): + if m.get_content_maintype() != 'multipart' and \ + m.get_content_type() not in decoders.keys(): + raise FalseReportException('invalid content type: {}'.format(m.get_content_type())) + + att = None + inv_fn = False + + for part in m.walk(): + if part.get_content_maintype() == 'multipart': + continue + if part.get_content_type() not in decoders.keys(): + continue + if part.get('Content-Disposition') is None: + continue + if not is_valid_filename(part.get_filename()): + inv_fn = True + continue + att = part + break + + if att is None: + raise FalseReportException('attachment not found{}'.format(inv_fn == True and ' (invalid filename)' or '')) + + xml = decoders[att.get_content_type()](att.get_payload(decode=True)) + dmarc = parse_dmarc(xml) + store_dmarc(dbfile, dmarc) + +def main(): + global logfile, dbfile + opts, args = gnu_getopt(argv, 'l:') + for o in opts: + if o[0] == '-l': + logfile = o[1] + if len(args) == 2: + print(args[1]) + dbfile = args[1] + m = email.message_from_file(stdin) + try: + process_message(m) + m['X-DMARC-Report'] = 'True' + except Exception as e: + m['X-DMARC-Report'] = 'False; {}'.format(repr(e)) + finally: + stdout.write(m.as_string()) + +if __name__ == '__main__': + main() -- cgit v1.2.3-54-g00ecf