I cobbled up together a small bash shell script that does this. It lists the MX records for a domain, and then tries to connect to each of them, issue an EHLO command, disconnect, then list the output of the server, alerting if the server supports STARTTLS. It should be easy to further query the server for the certificate, using some external utility called from the script. It requires netcat and a pair of djbdns utilities. It's a bit crude, but could be helpful. Script follows: --------------------- cut here -------------------------------------- #!/bin/bash ## Query the capabilities of mailservers for a domain. ## ## Requirements: nc (netcat), dnsmx and dnsip (from djbdns package) TMP=`mktemp /tmp/queryehlo.XXXXXX` EHLOSTRING="capquery" TIMEOUT=15 function help() { cat << EOF queryehlo - query the capabilities of mailservers for a domain Usage: queryehlo <domain> EOF exit 0 } function checkresources() { ERR=""; if [ ! "`which nc 2>/dev/null`" ]; then echo "ERROR: nc (netcat) not available in \$PATH." echo "netcat should be part of standard distro, or can be acquired from eg." echo " http://www.atstake.com/research/tools/network_utilities/". echo ERR="1" fi if [ ! "`which dnsmx 2>/dev/null`" ]; then echo "ERROR: dnsmx (from djbdns) not available in \$PATH." echo "djbdns can be downloaded from eg. http://cr.yp.to/djbdns.html" echo ERR="1" fi if [ "$ERR" == "1" ]; then exit; fi } function queryrelay() { if [ ! "$x" ]; then return; fi echo "Querying mail relay $1, `dnsip $x`" cat << EOF | nc -w $TIMEOUT $1 25 > $TMP EHLO $EHLOSTRING QUIT EOF if [ "`cat $TMP|grep STARTTLS`" ]; then echo "*** RELAY ADVERTISES SMTP/TLS SUPPORT" # insert eventual further interrogations here fi echo cat $TMP echo echo rm $TMP } checkresources if [ "$1" == "" ]; then help; fi if [ "$1" == "-h" ]; then help; fi if [ "$1" == "--help" ]; then help; fi dnsmx $1 | sort -n | while true; do read x1 x; if [ "$?" == "1" ]; then break; fi queryrelay $x; done
On 2004-07-08T17:50:57+0200, Thomas Shaddack wrote:
I cobbled up together a small bash shell script that does this. It lists the MX records for a domain, and then tries to connect to each of them, issue an EHLO command, disconnect, then list the output of the server, ...
Or, in perl... though I wonder if there's a way to get capabilities with Net::SMTP. Might make this cleaner. #!/usr/bin/perl use IO::Socket; use Net::DNS; for ($i = 0; $i <= $#ARGV; $i++) { my @mx = mx($ARGV[$i]); foreach $record (@mx) { my $hastls = 0; my $mhost = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $record->exchange, PeerPort => "25", Timeout => "10" ); print $mhost "EHLO I-love-my-country.whitehouse.gov\n"; print $mhost "QUIT\n"; while (<$mhost>) { if (/STARTTLS/) { $hastls = 1; last; } } print "$ARGV[$i] " . $record->preference . " " . $record->exchange; print $hastls ? " adv-tls\n" : " no-tls\n"; close $mhost; } }
It fails on hotmail.com; my script has problems there as well (and with couple others, the cure seems to be adding delays between the lines sent to the server; it makes the program slow, but more reliable). In my case I added "-i 3" to the netcat options. Isn't a panacea, but helped in most cases. In the rest, I have to resort to telnet. Thanks a lot. Seems I have to learn perl. Looks powerful. On Thu, 8 Jul 2004, Justin wrote:
On 2004-07-08T17:50:57+0200, Thomas Shaddack wrote:
I cobbled up together a small bash shell script that does this. It lists the MX records for a domain, and then tries to connect to each of them, issue an EHLO command, disconnect, then list the output of the server, ..
Or, in perl... though I wonder if there's a way to get capabilities with Net::SMTP. Might make this cleaner.
#!/usr/bin/perl
use IO::Socket; use Net::DNS;
for ($i = 0; $i <= $#ARGV; $i++) { my @mx = mx($ARGV[$i]); foreach $record (@mx) { my $hastls = 0; my $mhost = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $record->exchange, PeerPort => "25", Timeout => "10" ); print $mhost "EHLO I-love-my-country.whitehouse.gov\n"; print $mhost "QUIT\n"; while (<$mhost>) { if (/STARTTLS/) { $hastls = 1; last; } } print "$ARGV[$i] " . $record->preference . " " . $record->exchange; print $hastls ? " adv-tls\n" : " no-tls\n"; close $mhost; } }
On 2004-07-09T01:46:26+0200, Thomas Shaddack wrote:
It fails on hotmail.com; my script has problems there as well (and with couple others, the cure seems to be adding delays between the lines sent to the server; it makes the program slow, but more reliable).
This should work much better, and has some additional keywords that help to figure out what's going on. This works on hotmail. I noticed one host was hanging until I started using \r\n. It might be worthwhile to ensure nagle is turned off between the EHLO and the QUIT. #!/usr/bin/perl use IO::Socket; use Net::DNS; $dlevel = 0; sub debug { ($str, $mlevel) = @_; if ($mlevel <= $dlevel) { print "DEBUG $str"; } } sub checkmailtls { my ($domain, $mpri, $mrelay) = @_; my $proto = ""; my $hastls = "no-tls"; my @special; my $mhost = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $mrelay, PeerPort => "25", Timeout => "5" ); if (! defined $mhost) { print "$domain $mpri $mrelay noconnect\n"; return; } debug("testing $mrelay $mpri\n", 1); $greeting = <$mhost>; if ($greeting =~ /\*\*\*\*\*\*\*\*/) { $proto = "smtp"; push (@special, "filtered"); } if ($greeting =~ /(esmtp|postfix|sendmail)/i) { $proto = "esmtp"; } elsif ($greeting =~ /[^eE][sS][mM][tT][pP]/) { $proto = "smtp"; } else { $proto = "smtp"; } print $mhost "EHLO I-love-my-country.whitehouse.gov\r\n"; print $mhost "QUIT\r\n"; while (<$mhost>) { if (/^5[0-9]{2}/) { if ($proto == "esmtp") { push(@special, "lies"); $proto = "smtp"; } $hastls = "no-tls"; last; } if (/STARTTLS/) { if ($proto == "smtp") { $proto = "esmtp"; push(@special, "stealth"); } $hastls = "adv-tls"; last; } } print "$domain $mpri $mrelay $proto $hastls @special\n"; close $mhost; } ### begin #### debug("argc: $#ARGV\n", 1); if ($#ARGV >= 0) { for ($i = 0; $i <= $#ARGV; $i++) { push (@ipstack, $ARGV[$i]); } } else { while (<>) { chomp; push (@ipstack, $_); } } while ($domain = shift(@ipstack)) { # $res = Net::DNS::Resolver->new(); # @mx = mx($res, $domain); my @mx = mx($domain); if ($#mx == -1) { print "no MX!\n"; } foreach $record (@mx) { my $mrelay = $record->exchange ; my $mpri = $record->preference ; checkmailtls($domain, $mpri, $mrelay); } }
This one should work better. The last one had string comparison problems. #!/usr/bin/perl use IO::Select; use IO::Socket; use Net::DNS; $ehloname = "mail.senate.gov"; $timeout = 15; $dlevel = 0; sub debug { (my $str, my $mlevel) = @_; if ($mlevel <= $dlevel) { print "DEBUG $str"; } } sub checkmailtls { my ($domain, $mpri, $mrelay) = @_; my $proto = "smtp"; my $hastls = "no-tls"; my @flags; my $mhost = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $mrelay, PeerPort => "25", Timeout => "10" ); if (! defined $mhost) { print "$domain $mpri $mrelay noconnect\n"; return; } debug("opened connection to $mrelay\n", 1); $sel = IO::Select->new($mhost); @readable = $sel->can_read($timeout); # magic number if ($#readable == -1) { print "$domain $mpri $mrelay timeout-a\n"; goto OUT; } $greeting .= <$mhost>; # there's only one handle; we know which it is. debug("greeting: $greeting", 2); if ($greeting =~ /[\\*]{8}/) { $proto = "smtp"; push (@flags, "filtered"); } if ($greeting =~ /\b(esmtp|postfix|exim|sendmail)\b/i) { debug("setting esmtp (greet)!\n", 1); $proto = "esmtp"; debug("found esmtp-indicator in greeting\n", 1); } print $mhost "EHLO $ehloname\r\n"; print $mhost "QUIT\r\n"; if (! (@readable = $sel->can_read($timeout))) { print "$domain $mpri $mrelay timeout-b\n"; goto OUT; } while (<$mhost>) { #$sel->can_read(0)) { chomp; debug("loop-recv: $_\n", 2); if (/^5[0-9]{2}/) { if ($proto =~ /^esmtp/) { push(@flags, "lies"); $proto = "smtp"; } $hastls = "no-tls"; last; } if (/STARTTLS/) { if ($proto =~ /^smtp/) { debug("setting esmtp (stls)!\n", 1); $proto = "esmtp"; push(@flags, "nobproto"); } $hastls = "adv-tls"; last; } } print "$domain $mpri $mrelay $proto $hastls @flags\n"; # try again just in case the remote host didn't notice the first one print $mhost "QUIT\r\n"; OUT: close $mhost; debug("closed connection to $mrelay\n", 1); } ### begin #### if ($#ARGV >= 0) { for ($i = 0; $i <= $#ARGV; $i++) { push (@hostfifo, $ARGV[$i]); } } else { while (<>) { chomp; push (@hostfifo, $_); } } while ($domain = shift(@hostfifo)) { my @mx = mx($domain); if ($#mx == -1) { checkmailtls($domain, "A", $domain); } else { foreach $record (@mx) { my $mrelay = $record->exchange; my $mpri = $record->preference; checkmailtls($domain, $mpri, $mrelay); } } }
participants (2)
-
Justin
-
Thomas Shaddack