0

I've recently been "prettifying" some tried and true email forms to make them more mobile friendly, and thought I was going out of my mind seeing the PHP mail() function now randomly failing (and returning FALSE). Well it wasn't random. After nearly pulling out all my hair, I finally realized that anytime I entered an invalid domain for the form field that becomes my "reply-to" address, mail() would fail, and return FALSE! Note, these aren't "malformed" email addresses (which i already check for), just invalid ones (like "name@goooogle.com").

I've included a test form code below if you think it matters, but do you think this is a new "feature" of PHP, or something that just my hosting company's server is doing? If it is PHP, is there maybe some new "testDomain()" function I could also add to my forms? It would be nice to alert the user who made a legitimate mistake, but all I can tell them for sure is that their mail attempt failed. After all, mail() doesn't return some friendly little error code that indicates what happened, it just returns true or false. In fact when it does return false, it doesn't even report an error in the log file.

Truth be told, this is the ONLY time I've ever gotten mail() to fail in my forms. But I think assuming the failure always meant the domain was bad would be unwise.

<!DOCTYPE HTML>
<html>
<head> <title> Simple Test Email Form;</title>  </head>

<!--
<?php
// define variables and set to empty values
$name = $email =  $comments = ""; // $address = $citystate = $zip = $phone  not used
$nameErr = $emailErr = $commentsErr = "";
$headers = $email_message = $sendersIP = "";
$email_to = "myaddress@mydomain.org";  // this is bogus!!! 
$email_subject = "Private Mailform";
$status = "Form Not Yet Processed";

// some basic security functions

function clean_string($string) {
       $bad = array("content-type","bcc:","to:","cc:","href");
       return str_replace($bad,"",$string);
     }

function test_input($data) {
   $data = trim($data);
   $data = stripslashes($data);
   $data = htmlspecialchars($data);
   return $data;
}

// mail processing

if ($_SERVER["REQUEST_METHOD"] == "POST") 
{   
    if (empty($_POST["name"]))   {  $nameErr = " Name is required"; }
    else {  $name = test_input($_POST["name"]); }
    if (empty($_POST["email"])) { $emailErr = "Email is required"; }
    else {  $email = test_input($_POST["email"]); }
    // at least email should be validated
    if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email)) { $emailErr = "Invalid email format"; }

    // keep senders IP, so we can watch for idiots.
    $sendersIP = $_SERVER['REMOTE_ADDR'];

    $comments = $_POST["comments"];  

    // So is all well?
    if (empty($nameErr) && empty($emailErr) && empty($commentsErr)  )
        {
        $email_message = $headers = "";

        $email_message .= "Name:  ".clean_string($name)."\n";
        $email_message .= "Email: ".clean_string($email)."\n";
        $email_message .= "IP:    ".$sendersIP."\n";
        $email_message .= "Comments: "."\n\n".clean_string($comments)."\n";

        // create regular email headers
        $headers .= 'From: '.$email."\r\n".
'Reply-To: '.$email."\r\n" .
'X-Mailer: PHP/' . phpversion();

        $mail_sent = mail($email_to, $email_subject, $email_message, $headers); 

        // modify status string to show result      
         $status = ($mail_sent==TRUE) ? "mail() function returned TRUE" :" mail() function returned FALSE";
        }
}
?>

-->
<body >
<table><tr><td style ="text-align:right;" width=100%> 

<b><?php echo $status; ?></b><br>

<p><span >* = required fields.<br>Please double check your email address.</span></p>

<form name="contactform" method="post" enctype="multipart/form-data" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>"> 
Name: <span class="error">*<?php echo $nameErr;?></span>&nbsp;
   <input type="text" name="name" value="<?php echo $name;?>"><br>
Email: <span class="error">*<?php echo $emailErr;?></span>&nbsp;
    <input type="text" name="email" value="<?php echo $email;?>"><br>
<br>
<div align="center"> ---- <span class="error">*</span> Message ---- <span class="error"><?php echo $commentsErr;?></span><br>
<textarea name="comments" wrap="physical" cols="40" rows="10"  ><?php echo $comments;?></textarea>

<br><br>

</div>

<input name="submit" id="submit" type="submit" value="Submit"  >

</form> 
</td></tr></table>

</body>
</html>
Randy
  • 301
  • 2
  • 11
  • 4
    It’s very unlikely that it’s PHP doing that. I think it’s much more likely that the SMTP server your hosting provider is using put these validation steps in place. – icktoofay Jul 26 '15 at 21:33
  • Well thanks. If its NOT PHP that's unfortunate, because it seems like a welcome addition. I should have mentioned my hosting company is hostmonster.com, in case it turns out it is only happening with them. But their tech service is a pain to get through to, and their answers are seldom consistent. So I thought it best to start here and see if any others are experiencing this. – Randy Jul 26 '15 at 21:37

2 Answers2

2

mail has no native checks for validity. Vanilla sendmail doesn't either. So it's possible your host has their MTA client doing this. You might be able to test this using a separate mail client. For instance, ZF2, PHPMailer and many other modern PHP mailing systems don't use the mail function at all. They actually open a socket and send the mail commands directly. It's not easy, but you could tinker with this to send you back the actual commands and responses and see where the SMTP transaction is failing.

You can add your own validation before it gets that far, incidentally.

// this will fail to send if the DNS for the domain has no MX records
if(!checkdnsrr($domain, 'MX')) { 
     // don't send
}
Machavity
  • 30,841
  • 27
  • 92
  • 100
  • Thanks! That seems to work perfectly! After I make sure my $email is not malformed, I extract the domain and test it. GREAT! Now i can report it o the user as I would any other "fat finger" error. $domain = substr($email, stripos($email, "@") + 1); $domainValid = checkdnsrr($domain); – Randy Jul 26 '15 at 23:42
  • Ooops.. forgot to add the 'MX' for the record type. I will say though that I have accidentally found at least one valid domain that fails, even though there is a website and an IP associated with the domain. Have to find out why. I think I've been to websites where the mail form rejected my email as invalid when I knew it was right. Maybe instead of blocking I'll just issue a warning. Then if the mail() does fail, at least i will have tried. – Randy Jul 26 '15 at 23:58
1

No. mail() is not for that. It may be that SMTP you are using would do that, but that depends on the setup of said server.

PS: do not use mail(). It is very primitive. Use phpmailer or other tools to make mail sending easier

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • So I assume, from what you're saying, mail() definitely has no new inherent ability to test for a valid domain, correct? Well thanks then. Yes, mail is primitive, and building emails that send files is a major pain with mail(). But even if I started migrating to PHPMailer, do you think it would have any better sense of WHY the mail failed in a case like this, where some validation failure up the food chain (as in the SMTP) is returning the failure? Not being lazy, but would it solve the problem? – Randy Jul 26 '15 at 21:47
  • no, `mail()` won't do that. You can do this yourself i.e. by using `checkdnsrr()` or leave this to SMTP server – Marcin Orlowski Jul 26 '15 at 22:00
  • Just by random tests, I found a domain that is legitimate that seems to fail this test. I happened to try "rany@panny.com" in my form, and the domain "panny.com" fails, even though it has a dns and a public website. It could take a few seconds, but now I'm thinking gethostbyname($domain); If it returns an IV4 address, then the return string will have a digit for a first character. So far, I haven't got this to fail on any legitimate domain, and haven't gotten my host's mail() to fail on any one that passes. Wonder why checkdnsrr() only works some of the time. – Randy Jul 27 '15 at 01:36
  • 1
    Try `dns_get_record()` – Marcin Orlowski Jul 27 '15 at 01:46