#!/usr/local/bin/perl
# secho - echo data on incoming port connections
# Kees Cook 1997

# get the port number from the command line
($port) = @ARGV;
# if there was no command line, set the port to 4848
$port = 4848 unless $port;

# internet addressing is addressing type #2 ? outta the book
$AF_INET = 2;
# Streams are 1 ? outta the book again
$SOCK_STREAM = 1;

# out of the book.  The "pack" code for an internet address
$sockaddr = "S n a4 x8";

# get the protocol # for TCP - from book
($name, $aliases, $proto) = getprotobyname("tcp");
# if the port does begin ^ and end $ with one or more digits \d+
# then get some default port number for TCP from the system - book
if ($port !~ /^\d+$/) {
	($name, $aliases, $port) = getservbyport($port,"tcp");
}

# tell person what port they're going to be running on
print "Port = $port\n";

# smush the info into a system-callable variable - from book
$this = pack($sockaddr, $AF_INET, $port, "\0\0\0\0");

# from book... something to do with clearing buffers?
select(NS); $| = 1; select(stdout);

# try to get a socket for the port, or die with error message - book
socket(S,$AF_INET,$SOCK_STREAM,$proto) || die "$0 [$$]: socket: $!\n";
# bind to the address (associate the port and the TCP socket) - book
bind(S,$this) || die "$0 [$$]: bind: $!\n";
# start listening to that socket for connections - book
listen(S,5) || die "$0 [$$]: listen: $!\n";

# buffer stuff again - book
select(S); $| = 1; select(stdout);

# start counting the connections
for ($con = 1; ; $con++) {
	# tell them what connection # we're on
	print "$0 [$$]: Listening for connection $con....\n";
	# wait for a connection or die on error - book
	($addr = accept(NS,S)) || die "$0 [$$]: accept: $!\n";

	# once we have a connection, fork the process
	if (($child = fork()) == 0) {
		# the child process gets a result of 0 from fork()

		# unpack the system address of the connected socket
		# so we can use it - book
		($af,$port,$inetaddr) = unpack($sockaddr,$addr);
		# unpack the internet address of the socket
		@inetaddr = unpack("C4",$inetaddr);
		# make a dot separated string out of the numbers
		$ip = join(".",@inetaddr);
		# get the DNS name for the INET address - book
		# if it doesn't work, then set to "unknown"
		$name = gethostbyaddr($inetaddr,$af) || "unknown";
		# say what connection, process ID we are, and then
		# identify where the connection came from
		print "$0 [$$]: $con: connect from $name ($ip) port $port\n";

		# as long as the connection is there...
		while (<NS>) {
			# print whatever the hell came through
			print "$0 [$$]: $con: $_";
		}
		# close the socket gracefully
		close(NS);
		# quit this child processes
		print "$0 [$$]: Exiting\n";
		exit;
	}
	# the parent of the child process gets the PID of the child
	# but we don't care what happens to the child.

	# close the parent's copy of the socket (child has it's own open
	# copy) and start waiting for the next connection
	close(NS);
}
