def _evaluate_mechanism(self, ip, domain, sender, mechanism, rvalue): if rvalue is None: rvalue = domain else: rvalue = self.expand_macros(rvalue, ip, domain, sender) if mechanism == 'a': if self._hostname_matches_ip(ip, rvalue): return True elif mechanism == 'all': return True elif mechanism == 'exists': if len(self._dns_query(rvalue, 'A')): return True elif mechanism == 'include': # pass results in match per https://tools.ietf.org/html/rfc7208#section-5.2 return self._check_host(ip, rvalue, sender, top_level=False) == SPFResult.PASS elif mechanism == 'ip4': try: if its.py_v2 and isinstance(rvalue, str): rvalue = rvalue.decode('utf-8') ip_network = ipaddress.IPv4Network(rvalue, strict=False) except ipaddress.AddressValueError: raise SPFParseError('invalid IPv4 network: ' + rvalue) if ip in ip_network: return True elif mechanism == 'ip6': try: if its.py_v2 and isinstance(rvalue, str): rvalue = rvalue.decode('utf-8') ip_network = ipaddress.IPv6Network(rvalue, strict=False) except ipaddress.AddressValueError: raise SPFParseError('invalid IPv6 network: ' + rvalue) if ip in ip_network: return True elif mechanism == 'mx': for mx_record in self._dns_query(rvalue, 'MX'): mx_record = str(mx_record.exchange).rstrip('.') if self._hostname_matches_ip(ip, mx_record): return True elif mechanism == 'ptr': if isinstance(ip, ipaddress.IPv4Address): ip = str(ip) suffix = 'in-addr' else: ip = '.'.join(ip.exploded.replace(':', '')) suffix = 'ip6' ptr_domain = (rvalue or domain) ip = ip.split('.') ip.reverse() ip = '.'.join(ip) for ptr_record in self._dns_query(ip + '.' + suffix + '.arpa', 'PTR'): ptr_record = str(ptr_record.target).rstrip('.') if ptr_domain == ptr_record or ptr_domain.endswith('.' + ptr_record): return True else: raise SPFPermError( "unsupported mechanism type: '{0}'".format(mechanism)) return False
def _evaluate_mechanism(self, ip, domain, sender, mechanism, rvalue): if rvalue is None: rvalue = domain else: rvalue = self.expand_macros(rvalue, ip, domain, sender) if mechanism == 'a': if self._hostname_matches_ip(ip, rvalue): return True elif mechanism == 'all': return True elif mechanism == 'exists': answers, _ = self._dns_query(rvalue, 'A') if len(answers): return True elif mechanism == 'include': # pass results in match per https://tools.ietf.org/html/rfc7208#section-5.2 return self._check_host(ip, rvalue, sender, top_level=False) == SPFResult.PASS elif mechanism == 'ip4': try: if its.py_v2 and isinstance(rvalue, str): rvalue = rvalue.decode('utf-8') ip_network = ipaddress.IPv4Network(rvalue, strict=False) except ipaddress.AddressValueError: raise SPFParseError('invalid IPv4 network: ' + rvalue) if ip in ip_network: return True elif mechanism == 'ip6': try: if its.py_v2 and isinstance(rvalue, str): rvalue = rvalue.decode('utf-8') ip_network = ipaddress.IPv6Network(rvalue, strict=False) except ipaddress.AddressValueError: raise SPFParseError('invalid IPv6 network: ' + rvalue) if ip in ip_network: return True elif mechanism == 'mx': answers, additional = self._dns_query(rvalue, 'MX') for answer in answers: hostname = None if answer.rdtype == dns.rdatatype.MX: hostname = _to_hostname(answer.exchange) elif answer.rdtype == dns.rdatatype.CNAME: hostname = _to_hostname(answer.target) else: raise ValueError('answer is not an MX or CNAME record') found, matches = self._hostname_matches_additional(ip, hostname, additional) if matches: return True if not found and self._hostname_matches_ip(ip, hostname): return True elif mechanism == 'ptr': if isinstance(ip, ipaddress.IPv4Address): ip = str(ip) suffix = 'in-addr' else: ip = '.'.join(ip.exploded.replace(':', '')) suffix = 'ip6' ptr_domain = (rvalue or domain) ip = ip.split('.') ip.reverse() ip = '.'.join(ip) answers, _ = self._dns_query(ip + '.' + suffix + '.arpa', 'PTR') for ptr_record in answers: hostname = _to_hostname(ptr_record.target) if ptr_domain == hostname or ptr_domain.endswith('.' + hostname): return True else: raise SPFPermError("unsupported mechanism type: '{0}'".format(mechanism)) return False