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