xref: /onnv-gate/usr/src/cmd/sendmail/aux/etrn.pl (revision 8287:771477e4b843)
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