1#!/usr/perl5/bin/perl -w 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or http://www.opensolaris.org/os/licensing. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22# 23# Copyright (c) 1996-2000 by John T. Beck <john@beck.org> 24# All rights reserved. 25# 26# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 27# Use is subject to license terms. 28# 29 30require 5.8.4; # minimal Perl version required 31use strict; 32use warnings; 33use English; 34 35use Socket; 36use Getopt::Std; 37our ($opt_v, $opt_b); 38 39# system requirements: 40# must have 'hostname' program. 41 42my $port = 'smtp'; 43select(STDERR); 44 45chop(my $name = `hostname || uname -n`); 46 47my ($hostname) = (gethostbyname($name))[0]; 48 49my $usage = "Usage: $PROGRAM_NAME [-bv] host [args]"; 50getopts('bv'); 51my $verbose = $opt_v; 52my $boot_check = $opt_b; 53my $server = shift(@ARGV); 54my @hosts = @ARGV; 55die $usage unless $server; 56my @cwfiles = (); 57my $alarm_action = ""; 58 59if (!@hosts) { 60 push(@hosts, $hostname); 61 62 open(CF, "</etc/mail/sendmail.cf") || 63 die "open /etc/mail/sendmail.cf: $ERRNO"; 64 while (<CF>){ 65 # look for a line starting with "Fw" 66 if (/^Fw.*$/) { 67 my $cwfile = $ARG; 68 chop($cwfile); 69 my $optional = /^Fw-o/; 70 # extract the file name 71 $cwfile =~ s,^Fw[^/]*,,; 72 73 # strip the options after the filename 74 $cwfile =~ s/ [^ ]+$//; 75 76 if (-r $cwfile) { 77 push (@cwfiles, $cwfile); 78 } else { 79 die "$cwfile is not readable" unless $optional; 80 } 81 } 82 # look for a line starting with "Cw" 83 if (/^Cw(.*)$/) { 84 my @cws = split (' ', $1); 85 while (@cws) { 86 my $thishost = shift(@cws); 87 push(@hosts, $thishost) 88 unless $thishost =~ "$hostname|localhost"; 89 } 90 } 91 } 92 close(CF); 93 94 for my $cwfile (@cwfiles) { 95 if (open(CW, "<$cwfile")) { 96 while (<CW>) { 97 next if /^\#/; 98 my $thishost = $ARG; 99 chop($thishost); 100 push(@hosts, $thishost) 101 unless $thishost =~ $hostname; 102 } 103 close(CW); 104 } else { 105 die "open $cwfile: $ERRNO"; 106 } 107 } 108 # Do this automatically if no client hosts are specified. 109 $boot_check = "yes"; 110} 111 112my ($proto) = (getprotobyname('tcp'))[2]; 113($port) = (getservbyname($port, 'tcp'))[2] 114 unless $port =~ /^\d+/; 115 116if ($boot_check) { 117 # first connect to localhost to verify that we can accept connections 118 print "verifying that localhost is accepting SMTP connections\n" 119 if ($verbose); 120 my $localhost_ok = 0; 121 ($name, my $laddr) = (gethostbyname('localhost'))[0, 4]; 122 (!defined($name)) && die "gethostbyname failed, unknown host $server"; 123 124 # get a connection 125 my $sinl = sockaddr_in($port, $laddr); 126 my $save_errno = 0; 127 for (my $num_tries = 1; $num_tries < 5; $num_tries++) { 128 socket(S, &PF_INET, &SOCK_STREAM, $proto) 129 || die "socket: $ERRNO"; 130 if (connect(S, $sinl)) { 131 &alarm("sending 'quit' to $server"); 132 print S "quit\n"; 133 alarm(0); 134 $localhost_ok = 1; 135 close(S); 136 alarm(0); 137 last; 138 } 139 print STDERR "localhost connect failed ($num_tries)\n"; 140 $save_errno = $ERRNO; 141 sleep(1 << $num_tries); 142 close(S); 143 alarm(0); 144 } 145 if (! $localhost_ok) { 146 die "could not connect to localhost: $save_errno\n"; 147 } 148} 149 150# look it up 151 152($name, my $thataddr) = (gethostbyname($server))[0, 4]; 153(!defined($name)) && die "gethostbyname failed, unknown host $server"; 154 155# get a connection 156my $sinr = sockaddr_in($port, $thataddr); 157socket(S, &PF_INET, &SOCK_STREAM, $proto) 158 || die "socket: $ERRNO"; 159print "server = $server\n" if (defined($verbose)); 160&alarm("connect to $server"); 161if (! connect(S, $sinr)) { 162 die "cannot connect to $server: $ERRNO\n"; 163} 164alarm(0); 165select((select(S), $OUTPUT_AUTOFLUSH = 1)[0]); # don't buffer output to S 166 167# read the greeting 168&alarm("greeting with $server"); 169while (<S>) { 170 alarm(0); 171 print if $verbose; 172 if (/^(\d+)([- ])/) { 173 # SMTP's initial greeting response code is 220. 174 if ($1 != 220) { 175 &alarm("giving up after bad response from $server"); 176 &read_response($2, $verbose); 177 alarm(0); 178 print STDERR "$server: NOT 220 greeting: $ARG" 179 if ($verbose); 180 } 181 last if ($2 eq " "); 182 } else { 183 print STDERR "$server: NOT 220 greeting: $ARG" 184 if ($verbose); 185 close(S); 186 } 187 &alarm("greeting with $server"); 188} 189alarm(0); 190 191&alarm("sending ehlo to $server"); 192&ps("ehlo $hostname"); 193my $etrn_support = 0; 194while (<S>) { 195 if (/^250([- ])ETRN(.+)$/) { 196 $etrn_support = 1; 197 } 198 print if $verbose; 199 last if /^\d+ /; 200} 201alarm(0); 202 203if ($etrn_support) { 204 print "ETRN supported\n" if ($verbose); 205 &alarm("sending etrn to $server"); 206 while (@hosts) { 207 $server = shift(@hosts); 208 &ps("etrn $server"); 209 while (<S>) { 210 print if $verbose; 211 last if /^\d+ /; 212 } 213 sleep(1); 214 } 215} else { 216 print "\nETRN not supported\n\n" 217} 218 219&alarm("sending 'quit' to $server"); 220&ps("quit"); 221while (<S>) { 222 print if $verbose; 223 last if /^\d+ /; 224} 225close(S); 226alarm(0); 227 228select(STDOUT); 229exit(0); 230 231# print to the server (also to stdout, if -v) 232sub ps 233{ 234 my ($p) = @_; 235 print ">>> $p\n" if $verbose; 236 print S "$p\n"; 237} 238 239sub alarm 240{ 241 ($alarm_action) = @_; 242 alarm(10); 243 $SIG{ALRM} = 'handle_alarm'; 244} 245 246sub handle_alarm 247{ 248 &giveup($alarm_action); 249} 250 251sub giveup 252{ 253 my $reason = @_; 254 (my $pk, my $file, my $line); 255 ($pk, $file, $line) = caller; 256 257 print "Timed out during $reason\n" if $verbose; 258 exit(1); 259} 260 261# read the rest of the current smtp daemon's response (and toss it away) 262sub read_response 263{ 264 (my $done, $verbose) = @_; 265 (my @resp); 266 print my $s if $verbose; 267 while (($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) { 268 print $s if $verbose; 269 $done = $1; 270 push(@resp, $s); 271 } 272 return @resp; 273} 274