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