git subrepo clone https://github.com/mailcow/mailcow-dockerized.git mailcow/src/mailcow-dockerized

subrepo: subdir:   "mailcow/src/mailcow-dockerized"
  merged:   "a832becb"
upstream: origin:   "https://github.com/mailcow/mailcow-dockerized.git"
  branch:   "master"
  commit:   "a832becb"
git-subrepo: version:  "0.4.3"
  origin:   "???"
  commit:   "???"
Change-Id: If5be2d621a211e164c9b6577adaa7884449f16b5
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/spf.inc.php b/mailcow/src/mailcow-dockerized/data/web/inc/spf.inc.php
new file mode 100644
index 0000000..199f572
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/spf.inc.php
@@ -0,0 +1,143 @@
+<?php
+error_reporting(0);
+
+function get_spf_allowed_hosts($check_domain)
+{
+	$hosts = array();
+	
+	$records = dns_get_record($check_domain, DNS_TXT);
+	foreach ($records as $record)
+	{
+		$txt = explode(' ', $record['entries'][0]);
+		if (array_shift($txt) != 'v=spf1') // only handle SPF records
+			continue;
+		
+		foreach ($txt as $mech)
+		{
+			$qual = substr($mech, 0, 1);
+			if ($qual == '-' || $qual == '~') // only handle pass or neutral records
+				continue(2);
+			
+			if ($qual == '+' || $qual == '?')
+				$mech = substr($mech, 1); // remove the qualifier
+			
+			if (strpos($mech, '=') !== FALSE) // handle a modifier
+			{
+				$mod = explode('=', $mech);
+				if ($mod[0] == 'redirect') // handle a redirect
+				{
+					$hosts = get_spf_allowed_hosts($mod[1]);
+					return $hosts;
+				}
+			}
+			else
+			{
+				unset($cidr);
+				// reset domain to check_domain
+				$domain = $check_domain;
+				if (strpos($mech, ':') !== FALSE) // handle a domain specification
+				{
+					$split = explode(':', $mech);
+					$mech = array_shift($split);
+					$domain = implode(':', $split);
+					if (strpos($domain, '/') !== FALSE) // remove CIDR specification
+					{
+						$split = explode('/', $domain);
+						$domain = $split[0];
+						$cidr = $split[1];
+					}
+				}
+				
+				$new_hosts = array();
+        if ($mech == 'include' && $check_domain != $domain) // handle an inclusion
+				{
+					$new_hosts = get_spf_allowed_hosts($domain);
+				}
+				elseif ($mech == 'a') // handle a mechanism
+				{
+					$new_hosts = get_a_hosts($domain);
+				}
+				elseif ($mech == 'mx') // handle mx mechanism
+				{
+					$new_hosts = get_mx_hosts($domain);
+				}
+				elseif ($mech == 'ip4' || $mech == 'ip6') // handle ip mechanism
+				{
+					$new_hosts = array($domain);
+				}
+				
+				if (isset($cidr)) // add CIDR specification if present
+				{
+					foreach ($new_hosts as &$host)
+					{
+						$host .= '/' . $cidr;
+					}
+					unset($host);
+				}
+				
+				$hosts = array_unique(array_merge($hosts,$new_hosts), SORT_REGULAR);
+			}
+		}
+	}
+	foreach ($hosts as &$host) {
+		if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+			$host = $host;
+		}
+	}
+	return $hosts;
+}
+
+
+function get_mx_hosts($domain)
+{
+	$hosts = array();
+  try {
+    $mx_records = dns_get_record($domain, DNS_MX);
+    if ($mx_records) {
+      foreach ($mx_records as $mx_record) {
+        $new_hosts = get_a_hosts($mx_record['target']);
+        $hosts = array_unique(array_merge($hosts,$new_hosts), SORT_REGULAR);
+      }
+    }
+  }
+  catch (Exception $e) {
+    if ($e->getMessage() !== 'dns_get_record(): A temporary server error occurred.') {
+      throw $e;
+    }
+    $mx_records = false;
+  }
+	return $hosts;
+}
+
+function get_a_hosts($domain)
+{
+	$hosts = array();
+	
+	$a_records = dns_get_record($domain, DNS_A);
+	foreach ($a_records as $a_record)
+	{
+		$hosts[] = $a_record['ip'];
+	}
+	$a_records = dns_get_record($domain, DNS_AAAA);
+	foreach ($a_records as $a_record)
+	{
+		$hosts[] = $a_record['ipv6'];
+	}
+	
+	return $hosts;
+}
+
+function get_outgoing_hosts_best_guess($domain)
+{
+	// try the SPF record to get hosts that are allowed to send outgoing mails for this domain
+	$hosts = get_spf_allowed_hosts($domain);
+	if ($hosts) return $hosts;
+	
+	// try the MX record to get mail servers for this domain
+	$hosts = get_mx_hosts($domain);
+	if ($hosts) return $hosts;
+	
+	// fall back to the A record to get the host name for this domain
+	return get_a_hosts($domain);
+}
+?>