#!/afs/athena/contrib/perl/perl

# RFC821 is the SMTP RFC

# $Id: expn.pl,v 1.25 1998/03/26 01:37:40 marc Exp $

unshift(@INC, "/afs/athena.mit.edu/user/m/a/marc/perl");
require 'resolv.pl';

$smtpserver = "mit.edu";		# default server (lowercase)

if (@ARGV == 0) {
    die <<EOH;
Usage: expn [-nomx] [-vrfy] [-v(erb)] [-h(ost) hostname] recipient[\@hostname]
    
If you specify a hostname with -h or -host, it will be the default for
The rest of the addresses on the command line. if you specify
recipient\@hostname, the host will be used only for that request.
The default mail server used is $smtpserver.
EOH
}

require 'sys/socket.ph';

$connected = "";
$smtp = 0;

sub disconnect {
    if ($connected && $smtp) {
	print(SMTP "QUIT\r\n");
	$_ = <SMTP>;
	s/[\015\012]+$/\n/;
	/^[0-9]{3}/;
	print("$&-DISCONNECT from $connected\n$_");
	
	close(SMTP);
	$smtp = 0;
    }
    $connected = "";
}

sub mxlookup {
    local(@ans) = &res_search($_[0], "MX", "IN");
    local(@ret, @s);

    @ret = ($ans[$ans[4]] =~ /^(\S+)/);

    if ($ans[3]) {
	push(@ret, "", $ans[3]);
    } else {
	for (@ans[$ans[6]..($ans[6]+$ans[7]-1)]) {
	    @s = split(' ',$_,5);
	    push(@ret, $s[4]) if ($s[1] eq "MX") && ($s[2] eq "IN");
	}
    }

    @ret;
}

sub smtpcmd { # @_ = ($cmd)
    local($first) = (1);

    print SMTP "$_[0]\r\n";
    while (<SMTP>) {
	s/[\015\012]+$/\n/;
	/^([0-9]{3})(.)/;
	if ($first) {
	    print "$1-$_[0]\n";
	    $first = 0;
	}
	print;
	if ($2 eq " ") { last; }
    }
}

sub telnet { # @_ = ($hostname)
    local($hentarg,@hent,$canon,$mxmsg,$port,$sin,$sock,$fd);
    local($host) = @_;
    
    $host =~ tr/A-Z/a-z/;
    
    if ($host eq $connected) { return; }
    
    if ($host eq "") {
	&disconnect();
	return;
    }
    
    if ($host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
	$hentarg = $host;
	@hent = gethostbyaddr(pack("CCCC", $1, $2, $3, $4),&AF_INET);
    } elsif (!$nomx) {
	($canon,@mx) = &mxlookup($host);

	if (!$mx[0]) {
	    @hent = gethostbyname($hentarg = $host);
	} elsif (@matches = grep(/$canon$/i,@mx)) {
	    ($hentarg = $matches[0]) =~ s/^\d+\s+//;
	    $mxmsg = "\0\0\0-$canon is an MX address.  MX hosts:\n";
	    foreach (@mx) {
		$mxmsg .= "\0\0\0-  ".$_."\n";
	    }
	    $mxmsg .= 
		"\0\0\0-Address is subdomain of MX host \"$hentarg\", ".
		    "connecting there...\n";
	    @hent = gethostbyname($hentarg);
	} else {
	    print ("421-CONNECT to $canon\n".
		   "421-$canon is an MX address.  MX hosts:\n");
	    foreach (@mx[$[..($#mx-1)]) {
		print "421-  ",$_,"\n";
	    }
	    print "421   ",$mx[$#mx],"\n";
	    $connected = $host;
	    return;
	}
    } else {
	@hent = gethostbyname($hentarg = $host);
    }

    # at this point, @hent contains the info for the host to try to
    # connect to.  $mxmsg may contain MX-related info on how we got there.

    if (! @hent) {
	$mxmsg =~ s/\0\0\0/421/g;

	print "421-CONNECT to $host\n",$mxmsg,"421 No such host ($hentarg)\n";
	return;
    }
    
    $port = (getservbyname("smtp","tcp"))[2];
    
    &disconnect();
    
    $sin = pack("S n a4 x8",&AF_INET,$port,$hent[4]);
    if (!socket(SMTP, &AF_INET, &SOCK_STREAM, &PF_UNSPEC)) {
	$mxmsg =~ s/\0\0\0/421/g;

	print("421-CONNECT to $host\n",$mxmsg,
	      "421 socket error: $! ($hentarg)\n");
	return;
    }
    if (!connect(SMTP, $sin)) {
	$mxmsg =~ s/\0\0\0/421/g;

	print("421-CONNECT to $host\n",$mxmsg,
	      "421 connect error: $! ($hentarg)\n");
	return;
    }
    
    $fd = select(SMTP); $| = 1;   # set nonbufferred
    select($fd);
    
    if (($_ = <SMTP>) eq "") {
	$mxmsg =~ s/\0\0\0/421/g;

	print("421-CONNECT to $host\n",$mxmsg,
	      "421 End of file reading sendmail banner ($hentarg)\n");
	return;
    }
    s/[\015\012]+$/\n/;

    ($code,$last) = (/^([0-9]{3})(.)/);
    if ($code eq "220") { $connected = $host; $smtp = 1; }
    print "$code-CONNECT to $host\n";

    $mxmsg =~ s/\0\0\0/$code/g;
    print $mxmsg;

    print $_;

    if ($last eq "-") {
	while (<SMTP>) {
	    s/[\015\012]+$/\n/;
	    print;
	    if (/^[0-9]{3} /) { last; }
	}
    }

    $localname = (gethostbyaddr(((unpack("S n a4 x8", getsockname(SMTP)))[2]),
				&AF_INET))[0] || die "gethostbyaddr: $!\n";
    &smtpcmd("HELO $localname");

    if ($verb) {
	&smtpcmd("VERB");
    }
}

sub expn { # @_ = ($name)
    local($name) = @_;

    if ($connected eq "") {
	&telnet($smtpserver);
	if ($connected eq "") {
	    return;
	}
    }
    return if !$smtp;

    print(SMTP ($vrfy?"VRFY":"EXPN")." $name\r\n");
    $_ = <SMTP>;
    s/[\015\012]+$/\n/;
    /^([0-9]{3})(.)/;
    print "$1-".($vrfy?"VRFY":"EXPN")." $name at $connected\n$_";
    ## XXX rfc821 says nothing about 050 (0yz?) replies, so I'm
    ## going to wing it.
    if (($1 ne "050") && ($2 eq " ")) { return; }
    while (<SMTP>) {
	s/[\015\012]+$/\n/;
	print;
	if (/^[1-9][0-9][0-9] /) { last; }
    }
}

while ($arg = shift(@ARGV)) {
    if ($arg eq "") { next; }
    if ($arg eq "-nomx") { $nomx = 1; next; }
    if (($arg eq "-verb") || ($arg eq "-v")) { $verb = 1; next; }
    if ($arg eq "-vrfy") { $vrfy = 1; next; }
    if (($arg eq "-host") || ($arg eq "-h")) {
	$arg = shift(@ARGV);
	&telnet($arg);
    } elsif ($arg =~ /^(.*)\@(.*)$/) {
	local($save) = ($connected);
	&telnet($2);
	if ($connected eq "") {	next; }
	&expn($1);
	&telnet($save);
    } else {
	&expn($arg);
    }
}

&disconnect();
