#!/bin/perl
#Telnet client.  Speaks the telnet protocol and declines all telnet options.

use Socket;
use POSIX;        #for WIFEXITED and WEXITSTATUS

use FileHandle;   #for autoflush
STDOUT->autoflush();

$telnet_port = getservbyname('telnet', 'tcp')   #look up telnet in /etc/services
	or warn "$0: couldn't find port number for telnet";
	exit 1;

if (@ARGV == 1) {   #This @ARGV is in a scalar context
	$port = $telnet_port;
} elsif (@ARGV == 2) {
	$port = $ARGV[1];
} else {
	warn "$0: requires 1 or 2 command line arguments";
	exit 2;
}

socket(S, AF_INET, SOCK_STREAM, 0) or die "$0: $!";
S->autoflush();      #pp. 130, 444 in O'Reilly Perl book

$ip = inet_aton($ARGV[0])          or die "$0: inet_aton failed";
$address = sockaddr_in($port, $ip) or die "$0: sockaddr_in failed";

$dotted = inet_ntoa($ip);
print "Trying $dotted...\n";
connect(S, $address)               or die "$0: $!";
$name = gethostbyaddr($ip, AF_INET) or $name = $dotted;
print "Connected to $name.\n";

$SIG{CHLD} = 'death_of_child';
$pid = fork();
defined $pid or die "$0: $!";

if ($pid == 0) {
	#Arrive here if I am the child.

	#Telnet protocol codes from RFC 854

	$WILL = "\xFB";   #251
	$WONT = "\xFC";   #252

	$DO   = "\xFD";   #253
	$DONT = "\xFE";   #254

	$IAC  = "\xFF";   #255: "interpret as command"

	while (sysread(S, $c, 1) == 1) {   #Read one byte from the server.
		if ($port != $telnet_port || $c ne $IAC) {
			print $c;
			next;        #Go back to line 52.
		}

		#Read a telnet command and the following option.
		sysread(S, $command, 1);
		sysread(S, $option, 1);

		#If the server says it's willing to perform a telnet option,
		#tell it not to.
		if ($command eq $WILL || $command eq $WONT) {
			print S "$IAC$DONT$option";
		}

		#If the server asks us to perform a telnet option,
		#tell it we won't.
		elsif ($command eq $DO || $command eq $DONT) {
			print S "$IAC$WONT$option";
		}

		else {
			warn "$0: Don't know telnet command ", ord($command);
			exit 1;
		}
	}

	close(S) or die "$0: $!";
	exit 0;
}

#Arrive here if I am the parent.
#Copy from the stdin to the server.
while (<STDIN>) {                #Read one line from the stdin
	print S $_;                  #and write it to the server.
}

shutdown(S, SHUT_RDWR) or die "$0: $!";
print "Connection closed.\n";
exit 0;

sub death_of_child {
	$p = wait();
	if ($p != $pid) {
		warn "$0: My child is $pid, not $p";
		exit 3;
	}

	if (!WIFEXITED($?)) {
		warn "$0: My child did not return an exit status";
		exit 4;
	}

	$status = WEXITSTATUS($?);
	if ($status != 0) {
		warn "Child returned exit status $status";
		exit 5;
	}

	shutdown(S, SHUT_RDWR) or die "$0: $!";
	print "Connection closed by foreign host.\n";
	exit 0;
}
