index.php

<?php
    
//Mail functions. If you view the source, it's attached below this file.
    
require('_Mail.php');

    
//Set here if source code viewing is allowed
    
define('ALLOW_SOURCE',TRUE);
    
//Permit API usage
    
define('ALLOW_API',TRUE);

    
//Characters used to replace sensitive information in the source. Any valid HTML.
    //To be extra boring, just use 8 asterisks
    
define('SOURCE_REPLACE',
        
'<span style="color:#ff0000;">&#9608;</span>'.
        
'<span style="color:#ff7f00;">&#9608;</span>'.
        
'<span style="color:#ffbf00;">&#9608;</span>'.
        
'<span style="color:#ffff00;">&#9608;</span>'.
        
'<span style="color:#00ff00;">&#9608;</span>'.
        
'<span style="color:#00ff80;">&#9608;</span>'.
        
'<span style="color:#00ffff;">&#9608;</span>'.
        
'<span style="color:#0000ff;">&#9608;</span>');
    
//define('SOURCE_REPLACE','********');

    /*//Remove this box to prevent remote sites from using this*/
    /**/
header('Access-Control-Allow-Origin: *');              //
    /**/
header('Access-Control-Allow-Methods: GET');           //
    /**//////////////////////////////////////////////////////////

    
$addr=($_POST && isset($_POST['addr']))?$_POST['addr']:'';

    
//Source view
    
if(ALLOW_SOURCE && isset($_GET['source'])){
        
//HTML frame START
        
echo '<html><head><title>Mail Checker Source</title></head><body style="font-family:Sans-Serif;font-size:large"><h1>index.php</h1>';
        
//This file
        
highlight_file(__FILE__);
        
//Mail include file
        
echo '<h1>_Mail.php</h1>';
        
//Replace some values we don't want to show
        //Be aware that this doesn't works reliably if the values contain backslashes that are not literal.
        
echo
            
str_replace(MAIL_BLACKLIST_FILE,SOURCE_REPLACE,
            
str_replace(MAIL_DYNAMIC_MX    ,SOURCE_REPLACE,
            
str_replace(MAIL_MX_BLACKLIST  ,SOURCE_REPLACE,
                
highlight_file('_Mail.php'TRUE))));
        
//HTML frame END
        
echo '</body></html>';
        exit(
0);
    }

    
//GET API Request
    
if(ALLOW_API && isset($_GET['addr'])){
        
$addr=$_GET['addr'];
        
//Ensure param is a string and properly set
        
if(is_string($addr) && strlen($addr)>0)
        {
            
//Strip local part if existing, we only care for the domain name
            
$addr=mail_getHost($addr);

            
//Basic response format
            
$res=array(
                
'success'=> FALSE,
                
'addr'   => $addr,
                
'code'   => -1,
                
'desc'   => 'unknown error'
            
);
            
//Validate basic E-Mail format
            
if(mail_validate("example@$addr")){
                
//Validate E-Mail domain
                
switch(mail_accepted("example@$addr")){
                    
//Address is OK
                    
case ERR_MAIL_SUCCESS:
                        
$res['success']=TRUE;
                        
$res['code']=ERR_MAIL_SUCCESS;
                        
$res['desc']='accepted';
                        break;
                    
//Address seems invalid
                    
case ERR_MAIL_INVALID:
                        
$res['success']=TRUE;
                        
$res['code']=ERR_MAIL_INVALID;
                        
$res['desc']='invalid_format';
                        break;
                    
//Address has valid format but no E-Mail servers
                    
case ERR_MAIL_NOSERVER:
                        
$res['success']=TRUE;
                        
$res['code']=ERR_MAIL_NOSERVER;
                        
$res['desc']='no_mx';
                        break;
                    
//Address found in our blacklist file
                    
case ERR_MAIL_BLACKLIST:
                        
$res['success']=TRUE;
                        
$res['code']=ERR_MAIL_BLACKLIST;
                        
$res['desc']='blacklist';
                        break;
                    
//Address is a dynamic throwaway domain
                    
case ERR_MAIL_THROWAWAY:
                        
$res['success']=TRUE;
                        
$res['code']=ERR_MAIL_THROWAWAY;
                        
$res['desc']='throwaway';
                        break;
                }
            }
            else{
                
$res['success']=FALSE;
                
$res['desc']='Unable to parse address';
            }
        }
        else{
            
//Address was not specified
            
$res=array('success'=> FALSE);
        }
        
//Answer is JSON
        
header('Content-Type: application/json');
        
//Cache this response because it's unlikely to change
        
header('Expires: ' gmdate('D, d M Y H:i:s T',strtotime('+1 year')));
        echo 
json_encode($res);
        exit(
0);
    }
?><!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="pragma" content="no-cache" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>E-Mail Test</title>
        <!-- We only need the css of bootstrap. the JS is not necessary -->
        <link rel="stylesheet" type="text/css" href="/bootstrap4/bootstrap.min.css" />
        <!-- This script is not required. It merely completes the e-mail address if needed -->
        <script defer type="text/javascript" src="script.js"></script>
    </head>
    <body>
        <div class="container">
            <h1>E-Mail Test</h1>
            <p>
                This site performs a few tests on the address to see if it is valid.
                All tests are performed without connecting to a mail server or 3rd party service.<br />
                This test also checks against common throwaway services.
                This will not check if the address actually exists,
                only if the conditions for it existing are met.
                This means you can enter anything in front of the <kbd>@</kbd>
            </p>
            <form method="post" class="form-inline">
                <div class="form-group">
                    <label class="control-label">
                        E-Mail Address:
                        <input
                            type="email" name="addr" class="form-control" placeholder="E-Mail Address"
                            value="<?=htmlspecialchars($addr); ?>" />
                    </label>
                    <input type="submit" class="btn btn-primary" value="Check" />
                </div>
            </form>
            <br />
            <?php if($addr){
                if(
mail_validate($addr)){
                    switch(
mail_accepted($addr)){
                        case 
ERR_MAIL_SUCCESS:
                            echo 
'<div class="alert alert-success">E-Mail Address is valid and we would perform online tests now. This means attempting to send an E-Mail and tell, if an error happened (SMTP Code &gt;499)</div>';
                            break;
                        case 
ERR_MAIL_INVALID:
                            echo 
'<div class="alert alert-danger">E-Mail Address is invalid (for example invalid Domain name)</div>';
                            break;
                        case 
ERR_MAIL_NOSERVER:
                            echo 
'<div class="alert alert-danger">E-Mail Address is invalid. The domain has no E-Mail servers</div>';
                            break;
                        case 
ERR_MAIL_BLACKLIST:
                            echo 
'<div class="alert alert-warning">E-Mail Address is valid but blacklisted on our system (Blacklist).</div>';
                            break;
                        case 
ERR_MAIL_THROWAWAY:
                            echo 
'<div class="alert alert-warning">E-Mail Address is valid but blacklisted on our system (Dynamic throwaway detection).</div>';
                            break;
                    }
                }
                else{
                    echo 
'<div class="alert alert-danger">E-Mail Address is invalid (wrong format)</div>';
                }
            }
            elseif(
ALLOW_API){
            
?>
            <h2>API</h2>
            <p>
                You can use our JSON API to validate addresses.
                Example: <code>https://<?=$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];?>?addr=test@example.com</code>.<br />
                You can also only supply a domain name because our validation doesn't requires the local part.<br />
                The call above is identical to <code>https://<?=$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];?>?addr=example.com</code>.
            </p>
            <p>
                The answer is delivered as a JSON object with these values:
            </p>
            <ul>
                <li>
                    <b>success</b>: boolean, true or false depending if validation was attempted.
                    If false, your address is likely invalid or you messed up the request.
                    In this case, no other properties will be present.
                </li>
                <li><b>addr</b>: string, the address we validated</li>
                <li><b>code</b>: int, result of validation. See below for a list of codes</li>
                <li><b>desc</b>: string, brief description of the code</li>
            </ul>
            Codes are as follows:
            <ul>
                <li><b><?=ERR_MAIL_SUCCESS;?></b>: The address is accepted</li>
                <li><b><?=ERR_MAIL_INVALID;?></b>: The format is invalid</li>
                <li><b><?=ERR_MAIL_NOSERVER;?></b>: This address has no mailservers (no MX servers)</li>
                <li><b><?=ERR_MAIL_BLACKLIST;?></b>: This address is blacklisted (hardcoded list)</li>
                <li><b><?=ERR_MAIL_THROWAWAY;?></b>: This address is blacklisted (dynamically generated server list)</li>
            </ul>
            <p>
                The dynamic list (Code 4) is for services that constantly buy different domains to evade detection.
                <?php if(MAIL_AUTO_LIST_UPDATE){ ?><br />
                Note: This instance has <code>MAIL_AUTO_LIST_UPDATE</code> enabled.
                This means you will encounter code 4 at most once for a certain host part,
                after that you will receive code 3.
                <?php ?>
            </p>
            <p>
                Addresses without an MX server (code 2) are usually invalid.
                The standard says that if no MX servers are to be found,
                the host part in the mail address itself should be treated as MX server and used for mail delivery.
                Most E-mail provider will reject messages from addresses that lack an MX,
                so it's unlikely you encounter this code for valid addresses.
            </p>
            <h2>User Agent</h2>
            <p>
                When using our API, we expect a proper User-Agent header.
                Headers that are generic or mimic browsers might get your IP blocked.
                Use a descriptive agent with a help link.<br />
                Example: <code>Mail-Checker/1.1 +https://example.com/path/to/help</code>.
            </p>
            <?php ?>
            <i class="pull-right">
            <?php if(ALLOW_SOURCE){
                echo 
"<a href=\"?source\">View Source</a> |";
            }
?>
            <a href="/">Service Directory</a>
            </i>
        </div>
    </body>
</html>

_Mail.php

<?php
    
//Don't do any sort of string concatenation or the censoring for source view will no longer work.
    //You can ignore this warning if you don't plan to publish the source.

    //These MX servers are blacklisted on our system.
    //Usage: Used when some MX servers accept mails from an ever changing list of domain names.
    //Format: domain1.com,domain2.com,...
    
define('MAIL_MX_BLACKLIST','');

    
//The MX servers of these domains are blacklisted on our system.
    //Always compare the mail domain yourself if you can, because MAIL_DYNAMIC_MX is slower.
    //Usage: Used if a provider hosts an unknown number of spam domains but they all point to the same MX server.
    //       In that case listing a single domain name here will block all other domain names with the same MX servers.
    //Format: domain1.com,domain2.com,...
    
define('MAIL_DYNAMIC_MX','');

    
//File with the hardcoded domain blacklist.
    //This file should contain one domain on each line.
    
define('MAIL_BLACKLIST_FILE','');

    
//If enabled, it automatically adds addresses to the blacklist.
    //To be added it has to be caught by MAIL_DYNAMIC_MX or MAIL_MX_BLACKLIST.
    //Leaving this enabled is recommended as it speeds up processing,
    //because the blacklist is much faster than the dynamic detection.
    //Disable if the list is read-only.
    
define('MAIL_AUTO_LIST_UPDATE',TRUE);
    
    
//No changes below needed

    //Mail is valid
    
define('ERR_MAIL_SUCCESS',  0);
    
//Mail is invalid (format)
    
define('ERR_MAIL_INVALID',  1);
    
//Mail domain has no MX
    
define('ERR_MAIL_NOSERVER'2);
    
//Mail is listed in blacklist file
    
define('ERR_MAIL_BLACKLIST',3);
    
//Mail is blacklisted because of MAIL_DYNAMIC_MX or MAIL_MX_BLACKLIST
    
define('ERR_MAIL_THROWAWAY',4);

    
//Holds the blacklist. Do not manually change
    
$mail_Blacklist=NULL;

    
//Formats an e-mail address
    //Removes "plus addressing" which is often used to pretend to be multiple people.
    
function mail_format($addr){
        if(
mail_validate($addr)){
            
$seg1=substr($addr,0,strrpos($addr,'@'));
            
$seg2=substr($addr,strrpos($addr,'@')+1);
            if(
strpos($seg1,'+')!==FALSE){
                
$seg1=substr($seg1,0,strpos($seg1,'+'));
                
//Add the end quote if a start quote is there.
                
if(strlen($seg1)>&& $seg1[0]==='"'){
                    
$seg1.='"';
                }
            }
            
$addr="$seg1@$seg2";
            return 
mail_validate($addr)?$addr:NULL;
        }
        return 
NULL;
    }
    
    
//Reads the blacklist (if not done already)
    
function mail_readBlacklist()
    {
        global 
$mail_Blacklist;
        if(
$mail_Blacklist===NULL)
        {
            if(
file_exists(MAIL_BLACKLIST_FILE))
            {
                
//Read blacklist file and trim potential whitespace from all entries
                //In other words, CRLF vs LF will have no effect
                
$mail_Blacklist=explode("\n",trim(file_get_contents(MAIL_BLACKLIST_FILE)));
                
$mail_Blacklist=array_map('trim',$mail_Blacklist);
            }
            else
            {
                
//You can also just set it to an empty array instead.
                
error_log('Not found: ' MAIL_BLACKLIST_FILE);
                throw new 
Exception('Mail blacklist not found at location indicated by MAIL_BLACKLIST_FILE');
            }
        }
        return 
$mail_Blacklist;
    }

    
//Gets the host part of an E-mail address
    
function mail_getHost($addr)
    {
        
$host=$addr;
        if(
strpos($host,'@')!==FALSE)
        {
            return 
substr($host,strrpos($host,'@')+1);
        }
        return 
$host;
    }

    
//Performs basic E-mail format validation
    
function mail_validate($addr){
        return 
filter_var($addr,FILTER_VALIDATE_EMAIL)!==FALSE;
    }

    
//Checks if the given E-mail address is accepted
    //This is the only function you want to use for validation.
    
function mail_accepted($addr)
    {
        
$host=mail_getHost($addr);
        
$ips=mail_getMailIp($host);
        if(
$ips!==FALSE)
        {
            if(!
mail_validate($addr)){
                return 
ERR_MAIL_INVALID;
            }
            if(
count($ips)===0){
                return 
ERR_MAIL_NOSERVER;
            }
            if(
mail_isBlacklisted($addr)){
                return 
ERR_MAIL_BLACKLIST;
            }
            if(
mail_isThrowaway($addr)){
                return 
ERR_MAIL_THROWAWAY;
            }
            return 
ERR_MAIL_SUCCESS;
        }
        return 
ERR_MAIL_NOSERVER;
    }

    
//Checks if the address is blacklisted
    
function mail_isBlacklisted($addr)
    {
        return 
in_array(mail_getHost($addr),mail_readBlacklist());
    }

    
//Checks if the given address is a throwaway (test by MX)
    
function mail_isThrowaway($addr)
    {
        
$host=mail_getHost($addr);
        if(
$ips=mail_getMailIp($host))
        {
            
$dyn_blacklist=array_merge(mail_getBlacklistedMxAddr(),mail_getDynamicBlacklist());

            foreach(
$dyn_blacklist as $ip)
            {
                if(
in_array($ip,$ips))
                {
                    if(
MAIL_AUTO_LIST_UPDATE && !mail_isBlacklisted($addr)){
                        
$blacklist=mail_readBlacklist();
                        
$blacklist[]=$host;
                        
sort($blacklist,SORT_STRING|SORT_FLAG_CASE);
                        @
file_put_contents(MAIL_BLACKLIST_FILE,implode(PHP_EOL,$blacklist));
                    }
                    return 
TRUE;
                }
            }
        }
        return 
FALSE;
    }

    
//Gets MX server IP addresses
    
function mail_getMailIp($host)
    {
        if(
$hosts=mail_getMx($host))
        {
            
$addrs=array();
            foreach(
$hosts as $entry)
            {
                if(
$addr=mail_getIpAddr($entry))
                {
                    
$addrs=array_merge($addrs,$addr);
                }
            }
            return 
$addrs;
        }
        return 
FALSE;
    }

    
//Gets MX servers from a domain
    
function mail_getDynamicBlacklist()
    {
        
$hosts=explode(',',MAIL_DYNAMIC_MX);
        
$addrs=array();
        foreach(
$hosts as $host)
        {
            if(
$addr=mail_getMailIp($host))
            {
                
$addrs=array_merge($addrs,$addr);
            }
        }
        return 
$addrs;
    }

    
//Gets blacklisted IP addresses
    
function mail_getBlacklistedMxAddr()
    {
        
$addrs=array();
        
$hosts=explode(',',MAIL_MX_BLACKLIST);
        foreach(
$hosts as $host)
        {
            if(
$addr=mail_getIpAddr($host))
            {
                
$addrs=array_merge($addrs,$addr);
            }
        }
        return 
$addrs;
    }

    
//Checks if a DNS name has at least a single record of the specified type
    
function mail_hasRecord($hostOrAddr,$type='MX')
    {
        return 
checkdnsrr(mail_getHost($hostOrAddr),$type);
    }

    
//Gets all MX records of a domain name
    
function mail_getMx($host)
    {
        if(!
mail_hasRecord($host)){return FALSE;}
        
$results=dns_get_record($host,DNS_MX);
        
$hosts=array();
        foreach(
$results as $result)
        {
            switch(
$result['type'])
            {
                case 
'MX':
                    
$hosts[]=$result['target'];
                    break;
            }
        }
        return 
count($hosts)>0?$hosts:FALSE;
    }

    
//Gets all IP addresses from a domain name
    
function mail_getIpAddr($host)
    {
        if(!
mail_hasRecord($host,'A') && !mail_hasRecord($host,'AAAA')){return FALSE;}
        
$results=dns_get_record($host,DNS_A|DNS_AAAA);
        
$ips=array();
        foreach(
$results as $result)
        {
            switch(
$result['type'])
            {
                case 
'A':
                    
$ips[]=$result['ip'];
                    break;
                case 
'AAAA':
                    
$ips[]=$result['ipv6'];
                    break;
            }
        }
        return 
count($ips)>0?$ips:FALSE;
    }
?>