#!/usr/bin/perl
#
# A CLX Daemon accepting telnet connections on a tcp port
#
# You must put this into your /etc/inetd.conf file and have 
# users log in with telnet <yourhost> <portnumber>
#
# To make it work you must first create a password file ~/config/passwd
# with zero length. This file will later contain callsigns, 
# encrypted passwords, host addresses and login dates of users 
# logging in via this port.
#
# Beware! This may be a major security hole when run directly on
# the Internet. Be careful, strange people are out there!
#
# Last Change: DL6RAI Mon Nov  9 18:42:13 GMT 1998

$debug=0;

($home) = (getpwnam('clx_us'))[7];
$passwd = "$home/config/passwd";
$status = "$home/log/clx_stat";
$cluster = "$home/config/cluster_par";
$limit = 300;
$log = "$home/log/clxd.log";
$if = "$home/bin/net_usr";
$ENV{'LD_LIBRARY_PATH'}="$home/lib:$home/postgres/lib";

if ( -r "$home/config/adv_txt.default" ) {
	$message_file = "$home/config/adv_txt.default";
} else {
	$message_file = "$home/config/adv_txt";
}

open(MSG,"< $message_file");
while (<MSG>) {
	chomp;
	($nr,$text) = split(/[\s\t]+/,$_,2);

	# Ignore any `*' and `?'
	$nr =~ s/[\*\?]//;

	# Only the numbers >= 8000 are for clxd
	if ( $nr =~ /8.../ ) {

		# Save the `%'
		$text =~ s/%/%%/g;

		# all messages not ending in `:' get a \n
		if ( $text =~ /[^:]$/ ) { 
			$text = $text . "\\n" 
		}

		# Now construct a new command ...
		$cmd = sprintf("\$msg[$nr]=\"$text\"");

		# ...and exectue it
		eval $cmd;

		if ( $debug ) {
			send_message("Msg #%s, text: %s\n", $nr, $text);
			send_message($cmd, "\n");	
		}
	}
}
close(MSG);

open(CLU,"< $cluster");
while (<CLU>) {
	if (/^SECTION:/ || /conn_call:/) {
		$call = (split)[1];
		push(@nodecalls,$call);
	}
}
close(CLU);

$peer = getpeername(STDIN);
($family,$port,$c1,$c2,$c3,$c4) = unpack('S n C C C C',$peer);
$hostaddr = sprintf("%d.%d.%d.%d/%d",$c1,$c2,$c3,$c4,$port);
$date = `date '+%d-%b-%y %H%Mz'`;
chomp($date);

select((select(STDOUT), $| = 1)[$[]);
select((select(STDIN), $| = 1)[$[]);

send_message($msg[8001], $hostaddr);
&log($msg[8021],$hostaddr);

$dead = 0;
if ( -e $status ) {
	($size,$filedate) =  (stat($status))[7,9];
	$now = time();
	$diff = $now - $filedate;
	if ( $diff > $limit ||  $size ne 0 ) { 
		$dead = 1;
	}
} else { $dead = 1; }

if ( $dead ) { 
	if ( -f "$home/clx_etxt" ) {
		system "/bin/cat $home/clx_etxt";
	} elsif ( -f "/usr/local/etc/clx_etxt" ) {
		system "/bin/cat /usr/local/etc/clx_etxt";
	} else {
		send_message($msg[8012], $0, $if);
	}
	exit;
}

&validate;
send_message($msg[8002]);


exec $if, "-m", $call;

sub validate {
	$salt = "wrdlbrmft";
	
	$pw = 'a';
	$pw1 = 'b';
	$max_tries = 3;
	$delay = 3;		# Delay unit

	$callsign_valid = 0;
	$tries = 0;
	while (! $callsign_valid ) {
		if ($tries >= $max_tries) { 
			&log($msg[8027],$hostaddr,$tries);
			exit(1); 
		}
		send_message($msg[8004]);
		$call = <STDIN>;
		$call =~ tr/\r\n//d;
		$call =~ tr/A-Z/a-z/;		      # convert to lower case

		# Fix telnet negotiation garbage - tnx NG7M and Erik
		$call =~ s/[^A-Za-z0-9\-]//g;

		($call,$ssid) = split(/-/,$call);     # find SSID
		&log($msg[8022],$hostaddr,$call);

		$callsign_valid = &c_call($call);
		$tries++;
	}
	
	$tries = 0;
	while ($pw ne $pw1) {
		
		open(PASSWD,$passwd) || die "Cannot open $passwd\n";
		
		$found = 0;
		while ( ($var = <PASSWD>) && (! $found) ) {
			chomp($var);
			($searchcall,$pw,$from) = split(/:/,$var);
			$found = ( $searchcall eq $call );
		}
		close(PASSWD);
	
		if ($found) {
			$pw1 = &pw_read($msg[8005]);
			if ($pw ne $pw1) { 
				$tries++;
				&log($msg[8023],$call,$tries);
				if ($tries >= $max_tries) { 
					&log($msg[8024],$call,$tries);
					&log($msg[8027],$hostaddr,$tries);
					exit(1); 
				}
				send_message($msg[8006]);
				sleep($delay*$tries);
			}
		}
		else {
			while ($pw ne $pw1) {
				$pw = &pw_read($msg[8007]);
				$pw1 = &pw_read($msg[8008]);
				if ($pw ne $pw1) {
					$tries++;
					if ($tries >= $max_tries) { 
						exit(1); 
					}
					send_message($msg[8009]);
					sleep($delay*$tries);
				}
			}
			
			open(PASSWD,">> $passwd");
			print PASSWD "$call:$pw:$hostaddr:$date\n";
			close(PASSWD);
			&log($msg[8025],$call,$hostaddr);
		}

	}

	if ( "$ssid" ne "" ) {
		$call .= "-$ssid";
	}

	send_message($msg[8010], $call);
	&log($msg[8026],$call,$hostaddr);

	if (grep(/^$call$/,@nodecalls)) {
                # make telnet client turn off echo if it's a node
		printf("%c%c%c",255,251,1);
	}

	sub pw_read {

	#
	# I did not find out a good way how to suppress 
	# the echo with socket connections.
	#
	
		local($string) = @_;
		print "$string";
		$tty = `tty -s`;

		$line = <STDIN>;
		$line =~ s/[\r\n]//g;
		$line =~ s/\377..//g; # ignore any telnet negotiation frames

		$p = crypt($line, $salt);
		return $p;
	
	}
}


sub c_call {
	local($c) = @_;
	
	@valid_patterns = (	'CNCC',		# W4ZR
				'CNNC',		# Y34K
				'NCNC',		# 9N1A
				'CCNC',		# NL7G
				'CNCCC',	# G4KHG
				'CCNCC',	# DF7RX
				'CNNCC',	# T47CW
				'NCNCC',	# 9Q5XX
				'CNNCCC',	# S56SAC
				'CCNCCC',	# DL6RAI
				'NCNCCC',	# 4S7AWG
				'CNCCCC'	# R3ARES
						# I hope I forgot nobody
			);
	
	$valid = 1;
	if ( length($c) < 4 ) { $valid = 0; }
	if ( length($c) > 6 ) { $valid = 0; }
	if ( $valid ) {
		$c =~ tr/a-z/C/;
		$c =~ tr/0-9/N/;
		
		@x = grep(/^$c$/,@valid_patterns);
		$valid = $#x+1;
		if ( $debug ) {
		        send_message("matches: $valid\n");
			foreach $i (@x) {
			    send_message("matches: $valid, Muster: $i $c\n");
			}
		}
	}

	#if (! $valid) { print $msg[8011]; }
	if (! $valid ) { send_message($msg[8011]) }
	return $valid;
}

sub log {
	$log_msg = sprintf(@_[0],@_[1],@_[2],@_[3],@_[4],@_[5]);
	open(LOG,">> $log");
	print LOG "$date: $log_msg";
	close(LOG);
}

sub send_message {

    my @args = @_;
    foreach(@args) {
	s/\r\n$/\n/;
	s/\n$/\r\n/;
    }

    printf(@args);
}

__END__
