File:  [LON-CAPA] / loncom / LondConnection.pm
Revision 1.29: download - view: text, annotated - select for diffs
Tue Mar 2 22:38:07 2004 UTC (20 years, 1 month ago) by albertel
Branches: MAIN
CVS tags: HEAD
- making the regexp effieceint

#   This module defines and implements a class that represents
#   a connection to a lond daemon.
#
# $Id: LondConnection.pm,v 1.29 2004/03/02 22:38:07 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package LondConnection;

use strict;
use IO::Socket;
use IO::Socket::INET;
use IO::Handle;
use IO::File;
use Fcntl;
use POSIX;
use Crypt::IDEA;





my $DebugLevel=0;
my %hostshash;
my %perlvar;

#
#  Set debugging level
#
sub SetDebug {
    $DebugLevel = shift;
}

#
#   The config read is done in this way to support the read of
#   the non-default configuration file in the
#   event we are being used outside of loncapa.
#

my $ConfigRead = 0;

#   Read the configuration file for apache to get the perl
#   variable set.

sub ReadConfig {
    my $perlvarref = read_conf('loncapa.conf');
    %perlvar    = %{$perlvarref};
    my $hoststab   = read_hosts(
				"$perlvar{lonTabDir}/hosts.tab") || 
				die "Can't read host table!!";
    %hostshash  = %{$hoststab};
    $ConfigRead = 1;
    
}

#
#  Read a foreign configuration.
#  This sub is intended for the cases where the package
#  will be read from outside the LonCAPA environment, in that case
#  the client will need to explicitly provide:
#   - A file in hosts.tab format.
#   - Some idea of the 'lonCAPA' name of the local host (for building
#     the encryption key).
#
#  Parameters:
#      MyHost   - Name of this host as far as LonCAPA is concerned.
#      Filename - Name of a hosts.tab formatted file that will be used
#                 to build up the hosts table.
#
sub ReadForeignConfig {
    my $MyHost   = shift;
    my $Filename = shift;

    &Debug(4, "ReadForeignConfig $MyHost $Filename\n");

    $perlvar{lonHostID} = $MyHost; # Rmember my host.
    my $hosttab = read_hosts($Filename) ||
	die "Can't read hosts table!!";
    %hostshash = %{$hosttab};
    if($DebugLevel > 3) {
	foreach my $host (keys %hostshash) {
	    print "host $host => $hostshash{$host}\n";
	}
    }
    $ConfigRead = 1;

}

sub Debug {
    my $level   = shift;
    my $message = shift;
    if ($level < $DebugLevel) {
	print($message."\n");
    }
}

=pod

=head2 Dump

Dump the internal state of the object: For debugging purposes, to stderr.

=cut

sub Dump {
    my $self   = shift;
    my $key;
    my $value;
    print STDERR "Dumping LondConnectionObject:\n";
    while(($key, $value) = each %$self) {
	print STDERR "$key -> $value\n";
    }
    print STDERR "-------------------------------\n";
}

=pod

Local function to do a state transition.  If the state transition
callback is defined it is called with two parameters: the self and the
old state.

=cut

sub Transition {
    my $self     = shift;
    my $newstate = shift;
    my $oldstate = $self->{State};
    $self->{State} = $newstate;
    $self->{TimeoutRemaining} = $self->{TimeoutValue};
    if($self->{TransitionCallback}) {
	($self->{TransitionCallback})->($self, $oldstate); 
    }
}



=pod

=head2 new

Construct a new lond connection.

Parameters (besides the class name) include:

=item hostname

host the remote lond is on. This host is a host in the hosts.tab file

=item port

 port number the remote lond is listening on.

=cut

sub new {
    my $class    = shift;	# class name.
    my $Hostname = shift;	# Name of host to connect to.
    my $Port     = shift;	# Port to connect 

    if (!$ConfigRead) {
	ReadConfig();
	$ConfigRead = 1;
    }
    &Debug(4,$class."::new( ".$Hostname.",".$Port.")\n");

    # The host must map to an entry in the hosts table:
    #  We connect to the dns host that corresponds to that
    #  system and use the hostname for the encryption key 
    #  negotion.  In the objec these become the Host and
    #  LoncapaHim fields of the object respectively.
    #
    if (!exists $hostshash{$Hostname}) {
	&Debug(8, "No Such host $Hostname");
	return undef;		# No such host!!!
    }
    my @ConfigLine = @{$hostshash{$Hostname}};
    my $DnsName    = $ConfigLine[3]; # 4'th item is dns of host.
    Debug(5, "Connecting to ".$DnsName);
    # Now create the object...
    my $self     = { Host               => $DnsName,
                     LoncapaHim         => $Hostname,
                     Port               => $Port,
                     State              => "Initialized",
                     TransactionRequest => "",
                     TransactionReply   => "",
                     InformReadable     => 0,
                     InformWritable     => 0,
                     TimeoutCallback    => undef,
                     TransitionCallback => undef,
                     Timeoutable        => 0,
                     TimeoutValue       => 30,
                     TimeoutRemaining   => 0,
                     CipherKey          => "",
                     LondVersion        => "Unknown",
                     Cipher             => undef};
    bless($self, $class);
    unless ($self->{Socket} = IO::Socket::INET->new(PeerHost => $self->{Host},
					       PeerPort => $self->{Port},
					       Type     => SOCK_STREAM,
					       Proto    => "tcp",
					       Timeout  => 3)) {
	return undef;		# Inidicates the socket could not be made.
    }
    #
    # We're connected.  Set the state, and the events we'll accept:
    #
    $self->Transition("Connected");
    $self->{InformWritable}     = 1;    # When  socket is writable we send init
    $self->{Timeoutable}        = 1;    # Timeout allowed during startup negotiation. 
    $self->{TransactionRequest} = "init\n";
    
    #
    # Set socket to nonblocking I/O.
    #
    my $socket = $self->{Socket};
    my $flags    = fcntl($socket->fileno, F_GETFL,0);
    if($flags == -1) {
	$socket->close;
	return undef;
    }
    if(fcntl($socket, F_SETFL, $flags | O_NONBLOCK) == -1) {
	$socket->close;
	return undef;
    }

    # return the object :

    return $self;
}

=pod

=head2 Readable

This member should be called when the Socket becomes readable.  Until
the read completes, action is state independet. Data are accepted into
the TransactionReply until a newline character is received.  At that
time actionis state dependent:

=item Connected

in this case we received challenge, the state changes to
ChallengeReceived, and we initiate a send with the challenge response.

=item ReceivingReply

In this case a reply has been received for a transaction, the state
goes to Idle and we disable write and read notification.

=item ChallengeReeived

we just got what should be an ok\n and the connection can now handle
transactions.

=cut

sub Readable {
    my $self    = shift;
    my $socket  = $self->{Socket};
    my $data    = '';
    my $rv;
    if ($socket) {
	eval {
	    $rv = $socket->recv($data, POSIX::BUFSIZ, 0);
	}
    } else {
	$self->Transition("Disconnected");
	return -1;
    }
    my $errno   = $! + 0;	             # Force numeric context.

    unless (defined($rv) && length $data) {# Read failed,
	if(($errno == POSIX::EWOULDBLOCK)   ||
	   ($errno == POSIX::EAGAIN)        ||
	   ($errno == POSIX::EINTR)) {
	    return 0;
	}

	# Connection likely lost.
	&Debug(4, "Connection lost");
	$self->{TransactionRequest} = '';
	$socket->close();
	$self->Transition("Disconnected");
	return -1;
    }
    #  Append the data to the buffer.  And figure out if the read is done:

    &Debug(9,"Received from host: ".$data);
    $self->{TransactionReply} .= $data;
    if($self->{TransactionReply} =~ m/\n$/) {
	&Debug(8,"Readable End of line detected");
	if ($self->{State}  eq "Initialized") { # We received the challenge:
	    if($self->{TransactionReply} eq "refused\n") {	# Remote doesn't have
		
		$self->Transition("Disconnected"); # in host tables.
		$socket->close();
		return -1;
	    }

	    &Debug(8," Transition out of Initialized");
	    $self->{TransactionRequest} = $self->{TransactionReply};
	    $self->{InformWritable}     = 1;
	    $self->{InformReadable}     = 0;
	    $self->Transition("ChallengeReceived");
	    $self->{TimeoutRemaining}   = $self->{TimeoutValue};
	    return 0;
	}  elsif ($self->{State} eq "ChallengeReplied") {
	    if($self->{TransactionReply} ne "ok\n") {
		$self->Transition("Disconnected");
		$socket->close();
		return -1;
	    }
	    $self->Transition("RequestingVersion");
	    $self->{InformReadable}   = 0;
	    $self->{InformWritable}   = 1;
	    $self->{TransactionRequest} = "version\n";
	    return 0;
	} elsif ($self->{State} eq "ReadingVersionString") {
	    $self->{LondVersion}       = chomp($self->{TransactionReply});
	    $self->Transition("SetHost");
	    $self->{InformReadable}    = 0;
	    $self->{InformWritable}    = 1;
	    my $peer = $self->{LoncapaHim};
	    $self->{TransactionRequest}= "sethost:$peer\n";
	    return 0;
	} elsif ($self->{State} eq "HostSet") { # should be ok.
	    if($self->{TransactionReply} ne "ok\n") {
		$self->Transition("Disconnected");
		$socket->close();
		return -1;
	    }
	    $self->Transition("RequestingKey");
	    $self->{InformReadable}  = 0;
	    $self->{InformWritable}  = 1;
	    $self->{TransactionRequest} = "ekey\n";
	    return 0;
	} elsif ($self->{State}  eq "ReceivingKey") {
	    my $buildkey = $self->{TransactionReply};
	    my $key = $self->{LoncapaHim}.$perlvar{'lonHostID'};
	    $key=~tr/a-z/A-Z/;
	    $key=~tr/G-P/0-9/;
	    $key=~tr/Q-Z/0-9/;
	    $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
	    $key=substr($key,0,32);
	    my $cipherkey=pack("H32",$key);
	    $self->{Cipher} = new IDEA $cipherkey;
	    if($self->{Cipher} eq undef) {
		$self->Transition("Disconnected");
		$socket->close();
		return -1;
	    } else {
		$self->Transition("Idle");
		$self->{InformWritable}  =  0;
		$self->{InformReadable}  =  0;
		$self->{Timeoutable}     = 0;
		return 0;
	    }
	} elsif ($self->{State}  eq "ReceivingReply") {

	    # If the data are encrypted, decrypt first.

	    my $answer = $self->{TransactionReply};
	    if($answer =~ /^enc\:/) {
		$answer = $self->Decrypt($answer);
		$self->{TransactionReply} = $answer;
	    }

	    # finish the transaction

	    $self->{InformWritable}     = 0;
	    $self->{InformReadable}     = 0;
	    $self->{Timeoutable}        = 0;
	    $self->Transition("Idle");
	    return 0;
	} elsif ($self->{State} eq "Disconnected") { # No connection.
	    return -1;
	} else {			# Internal error: Invalid state.
	    $self->Transition("Disconnected");
	    $socket->close();
	    return -1;
	}
    }

    return 0;
    
}


=pod

This member should be called when the Socket becomes writable.

The action is state independent. An attempt is made to drain the
contents of the TransactionRequest member.  Once this is drained, we
mark the object as waiting for readability.

Returns  0 if successful, or -1 if not.

=cut
sub Writable {
    my $self     = shift;		# Get reference to the object.
    my $socket   = $self->{Socket};
    my $nwritten;
    if ($socket) {
	eval {
	    $nwritten = $socket->send($self->{TransactionRequest}, 0);
	}
    } else {
	# For whatever reason, there's no longer a socket left.


	$self->Transition("Disconnected");
	return -1;
    }
    my $errno    = $! + 0;
    unless (defined $nwritten) {
	if($errno != POSIX::EINTR) {
	    $self->Transition("Disconnected");
	    return -1;
	}
      
    }
    if (($nwritten >= 0)                        ||
        ($errno == POSIX::EWOULDBLOCK)    ||
	($errno == POSIX::EAGAIN)         ||
	($errno == POSIX::EINTR)          ||
	($errno ==  0)) {
	substr($self->{TransactionRequest}, 0, $nwritten) = ""; # rmv written part
      if(length $self->{TransactionRequest} == 0) {
         $self->{InformWritable} = 0;
         $self->{InformReadable} = 1;
         $self->{TransactionReply} = '';
         #
         # Figure out the next state:
         #
         if($self->{State} eq "Connected") {
            $self->Transition("Initialized");
         } elsif($self->{State} eq "ChallengeReceived") {
            $self->Transition("ChallengeReplied");
         } elsif($self->{State} eq "RequestingVersion") {
            $self->Transition("ReadingVersionString");
         } elsif ($self->{State} eq "SetHost") {
            $self->Transition("HostSet");
         } elsif($self->{State} eq "RequestingKey") {
            $self->Transition("ReceivingKey");
#            $self->{InformWritable} = 0;
#            $self->{InformReadable} = 1;
#            $self->{TransactionReply} = '';
         } elsif ($self->{State} eq "SendingRequest") {
            $self->Transition("ReceivingReply");
            $self->{TimeoutRemaining} = $self->{TimeoutValue};
         } elsif ($self->{State} eq "Disconnected") {
            return -1;
         }
         return 0;
      }
   } else {			# The write failed (e.g. partner disconnected).
      $self->Transition("Disconnected");
      $socket->close();
      return -1;
   }
	
}
=pod

=head2 Tick

   Tick is called every time unit by the event framework.  It

=item 1 decrements the remaining timeout.

=item 2 If the timeout is zero, calls TimedOut indicating that the current operation timed out.

=cut
    
sub Tick {
    my $self = shift;
    $self->{TimeoutRemaining}--;
    if ($self->{TimeoutRemaining} < 0) {
	$self->TimedOut();
    }
}

=pod

=head2 TimedOut

called on a timeout.  If the timeout callback is defined, it is called
with $self as its parameters.

=cut

sub TimedOut  {

    my $self = shift;
    if($self->{TimeoutCallback}) {
	my $callback = $self->{TimeoutCallback};
	my @args = ( $self);
	&$callback(@args);
    }
}

=pod

=head2 InitiateTransaction

Called to initiate a transaction.  A transaction can only be initiated
when the object is idle... otherwise an error is returned.  A
transaction consists of a request to the server that will have a
reply.  This member sets the request data in the TransactionRequest
member, makes the state SendingRequest and sets the data to allow a
timout, and to request writability notification.

=cut

sub InitiateTransaction {
    my $self   = shift;
    my $data   = shift;

    Debug(1, "initiating transaction: ".$data);
    if($self->{State} ne "Idle") {
	Debug(0," .. but not idle here\n");
	return -1;		# Error indicator.
    }
    # if the transaction is to be encrypted encrypt the data:

    if($data =~ /^encrypt\:/) {
	$data = $self->Encrypt($data);
    }

    # Setup the trasaction

    $self->{TransactionRequest} = $data;
    $self->{TransactionReply}   = "";
    $self->{InformWritable}     = 1;
    $self->{InformReadable}     = 0;
    $self->{Timeoutable}        = 1;
    $self->{TimeoutRemaining}   = $self->{TimeoutValue};
    $self->Transition("SendingRequest");
}


=pod

=head2 SetStateTransitionCallback

Sets a callback for state transitions.  Returns a reference to any
prior established callback, or undef if there was none:

=cut

sub SetStateTransitionCallback {
    my $self        = shift;
    my $oldCallback = $self->{TransitionCallback};
    $self->{TransitionCallback} = shift;
    return $oldCallback;
}

=pod

=head2 SetTimeoutCallback

Sets the timeout callback.  Returns a reference to any prior
established callback or undef if there was none.

=cut

sub SetTimeoutCallback {
    my $self                 = shift;
    my $callback             = shift;
    my $oldCallback          = $self->{TimeoutCallback};
    $self->{TimeoutCallback} = $callback;
    return $oldCallback;
}

=pod

=head2 Shutdown:

Shuts down the socket.

=cut

sub Shutdown {
    my $self = shift;
    my $socket = $self->GetSocket();
    Debug(5,"socket is -$socket-");
    if ($socket) {
	# Ask lond to exit too.  Non blocking so
	# there is no cost for failure.
	eval {
	    $socket->send("exit\n", 0);
	    $socket->shutdown(2);
	}
    }
}

=pod

=head2 GetState

selector for the object state.

=cut

sub GetState {
    my $self = shift;
    return $self->{State};
}

=pod

=head2 GetSocket

selector for the object socket.

=cut

sub GetSocket {
    my $self  = shift;
    return $self->{Socket};
}


=pod

=head2 WantReadable

Return the state of the flag that indicates the object wants to be
called when readable.

=cut

sub WantReadable {
    my   $self = shift;

    return $self->{InformReadable};
}

=pod

=head2 WantWritable

Return the state of the flag that indicates the object wants write
notification.

=cut

sub WantWritable {
    my $self = shift;
    return $self->{InformWritable};
}

=pod

=head2 WantTimeout

return the state of the flag that indicates the object wants to be
informed of timeouts.

=cut

sub WantTimeout {
    my $self = shift;
    return $self->{Timeoutable};
}

=pod

=head2 GetReply

Returns the reply from the last transaction.

=cut

sub GetReply {
    my $self = shift;
    return $self->{TransactionReply};
}

=pod

=head2 Encrypt

Returns the encrypted version of the command string.

The command input string is of the form:

  encrypt:command

The output string can be directly sent to lond as it is of the form:

  enc:length:<encodedrequest>

=cut

sub Encrypt {
    my $self    = shift;		# Reference to the object.
    my $request = shift;	        # Text to send.

   
    # Split the encrypt: off the request and figure out it's length.
    # the cipher works in blocks of 8 bytes.

    my $cmd = $request;
    $cmd    =~ s/^encrypt\://;	# strip off encrypt:
    chomp($cmd);		# strip off trailing \n
    my     $length=length($cmd);	# Get the string length.
    $cmd .= "         ";	# Pad with blanks so we can fill out a block.

    # encrypt the request in 8 byte chunks to create the encrypted
    # output request.

    my $Encoded = '';
    for(my $index = 0; $index <= $length; $index += 8) {
	$Encoded .= 
	    unpack("H16", 
		   $self->{Cipher}->encrypt(substr($cmd, 
						   $index, 8)));
    }

    # Build up the answer as enc:length:$encrequest.

    $request = "enc:$length:$Encoded\n";
    return $request;
    
    
}

=pod

=head2 Decrypt

Decrypt a response from the server.  The response is in the form:

 enc:<length>:<encrypted data>

=cut

sub Decrypt {
    my $self      = shift;	# Recover reference to object
    my $encrypted = shift;	# This is the encrypted data.

    #  Bust up the response into length, and encryptedstring:

    my ($enc, $length, $EncryptedString) = split(/:/,$encrypted);
    chomp($EncryptedString);

    # Decode the data in 8 byte blocks.  The string is encoded
    # as hex digits so there are two characters per byte:

    my $decrypted = "";
    for(my $index = 0; $index < length($EncryptedString);
	$index += 16) {
	$decrypted .= $self->{Cipher}->decrypt(
				    pack("H16",
					 substr($EncryptedString,
						$index, 
						16)));
    }
    #  the answer may have trailing pads to fill out a block.
    #  $length tells us the actual length of the decrypted string:

    $decrypted = substr($decrypted, 0, $length);

    return $decrypted;

}

=pod

=head2 GetHostIterator

Returns a hash iterator to the host information.  Each get from 
this iterator returns a reference to an array that contains 
information read from the hosts configuration file.  Array elements
are used as follows:

 [0]   - LonCapa host name.
 [1]   - LonCapa domain name.
 [2]   - Loncapa role (e.g. library or access).
 [3]   - DNS name server hostname.
 [4]   - IP address (result of e.g. nslookup [3]).
 [5]   - Maximum connection count.
 [6]   - Idle timeout for reducing connection count.
 [7]   - Minimum connection count.

=cut

sub GetHostIterator {

    return HashIterator->new(\%hostshash);    
}

###########################################################
#
#  The following is an unashamed kludge that is here to
# allow LondConnection to be used outside of the
# loncapa environment (e.g. by lonManage).
# 
#   This is a textual inclusion of pieces of the
#   Configuration.pm module.
#


my $confdir='/etc/httpd/conf/';

# ------------------- Subroutine read_conf: read LON-CAPA server configuration.
# This subroutine reads PerlSetVar values out of specified web server
# configuration files.
sub read_conf
  {
    my (@conf_files)=@_;
    my %perlvar;
    foreach my $filename (@conf_files,'loncapa_apache.conf')
      {
	  if($DebugLevel > 3) {
	      print("Going to read $confdir.$filename\n");
	  }
	open(CONFIG,'<'.$confdir.$filename) or
	    die("Can't read $confdir$filename");
	while (my $configline=<CONFIG>)
	  {
	    if ($configline =~ /^[^\#]*PerlSetVar/)
	      {
		my ($unused,$varname,$varvalue)=split(/\s+/,$configline);
		chomp($varvalue);
		$perlvar{$varname}=$varvalue;
	      }
	  }
	close(CONFIG);
      }
    if($DebugLevel > 3) {
	print "Dumping perlvar:\n";
	foreach my $var (keys %perlvar) {
	    print "$var = $perlvar{$var}\n";
	}
    }
    my $perlvarref=\%perlvar;
    return $perlvarref;
}

#---------------------- Subroutine read_hosts: Read a LON-CAPA hosts.tab
# formatted configuration file.
#
my $RequiredCount = 5;		# Required item count in hosts.tab.
my $DefaultMaxCon = 5;		# Default value for maximum connections.
my $DefaultIdle   = 1000;       # Default connection idle time in seconds.
my $DefaultMinCon = 0;          # Default value for minimum connections.

sub read_hosts {
    my $Filename = shift;
    my %HostsTab;
    
   open(CONFIG,'<'.$Filename) or die("Can't read $Filename");
    while (my $line = <CONFIG>) {
	if (!($line =~ /^\s*\#/)) {
	    my @items = split(/:/, $line);
	    if(scalar @items >= $RequiredCount) {
		if (scalar @items == $RequiredCount) { # Only required items:
		    $items[$RequiredCount] = $DefaultMaxCon;
		}
		if(scalar @items == $RequiredCount + 1) { # up through maxcon.
		    $items[$RequiredCount+1] = $DefaultIdle;
		}
		if(scalar @items == $RequiredCount + 2) { # up through idle.
		    $items[$RequiredCount+2] = $DefaultMinCon;
		}
		{
		    my @list = @items; # probably not needed but I'm unsure of 
		    # about the scope of item so...
		    $HostsTab{$list[0]} = \@list; 
		}
	    }
	}
    }
    close(CONFIG);
    my $hostref = \%HostsTab;
    return ($hostref);
}
#
#   Get the version of our peer.  Note that this is only well
#   defined if the state machine has hit the idle state at least
#   once (well actually if it has transitioned out of 
#   ReadingVersionString   The member data LondVersion is returned.
#
sub PeerVersion {
   my $self = shift;
   
   return $self->{LondVersion};
}

1;

=pod

=head1 Theory

The lond object is a state machine.  It lives through the following states:

=item Connected:

a TCP connection has been formed, but the passkey has not yet been
negotiated.

=item Initialized:

"init" sent.

=item ChallengeReceived:

lond sent its challenge to us.

=item ChallengeReplied:

We replied to lond's challenge waiting for lond's ok.

=item RequestingKey:

We are requesting an encryption key.

=item ReceivingKey:

We are receiving an encryption key.

=item Idle:

Connection was negotiated but no requests are active.

=item SendingRequest:

A request is being sent to the peer.

=item ReceivingReply:

Waiting for an entire reply from the peer.

=item Disconnected:

For whatever reason, the connection was dropped.

When we need to be writing data, we have a writable event. When we
need to be reading data, a readable event established.  Events
dispatch through the class functions Readable and Writable, and the
watcher contains a reference to the associated object to allow object
context to be reached.

=head2 Member data.

=item Host

Host socket is connected to.

=item Port

The port the remote lond is listening on.

=item Socket

Socket open on the connection.

=item State

The current state.

=item TransactionRequest

The request being transmitted.

=item TransactionReply

The reply being received from the transaction.

=item InformReadable

True if we want to be called when socket is readable.

=item InformWritable

True if we want to be informed if the socket is writable.

=item Timeoutable

True if the current operation is allowed to timeout.

=item TimeoutValue

Number of seconds in the timeout.

=item TimeoutRemaining

Number of seconds left in the timeout.

=item CipherKey

The key that was negotiated with the peer.

=item Cipher

The cipher obtained via the key.


=head2 The following are callback like members:

=item Tick:

Called in response to a timer tick. Used to managed timeouts etc.

=item Readable:

Called when the socket becomes readable.

=item Writable:

Called when the socket becomes writable.

=item TimedOut:

Called when a timed operation timed out.


=head2 The following are operational member functions.

=item InitiateTransaction:

Called to initiate a new transaction

=item SetStateTransitionCallback:

Called to establish a function that is called whenever the object goes
through a state transition.  This is used by The client to manage the
work flow for the object.

=item SetTimeoutCallback:

Set a function to be called when a transaction times out.  The
function will be called with the object as its sole parameter.

=item Encrypt:

Encrypts a block of text according to the cipher negotiated with the
peer (assumes the text is a command).

=item Decrypt:

Decrypts a block of text according to the cipher negotiated with the
peer (assumes the block was a reply.

=item Shutdown:

Shuts off the socket.

=head2 The following are selector member functions:

=item GetState:

Returns the current state

=item GetSocket:

Gets the socekt open on the connection to lond.

=item WantReadable:

true if the current state requires a readable event.

=item WantWritable:

true if the current state requires a writable event.

=item WantTimeout:

true if the current state requires timeout support.

=item GetHostIterator:

Returns an iterator into the host file hash.

=cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>