Preface
I want to handle incoming emails and i've drawn a blank with sendmail. I simply want to handle every incoming messages via PHP, for inserting into a database so i can provide a webmail frontend. In sendmail its a bit laborious (i have had to manually attach an alias to every domain, the alias then passes to the scrpt).
Synopsis
This article assumes your savvy with PHP and linux environment, and introduces the inetd program in ubuntu.
Prerequisites: Linux server with PHP-CLI and Inetd
Install inetd
inetd is a internet transfer protocol. It executes a program when a port is accessed remotely.
sudo apt-get install openbsd-inetd
Check its running
ps -ef | grep inetd
Add the line to the file
#... # protocol smtp stream tcp nowait root /php/cli/emailhandler.php #...
And the PHP script /php/cli/emailhandler.php
NOTE: chmod+x the file and remove any weird carriage returns if editing from a windows IDE otherwise you might see a message with "^M" e.g. "/usr/bin/php^M: bad interpreter"
#! /usr/bin/php5
<?php
/**
* This enables a two way conversation between client and local server.
* The comunication is based upon the SMPT standards defined in http://www.lesnikowski.com/mail/Rfc/rfc2821.txt
*
* 220 Server ready: a connection to the server has been made and the server is ready
* 221 Server closing: the server accepted the QUIT command and is ready to close the connection
* 250 OK: the server accepted the command (one of several similar responses)
* 354 Send mail: the server accepted the DATA command and is waiting for the Mail Message
* 550 Invalid: the server rejected the command (one of several similar responses)
*/
function store( $s )
{
file_put_contents( "/tmp/emails.log", "\n" . $s, FILE_APPEND );
}
function reply( $s )
{
store( "REPLY:$s" );
fwrite(STDOUT, $s . "\n" );
}
store( "START: " . date('c') );
reply( "220 Server ready for transmission");
/**
* The return should be "HELO something"
* Lets loop through all the requests and respond to them with "250 Ok"
*/
$s = "";
while( ( $data = fgets(STDIN) )
&& stripos( $data, "QUIT") === false )
{
$s .= $data;
store( $data );
if ( ! ( $data_section = ( ( @$data_section && (strpos( $data , "." ) !== 0 ) ) ) ) )
{
reply("250 Ok");
$data_section = ( strpos( $data, "DATA") === 0 );
}
}
store( $data );
/**
* Send the term signal "221 Closing transmission"
*/
reply("221 closing transmission");
store( $s . "\nCOMPLETED" );
?>
And thats it. This will populate a file called /tmp/emails.log which contains the content of the transmission.
Mock Up Using Netcat
I'm replicating the sending of a message using netcat. In the transcript that follows i'm using netcat as an SMTP sender on "sender.local" for transmission to the above PHP code and inetd which together make up the SMTP receiver on "receiver.local". Here is the transcript. Everything in italic bold i've manually typed and the response starts with a number, e.g. 221, 250, 354
sender.local:~$ nc -w 5 receiver.local 25 220 receiver.local Ready for transmission HELO sender.local
250 Ok MAIL FROM:<mail@mydomain.com>
250 Ok RCPT TO:<mail2@anotherdomain.com>
250 Ok DATA
354 Ready for mail message This is my first mail message
.
250 Ok QUIT
221 closing transmission sender.local:~$
Alternativly i could mimick this using just the one machine on receiver.local e.g.
receiver.local:~$ nc -w 5 localhost 25
Or i could trying connecting to a third party SMTP server... To find a mail servers for gmail.com i use the host command.
local:~$ host -t mx gmail.com gmail.com MX 5 gmail-smtp-in.l.google.com gmail.com MX 10 alt1.gmail-smtp-in.l.google.com ...
Selecting one of the addresses i make a connection using "nc" as before... and test my response codes against their's.
local:~$ nc -w 5 alt1.gmail-smtp-in.l.google.com 25 220 mx.google.com ESMTP k21si8982217waf.8 HELO 250 mx.google.com at your service
What's Next...
Whilst the transmission is running we might also like to...
- Validate smtp sender commands. i.e. HELO, MAIL FROM, RCPT TO, DATA,QUIT.
- Lookup address (does recipient exist)
- <opt> Spamfilter (if we really want to send a rejection). Baysian Filter
And once the transmission has completed.
- Copy parts of the transmission into a database.
- <opt> Let users devide whether its spam and update a Baysian filter
Resources
This took me a while but i found these articles exceptionally useful.
SMTP handshake comunication between client and server.
http://www.cs.stir.ac.uk/~kjt/software/comms/jasper/SMTP.html This was great to see what the transmission is all about.
http://www.faqs.org/rfcs/rfc821.html RFC 821 Simple Mail Transfer Protocol
http://www.greenend.org.uk/rjk/2000/05/21/smtp-replies.html The transmission order of send -> respond made clear
http://www.cs.ucr.edu/~ddreier/cs164as3.pdf I found this schooled assignment laid down a good introduction.
Inetd configuration and monitoring traffic
http://netbsd.org/docs/guide/en/chap-inetd.html Setup for the "Internet Super Server" Inetd
http://www.jfranken.de/homepages/johannes/vortraege/netcat_inhalt.en.html Netcat "TCP/IP swiss army knife" (sudonym "nc") is great for network exploration and allowed me test my inetd settings
