#!/usr/bin/perl
#
# This program works like a remote TNC for K1EA's Contest Software CT 
# when using Ethernet networking a la K1TTT. It opens a UDP socket
# for transmitting and receiving A and B type packets which - in CT
# convention are traffic packets to and from the remote TNC.
# A second process is forked for the rx job while the main program
# keeps going with the tx task.
# rx means: take anything sent from CT packet terminal input window
# and send it to the net_usr process
# tx meand: take anything that comes down the net_usr stream and
# send it out to the CT network.
#
# This program is run as a connect script. Configure this in your
# ~/config/cluster_par file as a passive user link connection,
# using x as the connect interface, and ct_remote_tnc as the
# connect path. 
#
# Additionally you can specify some parameters:
# 1. callsign that the station called has
# 2. IP address to which the packets are directed (problem: see below)
# 3. Port address used if different from 9870
#
# Here is an example
# SECTION:       cn8ww           # partner call
# conn_int:      x               # ax25/wampes/script
# conn_act:      passive         # active/passive
# conn_type:     clx             # partner type
# conn_prot:     user            # protocol type
# conn_path:     ct_remote_tnc cn8ww
# conn_ping:     no              # periodic link check
# mail_fwd:      no              # mail forwarding
#
# Open problems:
# I do not know how to form an IP broadcast address from within Linux.
# NETTSR does not care about the destination address but I need some
# address which makes the packets go to the ethernet segment (rather
# than go to the kernel which is the case with 127.0.0.1).
#
# Last Change: DL6RAI Sun Nov  7 19:12:23 GMT 1999

use IO::Socket;
use strict;
use FileHandle;
use IPC::Open2;

use constant MAXLEN => 1024;

my $nodecall = shift || "xx0xx"; # default callsign for node
my $mycall = shift || "xx0xx";   # default callsign for user
my $host = shift || "192.0.0.4"; # this is my local network, still looking for
				 # a more general solution
my $port = shift || 9870; 	 # Default port for NETTSR

my $quiet = 1;

my $junk = <STDIN>;

my $S = IO::Socket::INET->new(	Proto     => 'udp',
				Type      => SOCK_DGRAM,
				LocalPort => $port
				) || die "can't make socket: $!";

# the following statements are for opening a bidirectional pipe
# to net_usr.
my $in  = 0;
my $out = 0;
my $pid = open2(*in,*out,"net_usr $mycall") 
	|| die "Cannot open2() $!";
out->autoflush();

my $dest = sockaddr_in($port,inet_aton($host));

my $child = fork();
die "Can't fork: $!" unless defined $child;

tx_data($S,$dest,$child,*in) unless $child;
rx_data($S,*out)                 if $child;
close $S;

sub tx_data {

# this is the main program loop - broadcast anything that comes from
# net_usr back into the ethernet.

	my ($S,$dest,$child,$in) = @_;

	print "TX started\n" if ! $quiet;
	while (<$in>) {

		chop;

		my $data = 'B0' . $_;
		my $checksum = unpack("%32C*",$data);
		$checksum |= 0x80;
		$checksum &= 0xFF;
		$data .= chr($checksum) . "\n";
		print "SEND: $data" if ! $quiet;

		$S->send($data,0,$dest) || die "send(): $!";
	}

	kill 'TERM',$child;
}

sub rx_data {

# this is the child program loop - wait for data sent from the CT
# terminal and pass them on to net_usr

	my ($S,$out) = @_;
	my $data;
	print "RX started\n" if ! $quiet;

	while(1) {
		my $sender = $S->recv($data,MAXLEN,0) || die "recv(): $!";
		my ($remote_port,$remote_host) = sockaddr_in($sender);
		$remote_host = inet_ntoa($remote_host);
		warn "Received ",length($data),
			" bytes from [$remote_host,$remote_port]\n" if ! $quiet;
		chomp($data);

		my $checksum = ord(substr($data,-1));
		$data = substr($data,0,-1);

		my $c_checksum = unpack("%32C*",$data);
		$c_checksum |= 0x80;
		$c_checksum &= 0xFF;
		if ( $c_checksum ne $checksum ) {
			print "Network received bad checksum\n" if ! $quiet;
		} else {
			my $stn_nr = ord(substr($data,1,1))-48;
			printf("Stn %02d: ",$stn_nr) if ! $quiet;

			if ($data =~ /^A.*/) {
				$data = substr($data,2);
				printf("RCVD: %s\n",$data) if ! $quiet;

				print $out "$data\n";
			}
		}
	}
}

__END__
