#!/usr/bin/env perl
# $Id: muxsend.pl,v 1.18 2012/08/12 22:25:31 ksb Exp $
# Send a file (or stdin) to a tcpmux service			(petef)

use lib '/usr/local/lib/sac/perl'.join('.', unpack('c*', $^V)),
	'/usr/local/lib/sac';

use Getopt::Std;
use IO::Handle qw(autoflush);
use Socket;
use strict;
require 'sysexits.ph';

my($progname, %opts);
my($host, $ipout, $sockaddr, $reply, $port, $proto, $file);

$progname = $0;
$progname =~ s/.*\///;
getopts("VhOp:f:ux", \%opts);

if ($opts{'V'}) {
	print "$progname: ", '$Id: muxsend.pl,v 1.18 2012/08/12 22:25:31 ksb Exp $', "\n";
	exit EX_OK();
}

my($usage) = 'usage [-Oux] [-f file] [-p port] host [services]';
if ($opts{'h'}) {
	print "$progname: $usage\n",
		"$progname: usage -h\n",
		"$progname: usage -V\n",
		"f file  file to read input from (defaults to stdin)\n",
		"h       show this help message\n",
		"O       display replies from the service\n",
		"p port  tcpmux port (default 1)\n",
		"u       use unbuffered output under -O\n",
		"V       show only version information\n",
		"x       trace replies from the remote service on stderr\n",
		"host    host running tcpmux\n",
		"service request the named tcpmux services in turn\n";
	exit EX_OK();
}

$host = shift @ARGV;
if (!defined($host)) {
	print STDERR "$progname: $usage\n";
	exit EX_USAGE();
}
push(@ARGV, $progname) if (0 == scalar(@ARGV));
$file = $opts{'f'};
$file ||= '-';
$opts{'p'} ||= 1;

redirect:
if (! ($ipout = inet_aton($host))) {
	print STDERR "$progname: $host: invalid hostname\n";
	exit EX_NOHOST();
}
(undef,undef,$port,undef) = getservbyname($opts{'p'}, 'tcp');
(undef,undef,$port,undef) = getservbyport($opts{'p'}, 'tcp')
	unless defined($port);
if (!defined($port) && $opts{'p'} =~ m/^(\d+)$/o) {
	$port = $1;
}
if (!defined($port)) {
	print STDERR "$progname: $opts{'p'}: invalid port\n";
	exit EX_DATAERR();
}
$sockaddr = sockaddr_in($port, $ipout) || die "$progname: sockaddr_in";
$proto = getprotobyname('tcp') || die "$progname: getproto";
if (! socket(SOCKET, PF_INET, SOCK_STREAM, $proto)) {
	print STDERR "$progname: socket: $!\n";
	exit EX_OSERR();
}

if (! connect(SOCKET, $sockaddr)) {
	print STDERR "$progname: connect: $host: $!\n";
	exit EX_OSERR();
}
autoflush SOCKET 1;
autoflush STDOUT 1 if ($opts{'u'});

# negotiate with the mux to get to our service
my($muxservice);
while (defined($muxservice = shift @ARGV)) {
	print SOCKET "$muxservice\r\n";
	next if ('help' eq $muxservice);
	$reply = <SOCKET>;
	while ($reply =~ m/[\012\015]$/o) {
		chop $reply;
	}
	print STDERR "$reply\n" if $opts{'x'};
	next if ($reply =~ m/^\+/);
	if ($reply =~ m/^@([^:]*):([^:]*)/o) {
		my($looph, $loopp) = ($host, $opts{'p'});
		$opts{'p'} = $2 if (defined($2) and '' ne $2);
		$host = $1 if (defined($1) and '' ne $1);
		close SOCKET;
		unshift @ARGV, $muxservice;
		if ($host eq $looph and $loopp eq $opts{'p'}) {
			print STDERR "$progname: redirection loop: $muxservice on $looph:$loopp\n";
			exit EX_SOFTWARE();
		}
		goto redirect;
	}
	print STDERR "$progname: $host: $muxservice: $reply\n";
	exit EX_PROTOCOL();
}
if ($opts{'O'} && 0 == fork) {
	while (<SOCKET>) {
		print STDOUT;
	}
	exit EX_OK();
}

# read from our input (-f) + send to the socket
$file = "<&STDIN" if $file eq '-';
open(IN, "$file") || do {
	print STDERR "$progname: open: $file: $!\n";
	exit EX_CANTCREAT();
};
while(<IN>) {
	print SOCKET;
}
shutdown SOCKET, 1;
if ($opts{'O'}) {
	while (-1 != wait()) {
		# nada
	}
}
close(SOCKET);
close(IN);
exit EX_OK();
