You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.9 KiB
PHP
208 lines
7.9 KiB
PHP
<?php
|
|
|
|
// Minimum message length checks
|
|
const MSG_MIN_CHARS = 20;
|
|
const MSG_MIN_WORDS = 5;
|
|
// Banned words check, file should be one match (word or phrase) per line
|
|
const BANNED_WORDLIST = __DIR__ . "/../resources/net.contactspam/bannedwords.txt";
|
|
// Banned email domain check, one domain per line, useful if you get lots of spam from
|
|
// a domain your customers probably won't legitimately use
|
|
const BANNED_DOMAINS = __DIR__ . "/../resources/net.contactspam/banneddomains.txt";
|
|
const BANNED_IP_LIST = __DIR__ . "/../resources/net.contactspam/bannedips.txt";
|
|
const BANNED_IP_CIDR = __DIR__ . "/../resources/net.contactspam/toxic_ip_cidr.txt";
|
|
// Domains to skip looking up for SURBL
|
|
const WHITELIST_DOMAINS = __DIR__ . "/../resources/net.contactspam/whitelistdomains.txt";
|
|
|
|
$message = $VARS["message"] ?? "";
|
|
$fromemail = $VARS["email"] ?? "";
|
|
$clientip = $VARS["ipaddr"] ?? "";
|
|
$contactformdomain = trim(strtolower($VARS["domain"] ?? ""));
|
|
|
|
$msg_lower = trim(strtolower($message));
|
|
$email_lower = trim(strtolower($fromemail));
|
|
$email_parts = explode("@", $email_lower);
|
|
$email_domain = $email_parts[count($email_parts) - 1];
|
|
|
|
//
|
|
// If message too short (chars and/or words)
|
|
//
|
|
if (isset($VARS["message"])) {
|
|
if (strlen($msg_lower) < MSG_MIN_CHARS) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "length_chars", "hit" => "", "message" => "Message too short."]);
|
|
} else if (str_word_count($msg_lower) < MSG_MIN_WORDS) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "length_words", "hit" => "", "message" => "Message too short."]);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check email domain
|
|
//
|
|
$banneddomainlist = file(BANNED_DOMAINS, FILE_IGNORE_NEW_LINES);
|
|
foreach ($banneddomainlist as $domain) {
|
|
if ($email_domain == $domain) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "domain", "hit" => $domain, "message" => "Emails from \"" . htmlspecialchars($domain) . "\" are not allowed because of spam/abuse." . ($domain == "googlemail.com" ? " (Hint: use gmail.com instead)" : "")]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if email address is sketchy
|
|
//
|
|
if (!empty($email_parts) && count($email_parts) == 2) {
|
|
if ($email_parts[0] == $email_parts[1]) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "email_fake", "hit" => "", "message" => "Unacceptable email address."]);
|
|
}
|
|
}
|
|
if (!empty($contactformdomain)) {
|
|
if ($contactformdomain == $email_domain && $email_parts[0] != "test") {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "email_self", "hit" => "", "message" => "Please use your own email address, not ours."]);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check for blocked spammy words
|
|
//
|
|
$bannedwordlist = file(BANNED_WORDLIST, FILE_IGNORE_NEW_LINES);
|
|
foreach ($bannedwordlist as $word) {
|
|
if (strpos($msg_lower, $word) !== false) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "word", "hit" => $word, "message" => "\"" . htmlspecialchars($word) . "\" is not allowed because of spam/abuse."]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lookup reported client IP address against stopforumspam.com CIDR range list
|
|
//
|
|
/**
|
|
* https://stackoverflow.com/a/594134
|
|
*/
|
|
function cidr_match($ip, $range) {
|
|
list ($subnet, $bits) = explode('/', $range);
|
|
if ($bits === null) {
|
|
$bits = 32;
|
|
}
|
|
$ip = ip2long($ip);
|
|
$subnet = ip2long($subnet);
|
|
$mask = -1 << (32 - $bits);
|
|
$subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
|
|
return ($ip & $mask) == $subnet;
|
|
}
|
|
|
|
if (filter_var($clientip, FILTER_VALIDATE_IP, [FILTER_FLAG_IPV4])) {
|
|
$bannedipcidrlist = file(BANNED_IP_CIDR, FILE_IGNORE_NEW_LINES);
|
|
foreach ($bannedipcidrlist as $cidr) {
|
|
if (cidr_match($clientip, $cidr)) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "toxic_ip_cidr", "hit" => $clientip, "message" => "Your computer's IP address is on a spam blacklist."]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lookup reported client IP address against stopforumspam.com full IP list
|
|
//
|
|
if (filter_var($clientip, FILTER_VALIDATE_IP, [FILTER_FLAG_IPV4])) {
|
|
$bannediplist = file(BANNED_IP_LIST, FILE_IGNORE_NEW_LINES);
|
|
foreach ($bannediplist as $ip) {
|
|
if ($clientip == $ip) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "banned_ip", "hit" => $clientip, "message" => "Your computer's IP address is blacklisted for sending spam."]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Lookup reported client IP address to see if it's in a botnet or something and sending spam
|
|
//
|
|
try {
|
|
if (filter_var($clientip, FILTER_VALIDATE_IP, [FILTER_FLAG_IPV4])) {
|
|
$blacklist = "xbl.spamhaus.org";
|
|
$url = implode(".", array_reverse(explode(".", $clientip))) . "." . $blacklist;
|
|
|
|
// Cache IPs so we don't do a DNS lookup each time
|
|
$cacheresp = $memcache->get("net.contactspam.$url");
|
|
if ($cacheresp !== false) {
|
|
$dns_result = $cacheresp;
|
|
} else {
|
|
$dns_result = `host -t A $url b.gns.spamhaus.org`;
|
|
$memcache->set("net.contactspam.$url", $dns_result, 60 * 60 * 24);
|
|
}
|
|
if (strpos($dns_result, "NXDOMAIN") === false && strpos($dns_result, "127.0.") !== false) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "spamhaus_xbl", "hit" => $clientip, "message" => "Your computer or a device on your network is infected with a virus and is sending spam messages, so our system is blocking your message."]);
|
|
}
|
|
}
|
|
} catch (Exception $ex) {
|
|
|
|
}
|
|
|
|
//
|
|
// Check domains in message against blacklists
|
|
//
|
|
$lists = [
|
|
"multi.surbl.org",
|
|
"dbl.spamhaus.org",
|
|
"black.uribl.com"
|
|
];
|
|
try {
|
|
// Matches domain names
|
|
$regex = "/([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+)/i";
|
|
|
|
preg_match_all($regex, urldecode($message), $matches);
|
|
|
|
// Remove any domains on the whitelist before doing lookup
|
|
$domainlist = [];
|
|
$whitelistdomainlist = file(WHITELIST_DOMAINS, FILE_IGNORE_NEW_LINES);
|
|
foreach ($matches[0] as $match) {
|
|
$match = strtolower($match);
|
|
$found = false;
|
|
foreach ($whitelistdomainlist as $domain) {
|
|
if ($domain == $match) {
|
|
$found = true;
|
|
}
|
|
}
|
|
if (!$found) {
|
|
$domainlist[] = $match;
|
|
}
|
|
}
|
|
|
|
foreach ($domainlist as $d) {
|
|
|
|
foreach ($lists as $blacklist) {
|
|
$url = "$d.$blacklist";
|
|
|
|
// Cache IPs so we don't do a DNS lookup each time
|
|
$cacheresp = $memcache->get("net.contactspam.$url");
|
|
if ($cacheresp !== false) {
|
|
$dns_result = $cacheresp;
|
|
} else {
|
|
$dns_results = dns_get_record($url);
|
|
$dns_result = count($dns_results) > 0;
|
|
$memcache->set("net.contactspam.$url", "$dns_result", 60 * 60 * 24);
|
|
}
|
|
if ($dns_result) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "$blacklist", "hit" => $d, "message" => "Your message contains a domain ($d) that has been linked to recent spam or criminal activity. Message not sent."]);
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception $ex) {
|
|
|
|
}
|
|
|
|
// Check local spammer database
|
|
if (env("require_database")) {
|
|
if (!empty($clientip)) {
|
|
if ($database->has("net_contactspam_spammers", ["ip" => $clientip])) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "netsyms_ip_blacklist", "hit" => $clientip, "message" => "A computer at your IP address has sent spam in the past. Your message has been blocked."]);
|
|
}
|
|
}
|
|
if (!empty($email_lower)) {
|
|
if ($database->has("net_contactspam_spammers", ["email" => $email_lower])) {
|
|
exitWithJson(["status" => "OK", "clean" => false, "filter" => "netsyms_email_blacklist", "hit" => $email_lower, "message" => "Someone put your email as the from address on a spam message. Your message has been blocked."]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Well if we got here then the message tested negative for spam
|
|
//
|
|
exitWithJson(["status" => "OK", "clean" => true, "filter" => null, "hit" => null]);
|