Annotation of loncom/LondConnection.pm, revision 1.1

1.1     ! foxr        1: #
        !             2: #   This module defines and implements a class that represents
        !             3: #   a connection to a lond daemon. 
        !             4: package LondConnection;
        !             5: 
        !             6: use IO::Socket;
        !             7: use IO::Socket::INET;
        !             8: use IO::Handle;
        !             9: use IO::File;
        !            10: use Fcntl;
        !            11: use POSIX;
        !            12: use Crypt::IDEA;
        !            13: use LONCAPA::Configuration;
        !            14: use LONCAPA::HashIterator;
        !            15: 
        !            16: my $DebugLevel=4;
        !            17: 
        !            18: #   Read the configuration file for apache to get the perl
        !            19: #   variable set.
        !            20: 
        !            21: my $perlvarref = LONCAPA::Configuration::read_conf('loncapa.conf');
        !            22: my %perlvar    = %{$perlvarref};
        !            23: my $hoststab   = 
        !            24:     LONCAPA::Configuration::read_hosts(
        !            25:             "$perlvar{'lonTabDir'}/hosts.tab") || 
        !            26:     die "Can't read host table!!";
        !            27: my %hostshash  = %{$hoststab};
        !            28: 
        !            29: close(CONFIG);
        !            30: 
        !            31: sub Debug {
        !            32:     my $level   = shift;
        !            33:     my $message = shift;
        !            34:     if ($level < $DebugLevel) {
        !            35: 	print($message."\n");
        !            36:     }
        !            37: }
        !            38: =pod 
        !            39:    Dump the internal state of the object: For debugging purposes.
        !            40: =cut
        !            41: 
        !            42: sub Dump {
        !            43:     my $self   = shift;
        !            44:     print "Dumping LondConnectionObject:\n";
        !            45:     while(($key, $value) = each %$self) {
        !            46: 	print "$key -> $value\n";
        !            47:     }
        !            48:     print "-------------------------------\n";
        !            49: }
        !            50: 
        !            51: =pod
        !            52:   Local function to do a state transition.  If the state transition callback
        !            53:     is defined it is called with two parameters:  the self and the old state.
        !            54: =cut
        !            55: sub Transition {
        !            56:     my $self     = shift;
        !            57:     my $newstate = shift;
        !            58:     my $oldstate = $self->{State};
        !            59:     $self->{State} = $newstate;
        !            60:     $self->{TimeoutRemaining} = $self->{TimeoutValue};
        !            61:     if($self->{TransitionCallback}) {
        !            62: 	($self->{TransitionCallback})->($self, $oldstate); 
        !            63:     }
        !            64: }
        !            65: 
        !            66: =pod
        !            67:   Construct a new lond connection.
        !            68:   Parameters (besides the class name) include:
        !            69: =item hostname - host the remote lond is on. 
        !            70:     This host is a host in the hosts.tab file
        !            71: =item port     - port number the remote lond is listening on.
        !            72: =cut
        !            73: sub new {
        !            74:     my $class    = shift;	# class name.
        !            75:     my $Hostname = shift;	# Name of host to connect to.
        !            76:     my $Port     = shift;	# Port to connect 
        !            77:     &Debug(4,$class."::new( ".$Hostname.",".$Port.")\n");
        !            78: 
        !            79:     # The host must map to an entry in the hosts table:
        !            80:     #  We connect to the dns host that corresponds to that
        !            81:     #  system and use the hostname for the encryption key 
        !            82:     #  negotion.  In the objec these become the Host and
        !            83:     #  LoncapaHim fields of the object respectively.
        !            84:     #
        !            85:     if (!exists $hostshash{$Hostname}) {
        !            86: 	return undef;		# No such host!!!
        !            87:     }
        !            88:     my @ConfigLine = @{$hostshash{$Hostname}};
        !            89:     my $DnsName    = $ConfigLine[3]; # 4'th item is dns of host.
        !            90:     Debug(5, "Connecting to ".$DnsName);
        !            91:     # Now create the object...
        !            92:     my $self     = { Host               => $DnsName,
        !            93: 		     LoncapaHim         => $Hostname,
        !            94: 	             Port               => $Port,
        !            95: 	             State              => "Initialized",
        !            96: 	             TransactionRequest => "",
        !            97: 	             TransactionReply   => "",
        !            98: 	             InformReadable     => 0,
        !            99: 	             InformWritable     => 0,
        !           100: 		     TimeoutCallback    => undef,
        !           101: 		     TransitionCallback => undef,
        !           102: 	             Timeoutable        => 0,
        !           103: 	             TimeoutValue       => 60,
        !           104:              TimeoutRemaining   => 0,
        !           105: 		     CipherKey          => "",
        !           106: 		     Cipher             => undef};
        !           107:     bless($self, $class);
        !           108:     unless ($self->{Socket} = IO::Socket::INET->new(PeerHost => $self->{Host},
        !           109: 					       PeerPort => $self->{Port},
        !           110: 					       Type     => SOCK_STREAM,
        !           111: 					       Proto    => "tcp")) {
        !           112: 	return undef;		# Inidicates the socket could not be made.
        !           113:     }
        !           114:     #
        !           115:     # We're connected.  Set the state, and the events we'll accept:
        !           116:     #
        !           117:     $self->Transition("Connected");
        !           118:     $self->{InformWritable}     = 1;    # When  socket is writable we send init
        !           119:     $self->{TransactionRequest} = "init\n";
        !           120:     
        !           121:     #
        !           122:     # Set socket to nonblocking I/O.
        !           123:     #
        !           124:     my $socket = $self->{Socket};
        !           125:     $flags    = fcntl($socket->fileno, F_GETFL,0);
        !           126:     if($flags == -1) {
        !           127: 	$socket->close;
        !           128: 	return undef;
        !           129:     }
        !           130:     if(fcntl($socket, F_SETFL, $flags | O_NONBLOCK) == -1) {
        !           131: 	$socket->close;
        !           132: 	return undef;
        !           133:     }
        !           134: 
        !           135:     # return the object :
        !           136: 
        !           137:     return $self;
        !           138: }
        !           139: =pod
        !           140:    This member should be called when the Socket becomes readable.
        !           141:    Until the read completes,  action is state independet. Data are accepted
        !           142:     into the TransactionReply until a newline character is received.  At that
        !           143:    time actionis state dependent:
        !           144: =item Connected: in this case we received challenge, the state changes
        !           145:     to ChallengeReceived, and we initiate a send with the challenge response.
        !           146: =item ReceivingReply: In this case a reply has been received for a transaction,
        !           147:     the state goes to Idle and we disable write and read notification.
        !           148: =item ChallengeReeived: we just got what should be an ok\n and the
        !           149:     connection can now handle transactions.
        !           150: 
        !           151: =cut
        !           152: sub Readable {
        !           153:     my $self    = shift;
        !           154:     my $socket  = $self->{Socket};
        !           155:     my $data    = '';
        !           156:     my $rv      = $socket->recv($data, POSIX::BUFSIZ,  0);
        !           157:     my $errno   = $! + 0;	             # Force numeric context.
        !           158: 
        !           159:     unless (defined($rv) && length($data)) { # Read failed,
        !           160: 	if(($errno == POSIX::EWOULDBLOCK)   ||
        !           161: 	   ($errno == POSIX::EAGAIN)        ||
        !           162: 	   ($errno == POSIX::EINTR)         ||
        !           163: 	   ($errno == 0)) {
        !           164: 	    return 0;
        !           165: 	}
        !           166: 
        !           167: 	# Connection likely lost.
        !           168: 	&Debug(4, "Connection lost");
        !           169: 	$self->{TransactionRequest} = '';
        !           170: 	$socket->close();
        !           171: 	$self->Transition("Disconnected");
        !           172: 	return -1;
        !           173:     }
        !           174:     #  Append the data to the buffer.  And figure out if the read is done:
        !           175: 
        !           176:     &Debug(9,"Received from host: ".$data);
        !           177:     $self->{TransactionReply} .= $data;
        !           178:     if($self->{TransactionReply} =~ /(.*\n)/) {
        !           179: 	&Debug(8,"Readable End of line detected");
        !           180: 	if ($self->{State}  eq "Initialized") { # We received the challenge:
        !           181: 	    if($self->{TransactionReply} eq "refused") {	# Remote doesn't have
        !           182: 		
        !           183: 		$self->Transition("Disconnected"); # in host tables.
        !           184: 		$socket->close();
        !           185: 		return -1;
        !           186: 	    }
        !           187: 
        !           188: 	    &Debug(8," Transition out of Initialized");
        !           189: 	    $self->{TransactionRequest} = $self->{TransactionReply};
        !           190: 	    $self->{InformWritable}     = 1;
        !           191: 	    $self->{InformReadable}     = 0;
        !           192: 	    $self->Transition("ChallengeReceived");
        !           193: 	    $self->{TimeoutRemaining}   = $self->{TimeoutValue};
        !           194: 	    return 0;
        !           195: 	} elsif ($self->{State} eq "ChallengeReplied") { # should be ok.
        !           196: 	    if($self->{TransactionReply} != "ok\n") {
        !           197: 		$self->Transition("Disconnected");
        !           198: 		$socket->close();
        !           199: 		return -1;
        !           200: 	    }
        !           201: 	    $self->Transition("RequestingKey");
        !           202: 	    $self->{InformReadable}  = 0;
        !           203: 	    $self->{InformWritable}  = 1;
        !           204: 	    $self->{TransactionRequest} = "ekey\n";
        !           205: 	    return 0;
        !           206: 	} elsif ($self->{State}  eq "ReceivingKey") {
        !           207: 	    my $buildkey = $self->{TransactionReply};
        !           208: 	    my $key = $self->{LoncapaHim}.$perlvar{'lonHostID'};
        !           209: 	    $key=~tr/a-z/A-Z/;
        !           210: 	    $key=~tr/G-P/0-9/;
        !           211: 	    $key=~tr/Q-Z/0-9/;
        !           212: 	    $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
        !           213: 	    $key=substr($key,0,32);
        !           214: 	    my $cipherkey=pack("H32",$key);
        !           215: 	    $self->{Cipher} = new IDEA $cipherkey;
        !           216: 	    if($self->{Cipher} == undef) {
        !           217: 		$self->Transition("Disconnected");
        !           218: 		$socket->close();
        !           219: 		return -1;
        !           220: 	    } else {
        !           221: 		$self->Transition("Idle");
        !           222: 		$self->{InformWritable}  =  0;
        !           223: 		$self->{InformReadable}  =  0;
        !           224: 		$self->{Timeoutable}     = 0;
        !           225: 		return 0;
        !           226: 	    }
        !           227: 	} elsif ($self->{State}  eq "ReceivingReply") {
        !           228: 
        !           229: 	    # If the data are encrypted, decrypt first.
        !           230: 
        !           231: 	    my $answer = $self->{TransactionReply};
        !           232: 	    if($answer =~ /^enc\:/) {
        !           233: 		$answer = $self->Decrypt($answer);
        !           234: 		$self->{TransactionReply} = $answer;
        !           235: 	    }
        !           236: 
        !           237: 	    # finish the transaction
        !           238: 
        !           239: 	    $self->{InformWritable}     = 0;
        !           240: 	    $self->{InformReadable}     = 0;
        !           241: 	    $self->{Timeoutable}        = 0;
        !           242: 	    $self->Transition("Idle");
        !           243: 	    return 0;
        !           244: 	} elsif ($self->{State} eq "Disconnected") { # No connection.
        !           245: 	    return -1;
        !           246: 	} else {			# Internal error: Invalid state.
        !           247: 	    $self->Transition("Disconnected");
        !           248: 	    $socket->close();
        !           249: 	    return -1;
        !           250: 	}
        !           251:     }
        !           252: 
        !           253:     return 0;
        !           254:     
        !           255: }
        !           256: 
        !           257: 
        !           258: =pod
        !           259:   This member should be called when the Socket becomes writable.
        !           260: The action is state independent. An attempt is made to drain the contents of
        !           261: the TransactionRequest member.  Once this is drained, we mark the  object
        !           262: as waiting for readability.
        !           263: 
        !           264: Returns  0 if successful, or -1 if not.
        !           265:   
        !           266: =cut
        !           267: sub Writable {
        !           268:     my $self     = shift;		# Get reference to the object.
        !           269:     my $socket   = $self->{Socket};
        !           270:     my $nwritten = $socket->send($self->{TransactionRequest}, 0);
        !           271:     my $errno    = $! + 0;
        !           272:     unless (defined $nwritten) {
        !           273: 	if($errno != POSIX::EINTR) {
        !           274: 	    $self->Transition("Disconnected");
        !           275: 	    return -1;
        !           276: 	}
        !           277:       
        !           278:     }
        !           279:     if (($rv >= 0)                        ||
        !           280:         ($errno == POSIX::EWOULDBLOCK)    ||
        !           281: 	($errno == POSIX::EAGAIN)         ||
        !           282: 	($errno == POSIX::EINTR)          ||
        !           283: 	($errno ==  0)) {
        !           284: 	substr($self->{TransactionRequest}, 0, $nwritten) = ""; # rmv written part
        !           285: 	if(length $self->{TransactionRequest} == 0) {
        !           286: 	    $self->{InformWritable} = 0;
        !           287: 	    $self->{InformReadable} = 1;
        !           288: 	    $self->{TransactionReply} = '';
        !           289: 	    #
        !           290: 	    # Figure out the next state:
        !           291: 	    #
        !           292: 	    if($self->{State} eq "Connected") {
        !           293: 		$self->Transition("Initialized");
        !           294: 	    } elsif($self->{State} eq "ChallengeReceived") {
        !           295: 		$self->Transition("ChallengeReplied");
        !           296: 	    } elsif($self->{State} eq "RequestingKey") {
        !           297: 		$self->Transition("ReceivingKey");
        !           298: 		$self->{InformWritable} = 0;
        !           299: 		$self->{InformReadable} = 1;
        !           300: 		$self->{TransactionReply} = '';
        !           301: 	    } elsif ($self->{State} eq "SendingRequest") {
        !           302: 		$self->Transition("ReceivingReply");
        !           303: 		$self->{TimeoutRemaining} = $self->{TimeoutValue};
        !           304: 	    } elsif ($self->{State} eq "Disconnected") {
        !           305: 		return -1;
        !           306: 	    }
        !           307: 	    return 0;
        !           308: 	}
        !           309:     } else {			# The write failed (e.g. partner disconnected).
        !           310: 	$self->Transition("Disconnected");
        !           311: 	$socket->close();
        !           312: 	return -1;
        !           313:     }
        !           314: 	
        !           315: }
        !           316: =pod
        !           317:    Tick is called every time unit by the event framework.  It
        !           318:    1. decrements the remaining timeout.
        !           319:    2. If the timeout is zero, calls TimedOut indicating that the 
        !           320:       current operation timed out.
        !           321: 
        !           322: =cut
        !           323:     
        !           324: sub Tick {
        !           325:     my $self = shift;
        !           326:     $self->{TimeoutRemaining}--;
        !           327:     if ($self->{TimeoutRemaining} < 0) {
        !           328: 	$self->TimedOut();
        !           329:     }
        !           330: }
        !           331: =pod
        !           332:   TimedOut - called on a timeout.  If the timeout callback is defined,
        !           333:   it is called with $self as  its parameters.
        !           334: 
        !           335: =cut  
        !           336: sub TimedOut  {
        !           337: 
        !           338:     my $self = shift;
        !           339:     if($self->{TimeoutCallback}) {
        !           340: 	my $callback = $self->{TimeoutCallback};
        !           341: 	my @args = ( $self);
        !           342: 	&$callback(@args);
        !           343:     }
        !           344: }
        !           345: =pod
        !           346:     Called to initiate a transaction.  A transaction can only be initiated
        !           347:     when the object is idle... otherwise an error is returned.
        !           348:     A transaction consists of a request to the server that will have a reply.
        !           349:     This member sets the request data in the TransactionRequest member,
        !           350:     makes the state SendingRequest and sets the data to allow a timout,
        !           351:     and to request writability notification.  
        !           352: =cut
        !           353: sub InitiateTransaction {
        !           354:     my $self   = shift;
        !           355:     my $data   = shift;
        !           356: 
        !           357:     if($self->{State} ne "Idle") {
        !           358: 	return -1;		# Error indicator.
        !           359:     }
        !           360:     # if the transaction is to be encrypted encrypt the data:
        !           361: 
        !           362:     if($data =~ /^encrypt\:/) {
        !           363: 	$data = $self->Encrypt($data);
        !           364:     }
        !           365: 
        !           366:     # Setup the trasaction
        !           367: 
        !           368:     $self->{TransactionRequest} = $data;
        !           369:     $self->{TransactionReply}   = "";
        !           370:     $self->{InformWritable}     = 1;
        !           371:     $self->{InformReadable}     = 0;
        !           372:     $self->{Timeoutable}        = 1;
        !           373:     $self->{TimeoutRemaining}   = $self->{TimeoutValue};
        !           374:     $self->Transition("SendingRequest");
        !           375: }
        !           376: 
        !           377: 
        !           378: =pod
        !           379:     Sets a callback for state transitions.  Returns a reference to any
        !           380:     prior established callback, or undef if there was none:
        !           381: =cut
        !           382: sub SetStateTransitionCallback {
        !           383:     my $self        = shift;
        !           384:     my $oldCallback = $self->{TransitionCallback};
        !           385:     $self->{TransitionCallback} = shift;
        !           386:     return $oldCallback;
        !           387: }
        !           388: =pod
        !           389:    Sets the timeout callback.  Returns a reference to any prior established 
        !           390:    callback or undef if there was none.
        !           391: =cut
        !           392: sub SetTimeoutCallback {
        !           393:     my $self                 = shift;
        !           394:     my $callback             = shift;
        !           395:     my $oldCallback          = $self->{TimeoutCallback};
        !           396:     $self->{TimeoutCallback} = $callback;
        !           397:     return $oldCallback;
        !           398: }
        !           399: 
        !           400: =pod
        !           401:    GetState - selector for the object state.
        !           402: =cut
        !           403: sub GetState {
        !           404:     my $self = shift;
        !           405:     return $self->{State};
        !           406: }
        !           407: =pod
        !           408:    GetSocket - selector for the object socket.
        !           409: =cut
        !           410: sub GetSocket {
        !           411:     my $self  = shift;
        !           412:     return $self->{Socket};
        !           413: }
        !           414: =pod
        !           415:    Return the state of the flag that indicates the object wants to be
        !           416:     called when readable.
        !           417: =cut
        !           418: sub WantReadable {
        !           419:     my   $self = shift;
        !           420: 
        !           421:     return $self->{InformReadable};
        !           422: }
        !           423: =pod
        !           424:    Return the state of the flag that indicates the object wants write
        !           425:     notification.
        !           426: =cut
        !           427: sub WantWritable {
        !           428:     my $self = shift;
        !           429:     return $self->{InformWritable};
        !           430: }
        !           431: =pod
        !           432:   return the state of the flag that indicates the object wants to be informed
        !           433:    of timeouts.
        !           434: =cut
        !           435: sub WantTimeout {
        !           436:     my $self = shift;
        !           437:     return $self->{Timeoutable};
        !           438: }
        !           439: 
        !           440: =pod
        !           441:   Returns the reply from the last transaction.
        !           442: =cut
        !           443: sub GetReply {
        !           444:     my $self = shift;
        !           445:     return $self->{TransactionReply};
        !           446: }
        !           447: 
        !           448: =pod
        !           449:   Returns the encrypted version of the command string.
        !           450:   The command input string is of the form:
        !           451:   encrypt:command
        !           452:   The output string can be directly sent to lond as it's of the form:
        !           453:   enc:length:<encodedrequest>
        !           454: '
        !           455: =cut
        !           456: sub Encrypt {
        !           457:     my $self    = shift;		# Reference to the object.
        !           458:     my $request = shift;	        # Text to send.
        !           459: 
        !           460:    
        !           461:     # Split the encrypt: off the request and figure out it's length.
        !           462:     # the cipher works in blocks of 8 bytes.
        !           463: 
        !           464:     my $cmd = $request;
        !           465:     $cmd    =~ s/^encrypt\://;	# strip off encrypt:
        !           466:     chomp($cmd);		# strip off trailing \n
        !           467:     my     $length=length($cmd);	# Get the string length.
        !           468:     $cmd .= "         ";	# Pad with blanks so we can fill out a block.
        !           469: 
        !           470:     # encrypt the request in 8 byte chunks to create the encrypted
        !           471:     # output request.
        !           472: 
        !           473:     my $Encoded = '';
        !           474:     for(my $index = 0; $index <= $length; $index += 8) {
        !           475: 	$Encoded .= 
        !           476: 	    unpack("H16", 
        !           477: 		   $self->{Cipher}->encrypt(substr($cmd, 
        !           478: 						   $index, 8)));
        !           479:     }
        !           480: 
        !           481:     # Build up the answer as enc:length:$encrequest.
        !           482: 
        !           483:     $request = "enc:$length:$Encoded\n";
        !           484:     return $request;
        !           485:     
        !           486:     
        !           487: }
        !           488: =pod 
        !           489:     Decrypt
        !           490:     Decrypt a response from the server.  The response is in the form:
        !           491:   enc:<length>:<encrypted data>
        !           492: =cut
        !           493: sub Decrypt {
        !           494:     my $self      = shift;	# Recover reference to object
        !           495:     my $encrypted = shift;	# This is the encrypted data.
        !           496: 
        !           497:     #  Bust up the response into length, and encryptedstring:
        !           498: 
        !           499:     my ($enc, $length, $EncryptedString) = split(/:/,$encrypted);
        !           500:     chomp($EncryptedString);
        !           501: 
        !           502:     # Decode the data in 8 byte blocks.  The string is encoded
        !           503:     # as hex digits so there are two characters per byte:
        !           504: 
        !           505:     $decrpyted = "";
        !           506:     for(my $index = 0; $index < length($EncryptedString);
        !           507: 	$index += 16) {
        !           508: 	$decrypted .= $self->{Cipher}->decrypt(
        !           509: 				    pack("H16",
        !           510: 					 substr($EncryptedString,
        !           511: 						$index, 
        !           512: 						16)));
        !           513:     }
        !           514:     #  the answer may have trailing pads to fill out a block.
        !           515:     #  $length tells us the actual length of the decrypted string:
        !           516: 
        !           517:     $decrypted = substr($decrypted, 0, $length);
        !           518: 
        !           519:     return $decrypted;
        !           520: 
        !           521: }
        !           522: 
        !           523: =pod
        !           524: =head GetHostIterator
        !           525: 
        !           526: Returns a hash iterator to the host information.  Each get from 
        !           527: this iterator returns a reference to an array that contains 
        !           528: information read from the hosts configuration file.  Array elements
        !           529: are used as follows:
        !           530: 
        !           531: [0]   - LonCapa host name.
        !           532: [1]   - LonCapa domain name.
        !           533: [2]   - Loncapa role (e.g. library or access).
        !           534: [3]   - DNS name server hostname.
        !           535: [4]   - IP address (result of e.g. nslooup [3]).
        !           536: [5]   - Maximum connection count.
        !           537: [6]   - Idle timeout for reducing connection count.
        !           538: [7]   - Minimum connection count.
        !           539: 
        !           540: 
        !           541: =cut
        !           542: sub GetHostIterator {
        !           543: 
        !           544:     return HashIterator->new(\%hostshash);    
        !           545: }
        !           546: 
        !           547: 1;
        !           548: 
        !           549: =pod
        !           550: =head1 Theory
        !           551:    The lond object is a state machine.  It lives through the following states:
        !           552: 
        !           553: =item Connected: a TCP connection has been formed, but the passkey has not yet
        !           554:     been negotiated.
        !           555: =item Initialized: "init" sent.
        !           556: =item ChallengeReceived: lond sent its challenge to us.
        !           557: =item ChallengeReplied:  We replied to lond's challenge waiting for lond's ok.
        !           558: =item RequestingKey:    We are requesting an encryption key.
        !           559: =item ReceivingKey:     We are receiving an encryption key.
        !           560: =item Idle:  Connection was negotiated but no requests are active.
        !           561: =item SendingRequest: A request is being sent to the peer.
        !           562: =item ReceivingReply: Waiting for an entire reply from the peer.
        !           563: =item Disconnected:   For whatever reason, the connection was dropped.
        !           564: 
        !           565:   When we need to be writing data, we have a writable
        !           566: event. When we need to be reading data, a readable event established.
        !           567: Events dispatch through the class functions Readable and Writable, and the
        !           568: watcher contains a reference to the associated object to allow object context
        !           569: to be reached.
        !           570: 
        !           571: =head2 Member data.
        !           572: Host   - Host socket is connected to.
        !           573: Port   - The port the remote lond is listening on.
        !           574: Socket - Socket open on the connection.
        !           575: State  - The current state.
        !           576: TransactionRequest - The request being transmitted.
        !           577: TransactionReply   - The reply being received from the transaction.
        !           578: InformReadable     - True if we want to be called when socket is readable.
        !           579: InformWritable     - True if we want to be informed if the socket is writable.
        !           580: Timeoutable        - True if the current operation is allowed to timeout.
        !           581: TimeoutValue       - Number of seconds in the timeout.
        !           582: TimeoutRemaining   - Number of seconds left in the timeout.
        !           583: CipherKey          - The key that was negotiated with the peer.
        !           584: Cipher             - The cipher obtained via the key.
        !           585: 
        !           586: 
        !           587: 
        !           588: =head2 The following are callback like members:
        !           589: =item Tick: Called in response to a timer tick. Used to managed timeouts etc.
        !           590: =item Readable: Called when the socket becomes readable.
        !           591: =item Writable: Called when the socket becomes writable.
        !           592: =item TimedOut: Called when a timed operation timed out.
        !           593: 
        !           594: =head2 The following are operational member functions.
        !           595: =item InitiateTransaction: Called to initiate a new transaction
        !           596: =item SetStateTransitionCallback: Called to establish a function that is called
        !           597:     whenever the object goes through a state transition.  This is used by
        !           598:     The client to manage the work flow for the object.
        !           599: =item SetTimeoutCallback -Set a function to be called when a transaction times
        !           600:     out.  The function will be called with the object as its sole parameter.
        !           601: =item Encrypt - Encrypts a block of text according to the cipher negotiated
        !           602:        with the peer (assumes the text is a command).
        !           603: =item Decrypt - Decrypts a block of text according to the cipher negotiated
        !           604:        with the peer (assumes the block was a reply.
        !           605: 
        !           606: =head2 The following are selector member functions:
        !           607: 
        !           608: =item GetState: Returns the current state
        !           609: =item GetSocket: Gets the socekt open on the connection to lond.
        !           610: =item WantReadable: true if the current state requires a readable event.
        !           611: =item WantWritable: true if the current state requires a writable event.
        !           612: =item WantTimeout: true if the current state requires timeout support.
        !           613: =item GetHostIterator: Returns an iterator into the host file hash.
        !           614: =cut

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