10Sstevel@tonic-gate#!/usr/perl5/bin/perl -w 20Sstevel@tonic-gate# 30Sstevel@tonic-gate# CDDL HEADER START 40Sstevel@tonic-gate# 50Sstevel@tonic-gate# The contents of this file are subject to the terms of the 6*8287SJohn.Sonnenschein@Sun.COM# Common Development and Distribution License (the "License"). 7*8287SJohn.Sonnenschein@Sun.COM# You may not use this file except in compliance with the License. 80Sstevel@tonic-gate# 90Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate# See the License for the specific language governing permissions 120Sstevel@tonic-gate# and limitations under the License. 130Sstevel@tonic-gate# 140Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate# 200Sstevel@tonic-gate# CDDL HEADER END 210Sstevel@tonic-gate# 220Sstevel@tonic-gate# 230Sstevel@tonic-gate# Copyright (c) 1996-2000 by John T. Beck <john@beck.org> 240Sstevel@tonic-gate# All rights reserved. 250Sstevel@tonic-gate# 26*8287SJohn.Sonnenschein@Sun.COM# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 270Sstevel@tonic-gate# Use is subject to license terms. 280Sstevel@tonic-gate# 290Sstevel@tonic-gate 30*8287SJohn.Sonnenschein@Sun.COMrequire 5.8.4; # minimal Perl version required 310Sstevel@tonic-gateuse strict; 320Sstevel@tonic-gateuse warnings; 330Sstevel@tonic-gateuse English; 340Sstevel@tonic-gate 350Sstevel@tonic-gateuse Socket; 360Sstevel@tonic-gateuse Getopt::Std; 370Sstevel@tonic-gateour ($opt_v, $opt_b); 380Sstevel@tonic-gate 390Sstevel@tonic-gate# system requirements: 400Sstevel@tonic-gate# must have 'hostname' program. 410Sstevel@tonic-gate 420Sstevel@tonic-gatemy $port = 'smtp'; 430Sstevel@tonic-gateselect(STDERR); 440Sstevel@tonic-gate 450Sstevel@tonic-gatechop(my $name = `hostname || uname -n`); 460Sstevel@tonic-gate 470Sstevel@tonic-gatemy ($hostname) = (gethostbyname($name))[0]; 480Sstevel@tonic-gate 490Sstevel@tonic-gatemy $usage = "Usage: $PROGRAM_NAME [-bv] host [args]"; 500Sstevel@tonic-gategetopts('bv'); 510Sstevel@tonic-gatemy $verbose = $opt_v; 520Sstevel@tonic-gatemy $boot_check = $opt_b; 530Sstevel@tonic-gatemy $server = shift(@ARGV); 540Sstevel@tonic-gatemy @hosts = @ARGV; 550Sstevel@tonic-gatedie $usage unless $server; 560Sstevel@tonic-gatemy @cwfiles = (); 570Sstevel@tonic-gatemy $alarm_action = ""; 580Sstevel@tonic-gate 590Sstevel@tonic-gateif (!@hosts) { 600Sstevel@tonic-gate push(@hosts, $hostname); 610Sstevel@tonic-gate 620Sstevel@tonic-gate open(CF, "</etc/mail/sendmail.cf") || 630Sstevel@tonic-gate die "open /etc/mail/sendmail.cf: $ERRNO"; 640Sstevel@tonic-gate while (<CF>){ 650Sstevel@tonic-gate # look for a line starting with "Fw" 660Sstevel@tonic-gate if (/^Fw.*$/) { 670Sstevel@tonic-gate my $cwfile = $ARG; 680Sstevel@tonic-gate chop($cwfile); 690Sstevel@tonic-gate my $optional = /^Fw-o/; 700Sstevel@tonic-gate # extract the file name 710Sstevel@tonic-gate $cwfile =~ s,^Fw[^/]*,,; 720Sstevel@tonic-gate 730Sstevel@tonic-gate # strip the options after the filename 740Sstevel@tonic-gate $cwfile =~ s/ [^ ]+$//; 750Sstevel@tonic-gate 760Sstevel@tonic-gate if (-r $cwfile) { 770Sstevel@tonic-gate push (@cwfiles, $cwfile); 780Sstevel@tonic-gate } else { 790Sstevel@tonic-gate die "$cwfile is not readable" unless $optional; 800Sstevel@tonic-gate } 810Sstevel@tonic-gate } 820Sstevel@tonic-gate # look for a line starting with "Cw" 830Sstevel@tonic-gate if (/^Cw(.*)$/) { 840Sstevel@tonic-gate my @cws = split (' ', $1); 850Sstevel@tonic-gate while (@cws) { 860Sstevel@tonic-gate my $thishost = shift(@cws); 870Sstevel@tonic-gate push(@hosts, $thishost) 880Sstevel@tonic-gate unless $thishost =~ "$hostname|localhost"; 890Sstevel@tonic-gate } 900Sstevel@tonic-gate } 910Sstevel@tonic-gate } 920Sstevel@tonic-gate close(CF); 930Sstevel@tonic-gate 940Sstevel@tonic-gate for my $cwfile (@cwfiles) { 950Sstevel@tonic-gate if (open(CW, "<$cwfile")) { 960Sstevel@tonic-gate while (<CW>) { 970Sstevel@tonic-gate next if /^\#/; 980Sstevel@tonic-gate my $thishost = $ARG; 990Sstevel@tonic-gate chop($thishost); 1000Sstevel@tonic-gate push(@hosts, $thishost) 1010Sstevel@tonic-gate unless $thishost =~ $hostname; 1020Sstevel@tonic-gate } 1030Sstevel@tonic-gate close(CW); 1040Sstevel@tonic-gate } else { 1050Sstevel@tonic-gate die "open $cwfile: $ERRNO"; 1060Sstevel@tonic-gate } 1070Sstevel@tonic-gate } 1080Sstevel@tonic-gate # Do this automatically if no client hosts are specified. 1090Sstevel@tonic-gate $boot_check = "yes"; 1100Sstevel@tonic-gate} 1110Sstevel@tonic-gate 1120Sstevel@tonic-gatemy ($proto) = (getprotobyname('tcp'))[2]; 1130Sstevel@tonic-gate($port) = (getservbyname($port, 'tcp'))[2] 1140Sstevel@tonic-gate unless $port =~ /^\d+/; 1150Sstevel@tonic-gate 1160Sstevel@tonic-gateif ($boot_check) { 1170Sstevel@tonic-gate # first connect to localhost to verify that we can accept connections 1180Sstevel@tonic-gate print "verifying that localhost is accepting SMTP connections\n" 1190Sstevel@tonic-gate if ($verbose); 1200Sstevel@tonic-gate my $localhost_ok = 0; 1210Sstevel@tonic-gate ($name, my $laddr) = (gethostbyname('localhost'))[0, 4]; 1220Sstevel@tonic-gate (!defined($name)) && die "gethostbyname failed, unknown host $server"; 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate # get a connection 1250Sstevel@tonic-gate my $sinl = sockaddr_in($port, $laddr); 1260Sstevel@tonic-gate my $save_errno = 0; 1270Sstevel@tonic-gate for (my $num_tries = 1; $num_tries < 5; $num_tries++) { 1280Sstevel@tonic-gate socket(S, &PF_INET, &SOCK_STREAM, $proto) 1290Sstevel@tonic-gate || die "socket: $ERRNO"; 1300Sstevel@tonic-gate if (connect(S, $sinl)) { 1310Sstevel@tonic-gate &alarm("sending 'quit' to $server"); 1320Sstevel@tonic-gate print S "quit\n"; 1330Sstevel@tonic-gate alarm(0); 1340Sstevel@tonic-gate $localhost_ok = 1; 1350Sstevel@tonic-gate close(S); 1360Sstevel@tonic-gate alarm(0); 1370Sstevel@tonic-gate last; 1380Sstevel@tonic-gate } 1390Sstevel@tonic-gate print STDERR "localhost connect failed ($num_tries)\n"; 1400Sstevel@tonic-gate $save_errno = $ERRNO; 1410Sstevel@tonic-gate sleep(1 << $num_tries); 1420Sstevel@tonic-gate close(S); 1430Sstevel@tonic-gate alarm(0); 1440Sstevel@tonic-gate } 1450Sstevel@tonic-gate if (! $localhost_ok) { 1460Sstevel@tonic-gate die "could not connect to localhost: $save_errno\n"; 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate} 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate# look it up 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate($name, my $thataddr) = (gethostbyname($server))[0, 4]; 1530Sstevel@tonic-gate(!defined($name)) && die "gethostbyname failed, unknown host $server"; 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate# get a connection 1560Sstevel@tonic-gatemy $sinr = sockaddr_in($port, $thataddr); 1570Sstevel@tonic-gatesocket(S, &PF_INET, &SOCK_STREAM, $proto) 1580Sstevel@tonic-gate || die "socket: $ERRNO"; 1590Sstevel@tonic-gateprint "server = $server\n" if (defined($verbose)); 1600Sstevel@tonic-gate&alarm("connect to $server"); 1610Sstevel@tonic-gateif (! connect(S, $sinr)) { 1620Sstevel@tonic-gate die "cannot connect to $server: $ERRNO\n"; 1630Sstevel@tonic-gate} 1640Sstevel@tonic-gatealarm(0); 1650Sstevel@tonic-gateselect((select(S), $OUTPUT_AUTOFLUSH = 1)[0]); # don't buffer output to S 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate# read the greeting 1680Sstevel@tonic-gate&alarm("greeting with $server"); 1690Sstevel@tonic-gatewhile (<S>) { 1700Sstevel@tonic-gate alarm(0); 1710Sstevel@tonic-gate print if $verbose; 1720Sstevel@tonic-gate if (/^(\d+)([- ])/) { 1730Sstevel@tonic-gate # SMTP's initial greeting response code is 220. 1740Sstevel@tonic-gate if ($1 != 220) { 1750Sstevel@tonic-gate &alarm("giving up after bad response from $server"); 1760Sstevel@tonic-gate &read_response($2, $verbose); 1770Sstevel@tonic-gate alarm(0); 1780Sstevel@tonic-gate print STDERR "$server: NOT 220 greeting: $ARG" 1790Sstevel@tonic-gate if ($verbose); 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate last if ($2 eq " "); 1820Sstevel@tonic-gate } else { 1830Sstevel@tonic-gate print STDERR "$server: NOT 220 greeting: $ARG" 1840Sstevel@tonic-gate if ($verbose); 1850Sstevel@tonic-gate close(S); 1860Sstevel@tonic-gate } 1870Sstevel@tonic-gate &alarm("greeting with $server"); 1880Sstevel@tonic-gate} 1890Sstevel@tonic-gatealarm(0); 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate&alarm("sending ehlo to $server"); 1920Sstevel@tonic-gate&ps("ehlo $hostname"); 1930Sstevel@tonic-gatemy $etrn_support = 0; 1940Sstevel@tonic-gatewhile (<S>) { 1950Sstevel@tonic-gate if (/^250([- ])ETRN(.+)$/) { 1960Sstevel@tonic-gate $etrn_support = 1; 1970Sstevel@tonic-gate } 1980Sstevel@tonic-gate print if $verbose; 1990Sstevel@tonic-gate last if /^\d+ /; 2000Sstevel@tonic-gate} 2010Sstevel@tonic-gatealarm(0); 2020Sstevel@tonic-gate 2030Sstevel@tonic-gateif ($etrn_support) { 2040Sstevel@tonic-gate print "ETRN supported\n" if ($verbose); 2050Sstevel@tonic-gate &alarm("sending etrn to $server"); 2060Sstevel@tonic-gate while (@hosts) { 2070Sstevel@tonic-gate $server = shift(@hosts); 2080Sstevel@tonic-gate &ps("etrn $server"); 2090Sstevel@tonic-gate while (<S>) { 2100Sstevel@tonic-gate print if $verbose; 2110Sstevel@tonic-gate last if /^\d+ /; 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate sleep(1); 2140Sstevel@tonic-gate } 2150Sstevel@tonic-gate} else { 2160Sstevel@tonic-gate print "\nETRN not supported\n\n" 2170Sstevel@tonic-gate} 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate&alarm("sending 'quit' to $server"); 2200Sstevel@tonic-gate&ps("quit"); 2210Sstevel@tonic-gatewhile (<S>) { 2220Sstevel@tonic-gate print if $verbose; 2230Sstevel@tonic-gate last if /^\d+ /; 2240Sstevel@tonic-gate} 2250Sstevel@tonic-gateclose(S); 2260Sstevel@tonic-gatealarm(0); 2270Sstevel@tonic-gate 2280Sstevel@tonic-gateselect(STDOUT); 2290Sstevel@tonic-gateexit(0); 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate# print to the server (also to stdout, if -v) 2320Sstevel@tonic-gatesub ps 2330Sstevel@tonic-gate{ 2340Sstevel@tonic-gate my ($p) = @_; 2350Sstevel@tonic-gate print ">>> $p\n" if $verbose; 2360Sstevel@tonic-gate print S "$p\n"; 2370Sstevel@tonic-gate} 2380Sstevel@tonic-gate 2390Sstevel@tonic-gatesub alarm 2400Sstevel@tonic-gate{ 2410Sstevel@tonic-gate ($alarm_action) = @_; 2420Sstevel@tonic-gate alarm(10); 2430Sstevel@tonic-gate $SIG{ALRM} = 'handle_alarm'; 2440Sstevel@tonic-gate} 2450Sstevel@tonic-gate 2460Sstevel@tonic-gatesub handle_alarm 2470Sstevel@tonic-gate{ 2480Sstevel@tonic-gate &giveup($alarm_action); 2490Sstevel@tonic-gate} 2500Sstevel@tonic-gate 2510Sstevel@tonic-gatesub giveup 2520Sstevel@tonic-gate{ 2530Sstevel@tonic-gate my $reason = @_; 2540Sstevel@tonic-gate (my $pk, my $file, my $line); 2550Sstevel@tonic-gate ($pk, $file, $line) = caller; 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate print "Timed out during $reason\n" if $verbose; 2580Sstevel@tonic-gate exit(1); 2590Sstevel@tonic-gate} 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate# read the rest of the current smtp daemon's response (and toss it away) 2620Sstevel@tonic-gatesub read_response 2630Sstevel@tonic-gate{ 2640Sstevel@tonic-gate (my $done, $verbose) = @_; 2650Sstevel@tonic-gate (my @resp); 2660Sstevel@tonic-gate print my $s if $verbose; 2670Sstevel@tonic-gate while (($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) { 2680Sstevel@tonic-gate print $s if $verbose; 2690Sstevel@tonic-gate $done = $1; 2700Sstevel@tonic-gate push(@resp, $s); 2710Sstevel@tonic-gate } 2720Sstevel@tonic-gate return @resp; 2730Sstevel@tonic-gate} 274