The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -I../p
# Copyright (c) 1998 Sampo Kellomaki <sampo@iki.fi>, All Rights Reserved.
# This software may not be used or distributed for free or under any other
# terms than those detailed in file COPYING. There is ABSOLUTELY NO WARRANTY.

package tcpcat;

# tcpcat.pm - Send a message and receive a reply from server.
#
# Usage: $reply = &tcpcat ($address, $port, $message);
# E.g:   $reply = &tcpcat ('foo.bar.com', 900, "Hello World!");

use Socket;

$trace = 0;

### Start listening on given port

sub open_tcp_server {
    my ($port) = @_;  # port=0 --> let system pick any free port
    $port = getservbyname  ($port, 'tcp') unless $port =~ /^\d+$/;
    my $serv_params = pack ('S n a4 x8', &AF_INET, $port, "\0\0\0\0");
    
    if (socket (SERV_S, &PF_INET, &SOCK_STREAM, 0)) {
        warn "next bind\n" if $trace > 2;
        if (bind (SERV_S, $serv_params)) {
            my $old_out = select (SERV_S); $| = 1; select ($old_out);
	    warn "next listen\n" if $trace > 2;
	    if (listen(SERV_S, 5)) {
		$serv_params = getsockname(SERV_S); # getpeername(SERV_S);
		my ($fam, $sport, $saddr) = unpack('S n a4 x8',$serv_params);
		my ($a,$b,$c,$d) = unpack('C4',$saddr);
		$saddr = "$a.$b.$c.$d";
		warn "bound to $saddr:$sport\n" if $trace > 2;
		return ($saddr, $sport); # Success, now we're ready to accept
	    }
        }
    }
    warn "$0 $$: open_tcp_server: failed 0.0.0.0, $port ($!)\n";
    close SERV_S;
    return 0; # Fail
}

### Open stream to given host and port

sub open_tcp_connection {
    my ($dest_serv, $port) = @_;
    $port = getservbyname  ($port, 'tcp') unless $port =~ /^\d+$/;
    my $dest_serv_ip = gethostbyname ($dest_serv);
    unless (defined($dest_serv_ip)) {
        warn "$0 $$: open_tcp_connection: destination host not found:"
            . " `$dest_serv' (port $port) ($!)\n";
        return 0;
    }
    my $dest_serv_params = pack ('S n a4 x8', &AF_INET, $port, $dest_serv_ip);
    
    if (socket (S, &PF_INET, &SOCK_STREAM, 0)) {
        warn "next connect\n" if $trace > 2;
        if (connect (S, $dest_serv_params)) {
            my $old_out = select (S); $| = 1; select ($old_out);
            warn "connected to $dest_serv, $port\n" if $trace > 2;
            return 1; # Success
        }
    }
    warn "$0 $$: open_tcp_connection: failed `$dest_serv', $port ($!)\n";
    close S;
    return 0; # Fail
}

### Perform full roundtrip: open, write, read, close

sub tcpcat { # address, port, message --> returns reply, undef on error
    my ($dest_serv, $port, $out_message) = @_;
    my $reply = '';
    
    return unless (open_tcp_connection($dest_serv, $port));
    
    warn "$0 $$: tcpcat: sending `$out_message'\n" if $trace>2;
    print S $out_message;
    shutdown S, 1;   # Half close --> No more output, sends EOF to server
    warn "$0 $$: tcpcat: receiving...\n" if $trace>2;
    while (<S>)      { $reply .= $_; }
    warn "$0 $$: tcpcat: Done -- EOF. Got `$reply'.\n" if $trace>1;
    
    close S;
    return $reply;
}

1;
#__END__