#!/usr/bin/perl -w
$ver		= "brs0.02";

$AF_INET	= 2;
$SOCK_STREAM	= 1;
$SOCK_STREAM	= 3 - $SOCK_STREAM if $sunos5;
$sockaddr	= "S n a4 x8";
$default_port	= 19957;
$sunos5		= -d "/kernel";
$version	= -1;
$MAXVER		= 1;
$MINVER		= 1;
$initial_timeout= 30;
$starttime	= time;
$homedir	= "/usr/brute";	#T Home directory to work from
$logfile	= "brserv.log";	#T log file of all transactions
$ackfile	= "bralloc.log"; #T log file of all allocated keys
$packfile	= "brack.pend";	#T log of pending ACKs
$pkeyfile	= "brkey.pend";	#T log of outstanding keys
$brclient_err	= "brclient.err"; #T temp scratch file for brclient STDERR
$brclient	= "brclient";	#T command to run brclient
$first_key	= 1;		#T first key to give out
$last_key	= 0;		#T last key to give out
$reallocsize	= "50";		#T how many segments to allocate at a time
$brslavec	= "brslave.plc";
$brslaved	= "brslave.pld"; #T perl file to be require'd for dynamic data
$brslavep	= "brslave.plp"; #T perl file to be require'd for project info
$getopts	= "getopts.pl";	#T file to include to get &Getopts()
$exit_on_done	= 0;		#T exit when the initial allocation is done.
$sleep_on_done	= 0;		#T send "SLEEP"s after the initial allocation.
$ack_to_server	= 1;		#T Attempt to relay ACKs to external server
$min_segs_to_send=16;		#T min number of segs to ACK to server

$cr = "\r"; $lf="
"; $crlf = "$cr$lf";

eval "require \"$brslavec\"";	# Setup local configuration
eval "require \"$brslaved\"";	# Setup local data
eval "require \"$brslavep\"";	# Setup project data
eval "require \"$getopts\" || print STDERR \"No $getopts...\";" || print STDERR "No $getopts\n";	# some sort of getopts ?
&setup_strings;
eval "&Getopts(\"c:dEf:hil:nN:riS:v\") || exit(&do_help(3));";

$opt_h if 0;
$opt_i if 0;
$opt_r if 0;
$opt_d if 0;
$opt_n if 0;
$opt_E if 0;
$opt_S if 0;
$opt_v if 0;
$pipeerr if 0;
$rl_next = 0;

require "$opt_c" if $opt_c;

$first_key = hex($opt_f) if defined($opt_f);
$last_key = hex($opt_l) if defined($opt_l);
$last_key = $first_key + $opt_N -1 if defined($opt_N);
$exit_on_done = 1 if $opt_E;
$sleep_on_done = $opt_S if $opt_S;
exit(&do_help(0)) if $opt_h;
exit(&do_info(0)) if $opt_i;
&read_server_info unless $infoinfo && $workinfo;
$myname = &fqdn unless $myname;
$infoinfo="3fda:ssl:$0 $ver:running:pb\@cl.cam.ac.uk:sksp-test.brute.cl.cam.ac.uk/19957$cr
" unless $infoinfo;
$workinfo="#$0 $ver
# file checksum (ignoring # comments and whitespace) is 3fda, key is 9636340d46$cr
COMMENT          This is being used for client/server testing:key is 9636340d46$cr
CLEAR-MASTER     fbc009916010a6153f8f36$cr
CHALLENGE        07ea7b9d65eb61fabb4174e8453a5fc6$cr
CONNECTION-ID    d5e638d68ca8a1aeca2ef8c8e29602a4$cr
SERVER-VERIFY    006599b6d2f2a736$cr
" unless $workinfo;
($currproj)	= $infoinfo =~ /^([^:]+):/ unless defined($currproj);
$ENV{'BRACKSERVERS'} = $keyserv if $keyserv && ! $ENV{'BRACKSERVERS'};
$ENV{'BRKEYSERVERS'} = $ackserv if $ackserv && ! $ENV{'BRKEYSERVERS'};

sub do_help {
	print STDERR $help_msg;
	return @_[$[];
}

sub do_info {
	print STDERR $info_msg;
	return @_[$[];
}

$SIG{'PIPE'} = "pipeerr";
sub pipeerr {
	$logstring .= " ++ PIPE ERROR";
	print STDERR " ++ PIPE ERROR\n";
	$pipefault = 1;
}

$nocmdcode = "501";
$helonovercode = "501";
$timeoutcode = "421";
$helptext = "The following commands are accepted:$cr
HELP QUIT HELO INFO WORK KEYS ACK COMM SERV$cr
";

$keyscode	= "210";
$quitcode	= "220";
$greetcode	= "221"; $greetmsg	= "SKSP server ready";
$okcode		= "250"; $okmsg		= "OK";
$helocode	= "251";

$dot		= "follows, terminated by . on own line";
$workcode       = "310"; $workmsg       = "Project details $dot";
$infocode       = "311"; $infomsg       = "Project list $dot";
$helpcode	= "312"; $helpmsg	= "Help information$dot";

$argscode	= "501"; $argsmsg	= "Incorrect number of arguments";
$verscode	= "502"; $versmsg	= "Protocol version not supported";
$unknowncode	= "503"; $unknownmsg	= "Unknown command";

$noprojectcode  = "510"; $noprojectmsg  = "No such project";

$nohelocode	= "551"; $nohelomsg	= "HELO not received yet";
$rephelocode	= "552"; $rephelomsg	= "HELO already received, this one ignored";

$sleepcode	= "600";
$stopcode	= "601";
$min_interval	= 10;

# Keep -w happy ...
$result = $stream = 0;
$next_keys_time = time-1;
$next_acks_time = time-1;

chdir($homedir);

print STDERR "Port $default_port\n" if $opt_d;
sub open_listener{
	($result, $port, $queue) = @_;
	$SO_REUSEADDR   = 4;
	$SOL_SOCKET     = 0xffff;
	$setreuse = "I";  # how to pack for setsockopt(SOL_SOCKET,SO_REUSEADDR
	$val = pack($setreuse, 1);

	$port = $default_port unless defined $port && $port;
	$queue = 5 unless defined $queue;
	if ($port =~ /^\d+$/) {
		($name, $alias, $proto) = getprotobyname("tcp");
	} else {
		($name, $alias, $port, $proto) = getservbyname($port, "tcp");
	};
	$thataddr = pack("C C C C", 0,0,0,0);
	$that = pack($sockaddr, $AF_INET, $port, $thataddr) || return undef;
	socket(SOCK, $AF_INET, $SOCK_STREAM, $proto) ||
	socket(SOCK, $AF_INET, $SOCK_STREAM = 3 - $SOCK_STREAM, $proto) || return undef;
	setsockopt(SOCK,$SOL_SOCKET,$SO_REUSEADDR,$val) if $opt_r;
	unless (bind(SOCK, $that)) { close(SOCK); return undef; }
	unless (listen(SOCK, $queue)) { close(SOCK); return undef; }
	$_[0] = SOCK;
	return 1;
}

&open_listener($stream) || die("Failed to open listener on port $port, queue length $queue: $!");
$logstring = &datepid . " Started listening on port $port, queue length $queue";

$pendingack = '';
$segs_to_send = 0;
# Reload the pending ACKs.
if ($ack_to_server && open(PACK, $packfile)) {
	while(<PACK>) {
		# 3fda SEARCHED 48c3 08e8 1
		$segs_to_send += $1 if /^[0-9a-fA-F]+ +[a-zA-Z]+ +[0-9a-fA-F]+ +[0-9a-fA-F]+ +(\d+)/;
		$pendingack .= $_;
	}
	close(PACK);
	$segs_to_send *= 2;	# old stuff -- send more urgently
	($pack = $pendingack) =~ s/$crlf/ |crlf| /g;
	$pack =~ s/$cr/ |cr| /g;
	$pack =~ s/$lf/ |lf| /g;
	print STDERR "from $packfile: $pack\n" if $opt_d;
}
if (open(PKEY, $pkeyfile)) {
	if (($_ = <PKEY>) && /^([0-9a-fA-F]+) +([0-9a-fA-F]+)$/) {
		$new_first = hex($1);
		$new_last = hex($2);
		printf STDERR "from $pkeyfile: %04x-%04x %04x-%04x from $_\n",
			$first_key, $last_key, $new_first, $new_last if $opt_d;
		if ($new_first <= $new_last) {
			if ($first_key <= $last_key) {
				printf STDERR "Command line keys (%04x-%04x) given while still pending keys (%04x-%04x)\n", $new_first, $new_last, $first_key, $last_key;
				exit(1);
			}
			$first_key = $new_first;
			$last_key = $new_last;
		}
	}
	else { print STDERR "from $pkeyfile: Just: $_\n" if $opt_d; }
	close(PKEY);
}
else { print STDERR "no $pkeyfile\n" if $opt_d; }
&write_to_file($pkeyfile, sprintf("%04x %04x\n", $first_key, $last_key)) if (
	defined($first_key) && defined($opt_f) && defined($last_key) &&
	($first_key <= $last_key) && ($first_key == hex($opt_f)));
&openlog;

$accept_failed = 0;
$brclient_fails = 0;
$stopping = 0;
while (1) 
{
    $logstring .= sprintf(" ||%d", time-$starttime);
    &log($logstring);
    $hupped = 0;
    if ($ack_to_server && $pendingack && $segs_to_send >= $min_segs_to_send && time > $next_acks_time) {
	$datecomm       = " || dat=" . time;
	$logstring = &datepid . " 0.0.0.0:acks.pending";
	($pack = $pendingack) =~ s/$crlf/ |crlf| /g;
	$pack =~ s/$cr/ |cr| /g;
	$pack =~ s/$lf/ |lf| /g;
	$ENV{'BRACK'} = "$pendingack";
	$res = `$brclient -C$ver -vV 2> $brclient_err`;
	if (($res =~ /^OK/ || $res eq 'OK') && !$?) {
	undef($ENV{'BRACK'});
		&append_to_file($ackfile, sprintf("0000   0 nemo | all ACKed OK - $pack\n"));
		open(PACK, ">$packfile") && close(PACK);
		$logstring .= " |0 all ACKed OK - $pack";
		$pendingack = '';
		$segs_to_send = 0;
	} else {
		undef($ENV{'BRACK'});
		$brclient_fails++;
		$rc = sprintf("%04x", $?);
		$err=`cat $brclient_err`;
		print STDERR "Failed '$brclient -C$ver' ($pack) [$rc,$res]\n";
		if ($rc eq "0700") { # Server suggestion: SLEEP n (600 SLEEP n)
			if ($err =~ /SLEEP (\d+)/) {
				$next_acks_time = time + $1;
			}
		} elsif ($rc eq "0300") { # timed out ....
		} elsif ($rc eq "0200") { # duff args
			$stopping = 1;
		} elsif ($rc eq "0500") { # Duff args
			# Sigh -- we've done all we can :-((
			print STDERR "0500 so OK\n";
			$pendingack = '';
		$segs_to_send = 0;
		} elsif ($rc eq "0800") { # No such project
			# Sigh -- we've done all we can :-((
			print STDERR "0800 so OK\n";
			$pendingack = '';
		$segs_to_send = 0;
		} elsif ($rc eq "0600") { # STOP
			$stopping = 1;
		}
		$err =~ s/\n/\\n/g;
    		$logstring .= " |0 ACK FAILED $rc for $brclient -C$ver (BRACK=$pack) (STDERR=$err) (rc=$rc) $datecomm";
		&append_to_file($ackfile,
"0000   0 nemo | FAILED ($stopping): $brclient -C$ver (BRPACK=$pack) gave 0x$rc [[[$err]]]\n");
		$next_acks_time = time + ($min_interval * $brclient_fails) if time > $next_acks_time;
	}
	&log($logstring);
    }
    if ($first_key > $last_key && time > $next_keys_time && ! $stopping) {
	if ($exit_on_done) {
		&append_to_file($ackfile,
			sprintf("0000   0 nemo | exit as allocation done\n"));
		exit(0);
	}
	$datecomm	= " || dat=" . time;
    	$logstring = &datepid . " 0.0.0.0:keys.exhausted";
	if ($sleep_on_done) {
		&append_to_file($ackfile,
			sprintf("0000   0 nemo | Entering Sleep $sleep_on_done mode as allocation done\n"));
    		$logstring .= " |0 sleep_on_done $sleep_on_done $datecomm";
		$stopping = 1;
		next;
	}
	$res = `$brclient -C$ver -kP$currproj -n$reallocsize 2> $brclient_err`;
	($proj, $start, $len) = split(' ', $res);
	#($proj, $start, $len) = split(' ', `$brclient -C$ver -kP$currproj -n$reallocsize 2> $brclient_err`);
	if (defined($len) && $len ne '') {
	$err=`cat $brclient_err`;
	$err =~ s/\n/\\n/g;
	print STDERR "Got $proj, $start, $len from $brclient -C$ver -kP$currproj -n$reallocsize ($err) - $res\n";
		$brclient_fails = 0;
		$first_key = hex($start);
		$last_key = $first_key + $len -1;
    		$logstring .= " |0 ALLOCATED $proj $start $len $datecomm";
		&append_to_file($ackfile, 
			sprintf("0000   0 nemo | ALLOCATED: %s %s %s (%04x-%04x)\n",
				$proj, $start, $len, $first_key, $last_key));
		&write_to_file($pkeyfile,
			sprintf("%04x %04x\n", $first_key, $last_key));
	} else {
		$brclient_fails++;
		$rc = sprintf("%04x", $?);
		$err=`cat $brclient_err`;
		if ($rc eq "0700") { # Server suggestion: SLEEP n (600 SLEEP n)
			if ($err =~ /SLEEP (\d+)/) {
				$next_keys_time = time + $1;
			}
		} elsif ($rc eq "0300") { # timed out ....
		} elsif ($rc eq "0200") { # duff args
			$stopping = 1;
		} elsif ($rc eq "0800") { # No such project
			$stopping = 1;
		} elsif ($rc eq "0600") { # STOP
			$stopping = 1;
		}
		$err =~ s/\n/\\n/g;
    		$logstring .= " |0 ALLOCATE FAILED $rc for $brclient -kP$currproj -n$reallocsize [[[$err]]] $datecomm";
		&append_to_file($ackfile,
"0000   0 nemo | FAILED ($stopping): $brclient -kP$currproj -n$reallocsize gave 0x$rc [[[$err]]]\n");
		$next_keys_time = time + ($min_interval * $brclient_fails) if time > $next_keys_time;
	}
	&log($logstring);
    }
    $hupped = 0;
    $starttime = time;
    $addr = accept(SERVE,$stream);
    $pref = sprintf("%d", time - $starttime);
    $starttime = time;
    unless ($addr) {
	next if $hupped;
	print STDERR "accept failed!!\n";
	$accept_failed++;
	sleep(5);
	next if $accept_failed < 10;
	die("Too many accepts failed: $!");
    }
    select((select(SERVE), $| = 1)[0]);
    $accept_failed = 0;
    $hellod = 0;
    $closing = 0;
    $pipefault = 0;
    $comment = "";

    ($af,$port,$inetaddr) = unpack('S n a4 x8',$addr);
    undef @addrs;
    undef @aliases;
    undef $addrtype;
    undef $length;
    if ($opt_n) {
	undef($caller);
    } else { ($caller,$aliases,$addrtype,$length,@addrs) = gethostbyaddr($inetaddr, $af); }
    $datecomm	= " || dat=" . time;
    $caller = "unknown" unless defined $caller;
    $address = join('.', unpack('C4',$inetaddr));
    $logstring = &datepid . " $address:$caller";

    select((select(SERVE), $| = 1)[0]);
    $rin = '';
    vec($rin,fileno(SERVE),1) = 1;
    undef($ackinfo);
    $timeout = $initial_timeout;
    print SERVE "$greetcode $greetmsg, Ver $MINVER to $MAXVER. You have $timeout seconds.$crlf";
    $pref .= sprintf(",%d", time - $starttime);
    $logstring .= " {$pref}";
    $starttime = time;
    $input_pending = '';
    while ($_ = &read_line) {
	$_ .= $end unless ($end = chop) eq $lf;
	$_ .= $end unless ($end = chop) eq $cr;
	$logstring .= sprintf(" |%d %s", time-$starttime, $_);
	next if $pipefault;
	($cmd, $arg)= /^[ 	]*([^ 	]+)[ 	]*(.*)$/;
	if (! defined($cmd) || $cmd eq "") { print SERVE "$nocmdcode No command supplied$crlf"; next; }
	$cmd =~ y/a-z/A-Z/;
	if ($cmd eq "QUIT") { &flush_alloc; print SERVE "$quitcode Goodbye $caller$crlf"; last; }
	if ($closing) {
		print SERVE "$quitcode Goodbye $caller -- you should have sent a QUIT$crlf"; last;
	}

	if ($cmd eq "HELP") { print SERVE "$helpcode $helpmsg$crlf$helptext.$crlf"; next; }
	elsif ($cmd eq "HELO") { &helocmd; next; }

	unless ($hellod) { print SERVE "$nohelocode $nohelomsg$crlf"; next; }
	if ($cmd eq "KEYS") { &keyscmd; next; }
	elsif ($cmd eq "COMM") { &commcmd; next; }
	elsif ($cmd eq "ACK")  { &ackcmd;  next; }
	elsif ($cmd eq "SERV") { &servcmd; next; }
	elsif ($cmd eq "INFO") { &infocmd; next; }
	elsif ($cmd eq "WORK") { &workcmd; next; }
	else { print SERVE "$unknowncode $unknownmsg '$cmd'$crlf"; next; }
    }
    unless ($timeout > 0) {
	print SERVE "$timeoutcode Goodbye $caller -- you have been timed out$crlf";
	$logstring .= " ++ TIMEOUT";
	sleep(1);
    }
    close(SERVE);
}

# implement the HELO command
sub helocmd {
	if ($hellod) { print SERVE "$rephelocode $rephelomsg$crlf"; return; }
	if ($arg =~ /(.*) COMM (.*)/) {
		$arg = $1;
		$comment = $2;
	}
	($version, $email, $name) = split(/[ \t\n]+/, $arg, 3);
	unless (defined($version) && (($version) = $version =~ /^(\d+)$/)) {
		print SERVE "$helonovercode $cmd must have a numeric version as a first argument$crlf";
		return;
	}
	unless ($MINVER <= $version && $version <= $MAXVER) {
		print SERVE "$verscode $versmsg, only $MINVER to $MAXVER ($version)$crlf";
		return;
	}
	unless (defined($email)) {
		print SERVE "501 Need a Unique ID$crlf";
		return;
	}
	# $email = "unknown@$caller" unless defined($email);
	$name = "" unless defined($name);
	$fullemail = $email;
	$fullemail = "$name <$email>" if $name;
	$hellod++;
	print SERVE "$helocode $cmd $fullemail using version $version$crlf";
}

# implement the KEYS command
sub keyscmd {
	($project, $segs) = ($arg =~ /^([0-9A-Fa-f]+)[ 	]*(\d+)[ 	]*$/);
	unless (defined($segs) && $segs ne "") {
		$logstring .= " -> duff args";
		print SERVE "$argscode $argsmsg - $cmd expects \"project segs\"$crlf";
		return;
	}
	$project = "$currproj" if $project eq "0000";
	unless ($currproj eq $project) {
		$logstring .= " -> $project ne $currproj";
		print SERVE "$noprojectcode $noprojectmsg$crlf";
		return;
	}
	if ($stopping) {
		$logstring .= " -> $stopcode STOP";
		print SERVE "$stopcode STOP$crlf";
		return;
	}
	($start, $size) = &next_chunk($project, $segs);
	unless (defined($size) && ($size || $size == $segs)) {
		$sleep = $sleep_on_done;
		$sleep = 20 unless $sleep;
		$logstring .= " -> $sleepcode SLEEP $sleep";
		print SERVE "$sleepcode SLEEP $sleep$crlf";
		return;
	}
	printf SERVE "%03d %04x %4d$crlf", $keyscode, $start, $size;
	$logstring .= sprintf(" ==> %s %04x %d", $project, $start, $size);
}

# implement the COMM command
sub commcmd {
	$comment .= " | " if $comment;
	$comment .= $arg;
	print SERVE "$okcode $okmsg$crlf";
}

# implement the ACK command
sub ackcmd {
	$pendingack .= "COMM $comment\n" if $comment;
	$pendingack .= "$arg\n";
	# Log it !
	$segs_to_send += $1 if $arg =~ /^[0-9a-fA-F]+ +[a-zA-Z]+ +[0-9a-fA-F]+ +[0-9a-fA-F]+ +(\d+)/;
	&append_to_file($packfile,
	   sprintf("%s%s\n", ($comment) ? "COMM $comment\n" : "", $arg));
	print SERVE "$okcode $okmsg$crlf";
}

# implement the SERV command
sub servcmd {
	print SERVE "$okcode $myname$crlf";
}

# implement the INFO command
sub infocmd {
	print SERVE "$infocode $infomsg$crlf$infoinfo.$crlf";
}

# implement the WORK command
sub workcmd {
	print SERVE "$workcode $workmsg$crlf$workinfo.$crlf";
}

# Read info with a protective timeout. Can't use buffered as there is no way
# to test for input in the buffer ...
sub read_line {
	$rl_line = $input_pending;
	$input_pending = '';

	while($rl_line !~ /\n/ && &check_input) {
		last unless sysread(SERVE, $rl_next, 1024);
		$rl_line .= $rl_next;
	}
	return undef if $rl_line eq "";
	return undef if $timeout <= 0;

	return $rl_line unless $rl_line =~ /^([^\n]*\n)([^\0]+)/;
print STDERR "	++ Multiline read: [$rl_line] is [$1] and [$2]\n" if $opt_v;
	$input_pending = $2;
	return $1;
}

sub check_input {
	$started = time;
	$oldtimeout = $timeout;
	($nfound,$timeout) = select($dummy=$rin, undef, $dummy=$rin, $timeout);
	$timeout = 0 unless $nfound;

	# The perl man page warns that $timeout may not change on some systems
	$timeout -= time-$started if ($oldtimeout == $timeout);
	return 0 unless $timeout;
	return 1;
}

# Open the logfile
sub openlog {
	open(LOG, ">>$logfile");
	select((select(LOG), $| = 1)[0]);
}

# Log a message ...
sub log {
	print LOG "@_\n";
}

# Return the date and PID for logging purposes
sub datepid {
	($sec,$min,$hour,$mday,$mon,$year,$junk,$junk,$junk) = gmtime(time);
	$mon++;
	return sprintf ("%02d/%02d/%02d %02d:%02d:%02d $$", $year,$mon,$mday,$hour,$min,$sec);
}

# "allocate keyspace" for normal types.
sub next_chunk {
	($project, $number) = @_;
	&flush_alloc;
	return undef if ($first_key > $last_key);
	$pend_key = $first_key + $number;
	$pend_key = $last_key +1 if ($pend_key > $last_key);
	$ackinfo = sprintf("%04x %3d %s%s%s%s%s",
			$first_key, $pend_key-$first_key, $fullemail,
			" || $address:$caller",
			(($comment) ? " || " : ""), $comment, $datecomm);
	return ($first_key, $pend_key - $first_key);
}

sub flush_alloc {
	return unless defined($ackinfo);
	&append_to_file($ackfile, "$ackinfo\n");
	undef($ackinfo);
	$first_key = $pend_key;
	&write_to_file($pkeyfile, (($first_key <= $last_key) ?
		sprintf("%04x %04x\n", $first_key, $last_key) : ""));
}

# append a string to a file ...
sub append_to_file {
	return 1 if (! defined($_[1]) || $_[1] eq "");
	open(APPENDFILE, ">>$_[0]")	|| return;
	print APPENDFILE $_[1]		|| return;
	print APPENDFILE "\n" unless $_[1] =~ /[\n\r]$/;
	close(APPENDFILE)		|| return;
	return 1;
}
# write a string to a file ...
sub write_to_file {
	open(WRITEFILE, ">$_[0]")	|| return;
	if (defined($_[1]) && $_[1] ne "") {
		print WRITEFILE $_[1]	|| return;
		print WRITEFILE "\n" unless $_[1] =~ /[\n\r]$/;
	}
	close(WRITEFILE)		|| return;
	return 1;
}

# Try to work out the FQDN of this host ....
sub fqdn {
	$pqdn = "";

	# Try things which *MIGHT* return a FQDN. Save the PQDN if any.
	if (open(CMD, "hostname|")) {
		$_ = <CMD>;
		$_ .= $end unless ($end = chop) eq "\n";
		close(CMD);
		print STDERR "[hostname gave $_]\n" if $verbose;
		return $_ if /.\../;
		$pqdn = $_ if $_ ne "";
	}
	if (open(CMD, "uname -n|")) {
		$_ = <CMD>;
		$_ .= $end unless ($end = chop) eq "\n";
		close(CMD);
		print STDERR "[uname -n gave $_]\n" if $verbose;
		return $_ if /.\../;
		$pqdn = $_ if $_ ne "";
	}

	# Try to find the domain name
	if ($pqdn) {
		($name, @addrs) = gethostbyname($pqdn);
		if (defined($name)) {
			print STDERR "[gethostbyname gave $name]\n" if $verbose;
			return $name if $name =~ /.\../;
		}
	}
	$pqdn .= "." if $pqdn;
	if (open(CMD, "domainname|")) {
		$_ = <CMD>;
		$_ .= $end unless ($end = chop) eq "\n";
		close(CMD);
		print STDERR "[domainname gave $_]\n" if $verbose;
		return "$pqdn$_" if /.\../;
	}
	if (open(CMD, "/etc/resolv.conf")) {
		while(<CMD>) {
			@F = split;
			if (@F[$[] =~ /^domain$/) {
				print STDERR "[resolv.conf gave $F[$[+1]]\n" if $verbose;
				close(CMD);
				return "$pqdn$F[$[+1]";
			}
		}
		close(CMD);
	}
	return "UnKnown";
}

sub read_server_info {
	$ENV{'BRNSERV'} = "KEY";
	open(CONF, "$brclient -dTNACK|") || return;
	undef($ENV{'BRNSERV'});
	&read_server_info_sub;
	close(CONF);
}
sub read_server_info_sub {
	$_ = <CONF> || return; chop; $infoinfo = "$_$crlf" unless $infoinfo;
print STDERR "I:<<$infoinfo>>: $_.\n" if $opt_v;
	$_ = <CONF> || return; chop; $keyserv = $_ unless $keyserv;
print STDERR "K:$keyserv: $_.\n" if $opt_v;
	$_ = <CONF> || return; chop; $ackserv = $_ unless $ackserv;
print STDERR "A:$ackserv: $_.\n" if $opt_v;
	return if $workinfo;
	$workinfo = '';
	while ($_ = <CONF>) {
		chop;
		$workinfo .= "$_$crlf";
	}
	
}

sub setup_strings {
	$info_msg ="		[$0 $ver]

This code is meant to be run by someone who kind of knows what (s)he is doing.
It is meant to be MANAGED. If all goes well, it is just plug+play.
However, to reduce the server load, the code does early binding on
peoject, project data, master servers, etc.
This means that if something is changed on the central server, you may have
to intervene.
It also LOGS what it does, so that if things go wrong, you can poke around to
see which segments actually were scanned, and which were not.
You can look at the stat available from http://www.brute.cl.cam.ac.uk/brute/
to find the ACKs which haven't (yet) made it through to the central stats files,
and then re-ACK them manually.

This code does not use a lot of resources, and as it allocates quite large
chunks of key space, it's best to run it on a reliable server, so that it is
less like likely to crash, and the that log files are persistant.
If it does crash, restart it, giving it the unused part of the key space
which can be calculated by looking in \$ackfile (e.g. $ackfile).

It can take 'static' configuration info (such as \$ENV{'BRNAME'}, \$ENV{'BRID'}
\$homedir, \$logfile, \$ackfile, \$brclient_err, \$brclient, \$brslaved from
$brslavec and dynamic data (e.g.  \$reallocsize \$first_key \$last_key
\$currproj \$exit_on_done \$sleep_on_done $initial_timeout) from
$brslaved.
grep for '#T' in $0 to see tailorable variables.

If used on a network which is not (always) internet connnected,
set \$ack_to_server to 0, and it will store the ACKs in $packfile.
To process this, move the old file out the way, and then send off the results
(e.g.	cd $homedir; mv $packfile $packfile-old; touch $packfile;
	$brclient -A < $packfile
)
The next step is to get some segments (using brclient, the WWW interface, or
whatever) and pass them in. Note that it should only be used when the pending
segments (as held in $pkeyfile) have been exhaused, called something like:
	$0 -f 1234 -N 32

" unless defined($info_msg);

	$help_msg ="		[$0 $ver]

-c:	extra config data (perl)
-d	debug ...
-E	exit when initial range has been processed
-f:	first key (in hex) to allocate in range (see -l or -N)
-h	give this help message
-l:	last key (in hex) to allocate in range (see -f)
-i	give some information about this system
-n	do not try to map calling host addresses to names
-N:	number of keys (in dec) to allocate (inclusive) (see -f)
-r	'reuse' the listening port
-S:	tell clients to SLEEP when initial range has been processed
-v	go verbose -- see what's going on ...
" unless defined($help_msg);
}
