This commit is contained in:
Sean Whalen
2020-02-17 16:24:11 -05:00
parent 0ae15ed90c
commit d0bb858e74
6 changed files with 239 additions and 20 deletions

View File

@@ -1,3 +1,9 @@
6.9.0
-----
- Use system nameservers instead of Cloudflare by default
- Parse aggregate report records with missing SPF domains
6.8.2
-----

View File

@@ -83,8 +83,7 @@ CLI help
-o OUTPUT, --output OUTPUT
write output files to the given directory
-n NAMESERVERS [NAMESERVERS ...], --nameservers NAMESERVERS [NAMESERVERS ...]
nameservers to query (default is Cloudflare's
nameservers)
nameservers to query
-t DNS_TIMEOUT, --dns_timeout DNS_TIMEOUT
number of seconds to wait for an answer from DNS
(default: 2.0)
@@ -201,16 +200,6 @@ The full set of configuration options are:
- ``message`` - str: The email message (Default: Please see the attached parsedmarc report.)
.. warning::
It is **strongly recommended** to **not** use the ``nameservers`` setting.
By default, ``parsedmarc`` uses `Cloudflare's public resolvers`_,
which are much faster and more reliable than Google, Cisco OpenDNS, or
even most local resolvers.
The ``nameservers`` option should only be used if your network blocks DNS
requests to outside resolvers.
.. warning::
``save_aggregate`` and ``save_forensic`` are separate options because

View File

@@ -33,7 +33,7 @@ from parsedmarc.utils import is_outlook_msg, convert_outlook_msg
from parsedmarc.utils import timestamp_to_human, human_timestamp_to_datetime
from parsedmarc.utils import parse_email
__version__ = "6.8.2"
__version__ = "6.9.0"
logging.basicConfig(
format='%(levelname)8s:%(filename)s:%(lineno)d:'
@@ -86,10 +86,6 @@ def _parse_report_record(record, offline=False, nameservers=None,
Returns:
OrderedDict: The converted record
"""
if nameservers is None:
nameservers = ["1.1.1.1", "1.0.0.1",
"2606:4700:4700::1111", "2606:4700:4700::1001",
]
record = record.copy()
new_record = OrderedDict()
new_record_source = get_ip_address_info(record["row"]["source_ip"],
@@ -168,6 +164,8 @@ def _parse_report_record(record, offline=False, nameservers=None,
if type(auth_results["spf"]) != list:
auth_results["spf"] = [auth_results["spf"]]
for result in auth_results["spf"]:
if "domain" not in result:
result["domain"] = None
new_result = OrderedDict([("domain", result["domain"])])
if "scope" in result and result["scope"] is not None:
new_result["scope"] = result["scope"]

View File

@@ -161,8 +161,7 @@ def _main():
arg_parser.add_argument("-o", "--output",
help="write output files to the given directory")
arg_parser.add_argument("-n", "--nameservers", nargs="+",
help="nameservers to query "
"(default is Cloudflare's nameservers)")
help="nameservers to query")
arg_parser.add_argument("-t", "--dns_timeout",
help="number of seconds to wait for an answer "
"from DNS (default: 2.0)",

View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<feedback>
<report_metadata>
<org_name>zoho.com</org_name>
<email>noreply-dmarc@zoho.com</email>
<extra_contact_info>https://www.zoho.com/mail/help/adminconsole/dmarc-policy.html</extra_contact_info>
<report_id>e2cb5d97-dcbb-470f-b2dd-45519a2abbb3</report_id>
<date_range>
<begin>1581753600</begin>
<end>1581840000</end>
</date_range>
</report_metadata>
<policy_published>
<domain>example.com</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>none</p>
<pct>100</pct>
<fo>0</fo>
</policy_published>
<record>
<row>
<source_ip>216.71.146.18</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
<scope>mfrom</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.143.97</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<result>none</result>
<scope>helo</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.146.224</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
<scope>mfrom</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.143.87</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
<scope>mfrom</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.143.62</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
<scope>mfrom</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.143.52</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<result>none</result>
<scope>helo</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.148.142</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
<scope>mfrom</scope>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>216.71.148.247</source_ip>
<count>2</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<result>pass</result>
<domain>example.com</domain>
<selector>s1</selector>
</dkim>
<spf>
<domain>example.com</domain>
<result>pass</result>
<scope>mfrom</scope>
</spf>
</auth_results>
</record>
</feedback>

View File

@@ -14,7 +14,7 @@ from setuptools import setup
from codecs import open
from os import path
__version__ = "6.8.2"
__version__ = "6.9.0"
description = "A Python package and CLI for parsing aggregate and " \
"forensic DMARC reports"