File Coverage

Scooby.pm
Criterion Covered Total %
statement 7 10 70.0
branch n/a
condition n/a
subroutine 3 4 75.0
pod 0 1 0.0
total 10 15 66.6


line stmt bran cond sub pod time code
1             package Devel::Scooby;
2            
3             # Scooby.pm - a relocation mechanism for use with the Mobile::Location
4             # and Mobile::Executive modules.
5             #
6             # Author: Paul Barry, paul.barry@itcarlow.ie
7             # Create: October 2002.
8             # Update: April/May 2003 - Version 4.x series.
9             #
10             # Notes: This code takes advantage of the CPAN modules
11             # PadWalker and Storable (with a little help from the
12             # Data::Dumper module when it comes to Objects). The Crypt::RSA
13             # module provides PK+/PK- support.
14             #
15             # Version 1.x supported relocating simple Perl code.
16             # Version 2.x supported relocating SCALARs, ARRAYs, and
17             # HASHes and references to same.
18             # Version 3.x supported relocating Perl OO objects. Note
19             # that this will only occur after Scooby has contacted
20             # the receiving Location and determined that any
21             # required classes exist on the remote Perl system.
22             # Version 4.x supports authenticated relocation using Crypt::RSA,
23             # as well as encryption of the mobile agent source code.
24             #
25            
26             our $VERSION = 4.12;
27            
28             # The "constant.pm" module does not want to work with the debugger mechanism,
29             # so "our" variables are used instead.
30            
31             our $SCOOBY_CONFIG_FILE = "$ENV{'HOME'}/.scoobyrc";
32            
33             our $SIGNATURE_DELIMITER = "\n--end-sig--\n";
34            
35             our $ALARM_WAIT = 30;
36             our $LOCALHOST = '127.0.0.1';
37            
38             our $RESPONDER_PPORT = '30001';
39             our $REGISTER_PPORT = '30002';
40            
41             our $MAX_RECV_LEN = 65536;
42            
43             our $TRUE = 1;
44             our $FALSE = 0;
45            
46             ##########################################################################
47             # The Scooby Debugger starts here.
48             ##########################################################################
49            
50             {
51             package DB; # Remember: Scooby is a DEBUGGER.
52            
53             our ( $package, $file, $line ); # XXXXX: Note these are 'global'.
54            
55             sub DB {
56            
57             # Called for every line in the program that can be breakpointed.
58             #
59             # IN: nothing.
60             #
61             # OUT: nothing.
62            
63 0     0 0   ( $package, $file, $line ) = caller; # XXXXX: Writing to globals!
64             }
65            
66             sub sub {
67            
68             # Called before every subroutine call in the program.
69             #
70             # IN: nothing. Although "$sub" is set to the name of the
71             # subroutine that was just called (thanks to Perl's debugging
72             # mechanisms).
73             #
74             # OUT: nothing.
75            
76             if ( $sub =~ /^Mobile::Executive::relocate$/ )
77             {
78 1     1   10556 use Socket; # Functional interface to Socket API.
  1         4583  
  1         593  
79 1     1   1116 use Storable qw( freeze thaw ); # Provides a persistence mechanism.
  1         3923  
  1         98  
80 1     1   1869 use PadWalker qw( peek_my ); # Provides access to all lexically scoped variables.
  0            
  0            
81            
82             use Crypt::RSA; # Provides authentication and
83             # encryption services.
84            
85             my $remote = shift;
86            
87             # Next two lines turn the IP name into a dotted-decimal.
88            
89             my $tmp = gethostbyname( $remote ) or inet_aton( $remote );
90             $remote = inet_ntoa( $tmp );
91            
92             my $remote_port = shift;
93            
94             my $filename_mem = $file;
95             my $linenum_mem = ( $line + 1 );
96            
97             my $stringified;
98            
99             # We first determine the list of lexicals in the caller.
100            
101             my $them = peek_my( 0 );
102            
103             # Then we turn the list of lexicals into "Storable" output.
104            
105             my $str = freeze( \%{ $them } );
106            
107             # Then we turn the thawed output back into Perl code. This
108             # code is referred to as the "lexical init" code.
109            
110             $stringified = _storable_decode(
111             $remote,
112             $remote_port,
113             thaw( $str )
114             );
115            
116             # Determine the KEYSERVER address from the .scoobyrc file.
117            
118             open KEYFILE, "$SCOOBY_CONFIG_FILE"
119             or die "Scooby: unable to access ~/.scoobyrc. Does it exist?\n";
120            
121             my $keyline = ;
122            
123             close KEYFILE;
124            
125             # Note: format of 'rc' file is very strict. No spaces!
126            
127             $keyline =~ /^KEYSERVER=(.+)/;
128            
129             my $key_server = $1;
130            
131             # Now that we know the address of the key server, we can
132             # request the PK+ of the key server and the next location.
133            
134             _get_store_pkplus( $key_server, $LOCALHOST, $RESPONDER_PPORT );
135             _get_store_pkplus( $key_server, $remote, $remote_port );
136            
137             open RELOCATE_FILE, "$Mobile::Executive::absolute_fn"
138             or die "Scooby: Unable to open file for relocation: $!.\n";
139            
140             # Dump the current state of the agent to a temporary disk-file
141             # so that we can encrypt it with the next Location's PK+.
142            
143             my $tmp_filename = "$0.$$.temporary.tmp";
144            
145             open TMP_FILE, ">$tmp_filename"
146             or die "Scooby: could not write to temporary encryption file: $!.\n";
147            
148             my $line_count = 0;
149            
150             # Write the agent's source code one line at a time to the temporary file.
151            
152             while ( my $line2send = )
153             {
154             ++$line_count;
155             print TMP_FILE $line2send;
156            
157             # Check to see if we need to insert the "lexical init" code.
158            
159             if ( $line_count == ($linenum_mem-1) )
160             {
161             print TMP_FILE $stringified if defined( $stringified );
162             }
163             }
164            
165             close RELOCATE_FILE;
166            
167             close TMP_FILE;
168            
169             # The agent source code (which has mutated) is now in "$tmp_filename".
170            
171             open TOENCRYPT_FILE, "$tmp_filename"
172             or die "Scooby: temporary encryption file could not be opened: $!.\n";
173            
174             my @entire_toencrypt = ;
175            
176             close TOENCRYPT_FILE;
177            
178             # We are now done with the temporary file, so we can remove it from
179             # the local storage.
180            
181             unlink $tmp_filename;
182            
183             my $message = "@entire_toencrypt\n";
184             my $public_key_filename = "$remote.$remote_port.public";
185            
186             my $public_key = new Crypt::RSA::Key::Public(
187             Filename => $public_key_filename
188             );
189            
190             my $rsa = new Crypt::RSA;
191            
192             # Encrypt the mutated agent using the PK+ of the next Location.
193            
194             my $cyphertext = $rsa->encrypt(
195             Message => $message,
196             Key => $public_key,
197             Armour => $TRUE
198             ) or die $rsa->errstr, "\n";
199            
200             # Use the PK- of this Mobile::Executive invocation to
201             # sign the encrypted mobile agent.
202            
203             my $cypher_signature = $rsa->sign(
204             Message => $cyphertext,
205             Key => $Mobile::Executive::private_key,
206             Armour => $TRUE
207             ) or die $rsa->errstr, "\n";
208            
209             # Networking code to send agent to the server starts here.
210            
211             my $trans_serv = getprotobyname( 'tcp' );
212             my $remote_host = gethostbyname( $remote ) or inet_aton( $remote );
213             my $destination = sockaddr_in( $remote_port, $remote_host );
214            
215             socket( TCP_SOCK, PF_INET, SOCK_STREAM, $trans_serv )
216             or die "Scooby: socket creation failed: $!.\n";
217            
218             connect( TCP_SOCK, $destination )
219             or die "Scooby: connect to remote system failed: $!.\n";
220            
221             # Turn on auto-flushing.
222            
223             my $previous = select TCP_SOCK;
224             $| = 1;
225             select $previous;
226            
227             # Send the filename of the agent to the remote Location.
228            
229             print TCP_SOCK $filename_mem . "\n";
230            
231             # Send the line# for the next executable line to the Location.
232            
233             print TCP_SOCK $linenum_mem . "\n";
234            
235             # We need to work out the port that this client is using "locally".
236             # The Location will use this protocol port number to query the
237             # keyserver for the just-about-to-be-sent public key.
238            
239             my ( $local_pport, $local_ip ) = sockaddr_in( getsockname( TCP_SOCK ) );
240            
241             # Prior to sending the signature and cyphertext to the next
242             # Location, we need to update the keyserver with the appropriate
243             # PK+ so that the next Location can verify the signature. We
244             # write the PK+ to a disk-file, then read it back in, as this is
245             # the format that the keyserver expects to receive it in.
246            
247             $Mobile::Executive::public_key->write(
248             Filename => "$0.$$.$local_pport.public"
249             );
250            
251             open LOCAL_KEYFILE, "$0.$$.$local_pport.public"
252             or die "Scooby: the local public key file does not exist: $!.\n";
253            
254             my @entire_local_file = ;
255            
256             close LOCAL_KEYFILE;
257            
258             # We have no further need for the public key file, so remove it.
259            
260             unlink "$0.$$.$local_pport.public";
261            
262             # Send the "local" protocol port number and PK+ to the keyserver.
263            
264             my $keysock_obj = IO::Socket::INET->new( PeerAddr => $key_server,
265             PeerPort => $REGISTER_PPORT,
266             Proto => 'tcp' );
267            
268             if ( !defined( $keysock_obj ) )
269             {
270             die "Scooby: could not create socket object to key server: $!.\n";
271             }
272            
273             print $keysock_obj "$local_pport\n";
274            
275             print $keysock_obj @entire_local_file;
276            
277             $keysock_obj->close;
278            
279             # ACK that the just inserted PK+ is in the keyserver.
280            
281             _wait_for_pkplus_confirm( $key_server, inet_ntoa( $local_ip ), $local_pport );
282            
283             # Send the signature to the next Location.
284            
285             print TCP_SOCK "$cypher_signature" . $SIGNATURE_DELIMITER;
286            
287             # Send the encoded cyphertext to the next Location.
288            
289             print TCP_SOCK $cyphertext;
290            
291             close TCP_SOCK
292             or warn "Scooby: close failed: $!.\n";
293            
294             exit; # We are done on this Location, having just relocated
295             # to another. This is why we "exit" at this time.
296             }
297            
298             # Call the original subroutine with parameters (if there was any).
299             # We only get to here if there's no request for relocation.
300            
301             if ( defined @_ )
302             {
303             &$sub( @_ );
304             }
305             else
306             {
307             &$sub;
308             }
309             }
310            
311             ##########################################################################
312             # Scooby support routines follow.
313             ##########################################################################
314            
315             sub _wait_for_pkplus_confirm {
316            
317             # Contacts the key server and requests the PK+ for a specified
318             # IP address/port combo. Keeps asking for the PK+ until such time as the PK+
319             # is ACKed by the key server.
320             #
321             # IN: The IP name/address of the key server.
322             # The IP address to use when requesting a PK+ from the key server.
323             # The protocol port to use when requesting a PK+.
324             #
325             # OUT: nothing.
326            
327             use IO::Socket; # Provides OO interface to Socket API.
328            
329             my $server = shift;
330             my $lookup = shift;
331             my $port = shift;
332            
333             my $sig_ack = $FALSE;
334            
335             while ( $sig_ack == $FALSE )
336             {
337             # Opens a socket object to the keyserver.
338            
339             my $key_sock = IO::Socket::INET->new(
340             PeerAddr => $server,
341             PeerPort => $RESPONDER_PPORT,
342             Proto => 'tcp'
343             );
344            
345             if ( !defined( $key_sock ) )
346             {
347             die "Scooby: could not create key server socket object: $!.\n";
348             }
349            
350             # Send the lookup details to the keyserver.
351            
352             print $key_sock "$lookup\n";
353             print $key_sock $port;
354            
355             # We are done writing, so half close the socket.
356            
357             $key_sock->shutdown( 1 );
358            
359             my $data = '';
360            
361             # Read the entire response from the keyserver.
362            
363             while ( my $chunk = <$key_sock> )
364             {
365             $data = $data . $chunk;
366             }
367            
368             $key_sock->close;
369            
370             # This splits the signature and data on the SIGNATURE_DELIMITER
371             # pattern as used by the keyserver.
372            
373             ( my $key_sig, $data ) = split /\n--end-sig--\n/, $data;
374            
375             if ( $key_sig eq "NOSIG" )
376             {
377             $sig_ack = $FALSE;
378             }
379             else
380             {
381             $sig_ack = $TRUE;
382             }
383             }
384             }
385            
386             sub _get_store_pkplus {
387            
388             # Contacts the key server and requests the PK+ for a specified
389             # IP address/port combo. Stores the PK+ in the named disk-file.
390             #
391             # IN: The IP name/address of the key server.
392             # The IP address to use when requesting a PK+ from the key server.
393             # The protocol port to use when requesting a PK+.
394             #
395             # OUT: nothing.
396             #
397             # This code is an extension of the "_wait_for_pkplus_confirm" code.
398            
399             use Crypt::RSA; # Provides authentication and encryption services.
400             use IO::Socket; # Provides OO interface to Socket API.
401            
402             my $server = shift;
403             my $lookup = shift;
404             my $port = shift;
405            
406             my $key_sock = IO::Socket::INET->new(
407             PeerAddr => $server,
408             PeerPort => $RESPONDER_PPORT,
409             Proto => 'tcp'
410             );
411            
412             if ( !defined( $key_sock ) )
413             {
414             die "Scooby: could not create key server socket object: $!.\n";
415             }
416            
417             print $key_sock "$lookup\n";
418             print $key_sock $port;
419            
420             # We are done writing, so half close the socket.
421            
422             $key_sock->shutdown( 1 );
423            
424             my $data = '';
425            
426             while ( my $chunk = <$key_sock> )
427             {
428             $data = $data . $chunk;
429             }
430            
431             $key_sock->close;
432            
433             # This splits the signature and data on the SIGNATURE_DELIMITER
434             # pattern as used by the keyserver.
435            
436             ( my $key_sig, $data ) = split /\n--end-sig--\n/, $data;
437            
438             if ( $key_sig eq "NOSIG" )
439             {
440             die "Scooby: no signature found: aborting.\n";
441             }
442             elsif ( $key_sig eq "SELFSIG" )
443             {
444             my $lf = "$lookup.$port.public"; # Location PK+ filename.
445            
446             open KEYFILE, ">$lf"
447             or die "Scooby: could not create key file: $!.\n";
448            
449             print KEYFILE $data;
450            
451             close KEYFILE;
452             }
453             else
454             {
455             my $ksf = "$LOCALHOST.$RESPONDER_PPORT.public";
456            
457             my $key_server_pkplus = new Crypt::RSA::Key::Public(
458             Filename => $ksf
459             );
460            
461             my $rsa = new Crypt::RSA;
462            
463             my $verify = $rsa->verify(
464             Message => $data,
465             Signature => "$key_sig",
466             Key => $key_server_pkplus,
467             Armour => $TRUE
468             );
469            
470             if ( !$verify )
471             {
472             die "Scooby: signature for next location does not verify: aborting.\n";
473             }
474             else
475             {
476             open KEYFILE, ">$lookup.$port.public"
477             or die "Scooby: could not create key file: $!.\n";
478            
479             print KEYFILE $data;
480            
481             close KEYFILE;
482             }
483             }
484             }
485            
486             sub _check_modules_on_remote {
487            
488             # Contacts the remote Location, sends the list of required modules,
489             # waits for a response, then returns it to the caller.
490             #
491             # IN: The IP name (or address) of the remote Location.
492             # The protocol port number of the remote Location.
493             # The list of modules to look for.
494             #
495             # OUT: The message received from the server.
496            
497             my $remote = shift;
498             my $remote_port = shift;
499             my @tocheck = @_;
500            
501             use Socket; # Functional interface to Socket API.
502            
503             my $trans_serv = getprotobyname( 'tcp' );
504             my $remote_host = gethostbyname( $remote ) or inet_aton( $remote );
505            
506             # Note: the server listens at Port+1.
507            
508             my $destination = sockaddr_in( $remote_port+1, $remote_host );
509            
510             socket( CHECK_MOD_SOCK, PF_INET, SOCK_STREAM, $trans_serv )
511             or die "Scooby: socket creation failed: $!.\n";
512             my $con_ok = connect( CHECK_MOD_SOCK, $destination )
513             or die "Scooby: connect to remote system failed: $!.\n";
514            
515             # Send the list of modules to check.
516            
517             send( CHECK_MOD_SOCK, join( ' ', @tocheck ), 0 )
518             or warn "Scooby: problem with send: $!.\n";
519            
520             shutdown( CHECK_MOD_SOCK, 1 ); # Close the socket for writing.
521            
522             my $remote_response = '';
523            
524             # Add a signal handler to execute when the alarm sounds (or expires).
525            
526             $SIG{ALRM} = sub { die "no remote module check\n"; };
527            
528             alarm( $ALARM_WAIT );
529            
530             # We wait for up to ALARM_WAIT seconds for a response from the Location.
531            
532             eval {
533             recv( CHECK_MOD_SOCK, $remote_response, $MAX_RECV_LEN, 0 );
534            
535             alarm( 0 ); # Cancel the alarm, we do not need it now.
536             };
537            
538             close CHECK_MOD_SOCK
539             or warn "Scooby: close failed: $!.\n";
540            
541             # Process the timeout if it happened. Die if we see some message
542             # other than the one we expect.
543            
544             if ( $@ )
545             {
546             die "Scooby: $@\n" unless $@ =~ /no remote module check/;
547            
548             warn "Scooby: not able to check existence of remote modules.\n";
549             }
550            
551             return $remote_response;
552             }
553            
554             sub _storable_decode {
555            
556             # Called immediately after the lexical variables are stringified
557             # in order to return the "Storable" output to its original form.
558             #
559             # IN: The IP name (or address) of the remote Location.
560             # The protocol port number of the remote Location.
561             # The "thawed" output from the Storable::thaw method.
562             #
563             # OUT: The stringified representation of the Perl code that can be
564             # executed to reinitialize the relocated variables.
565             #
566             # NOTE: This code also checks to see if any required modules exist
567             # on the remote Location. It will "die" if some are missing.
568            
569             my $remote = shift;
570             my $remote_port = shift;
571             my $thawed = shift;
572            
573             my %for_refs;
574             my $stringified = '';
575             my @required_classes = ();
576            
577             # The lexicals are processed TWICE, as it is not possible to
578             # handle REFerences with a single pass over "$thawed".
579            
580             # Process the lexicals once, for SCALARs, ARRAYs and HASHes.
581             #
582             # Note: we need to remember the 'memory address' of each variable, so
583             # we check them against any REFerences in the second pass.
584             #
585             # The generated code is indented by four spaces.
586            
587             while ( my ( $name, $value ) = each ( %{ $thawed } ) )
588             {
589             if ( ref( $value ) eq 'SCALAR' )
590             {
591             $for_refs{ $value } = $name;
592            
593             # We do NOT want to enclose SCALAR numbers in quotes!
594            
595             if ( $$value =~ /[^0123456789.]+/ )
596             {
597             $stringified .= " $name = \"$$value\";\n";
598             }
599             else
600             {
601             $stringified .= " $name = $$value;\n";
602             }
603             }
604            
605             if ( ref( $value ) eq 'ARRAY' )
606             {
607             $for_refs{ $value } = $name;
608             $stringified .= " $name = qw( @$value );\n";
609             }
610            
611             if ( ref( $value ) eq 'HASH' )
612             {
613             $for_refs{ $value } = $name;
614             $stringified .= " $name = (\n";
615             while ( my ( $h_name, $h_value ) = each ( %{ $value } ) )
616             {
617             $stringified .= " \"$h_name\" => \"$h_value\",\n"
618             }
619             $stringified .= " );\n";
620             }
621             }
622            
623             # Second pass: process the lexicals again, this time for REFs.
624            
625             while ( my ( $name, $value ) = each ( %{ $thawed } ) )
626             {
627            
628             # Deal with references to Perl OO objects.
629            
630             if ( ref( $value ) eq 'REF' && !defined( $for_refs{ $$value } ) )
631             {
632             push @required_classes, ref( $$value );
633            
634             use Data::Dumper;
635            
636             my $string = Dumper( $value );
637            
638             # Make sure the appropriate Class is used.
639            
640             $stringified .= " use " . ref( $$value ) . ";\n\n";
641            
642             # Replace Data::Dumper's generated $VARn with correct name.
643            
644             $string =~ s/^\$VAR\d+ = \\//;
645            
646             # Add the code to bless the object to the stringified code.
647            
648             $stringified .= " $name = $string\n";
649             }
650            
651             # Deal with references to SCALARs, ARRAYs and HASHes.
652            
653             if ( ref( $value ) eq 'REF' && defined( $for_refs{ $$value } ) )
654             {
655             $stringified .= " $name = \\$for_refs{ $$value };\n";
656             }
657             }
658            
659             # Check to see if any required modules exist on the remote Location.
660             # The list provided is calculated as a result of processing any
661             # references to object instances.
662            
663             if ( @required_classes )
664             {
665             my $message = _check_modules_on_remote(
666             $remote,
667             $remote_port,
668             @required_classes
669             );
670            
671             if ( $message =~ /^NOK/ )
672             {
673             $message =~ s/^NOK: //;
674            
675             die "Required modules missing on remote: $message.\n";
676             }
677             elsif ( $message !~ /^OK/ )
678             {
679             warn "Something strange has happened: $message.\n";
680            
681             die "Is the remote Location ready?\n";
682             }
683             }
684            
685             # Assuming we haven't died, return the Perl code to the caller.
686            
687             return $stringified;
688             }
689            
690             } # End of DB package.
691            
692             1; # Evaluate true as the last statement of this package (required by Perl).
693            
694             ##########################################################################
695             # Documentation starts here.
696             ##########################################################################
697            
698             =pod
699            
700             =head1 NAME
701            
702             "Scooby" - the internal machinery that works with B and B to provide a mobile agent execution and location environment for the Perl Programming Language.
703            
704             =head1 VERSION
705            
706             4.0x (versions 1.x and 2.x were never released; version 3.x did not support encryption and authentication).
707            
708             =head1 SYNOPSIS
709            
710             perl -d:Scooby mobile_agent
711            
712             =head1 DESCRIPTION
713            
714             This is an internal module that is not designed to be "used" directly by a program. Assuming a mobile agent called B exists (that "uses" the B module), this module can be used to execute it, as follows:
715            
716             =over 4
717            
718             perl -d:Scooby multiwho
719            
720             =back
721            
722             The B<-d> switch to C invokes Scooby as a debugger. Unlike a traditional debugger that expects to interact with a human, Scooby runs automatically. It NEVER interacts with a human, it interacts with the mobile agent machinery.
723            
724             Scooby can be used to relocate Perl source code which contains the following:
725            
726             =over 4
727            
728             SCALARs (both numbers and strings).
729            
730             An ARRAY of SCALARs (known as a simple ARRAY).
731            
732             A HASH of SCALARs (known as a simple HASH).
733            
734             References to SCALARs.
735            
736             References to a simple ARRAY.
737            
738             References to a simple HASH.
739            
740             Objects.
741            
742             References to objects are B supported and are in no way guaranteed to behave the way you expect them to after relocation (even though they do relocate).
743            
744             The relocation of more complex data structures is B supported at this time (refer to the TO DO LIST section, below).
745            
746             =back
747            
748             =head1 Internal methods/subroutines
749            
750             =over 4
751            
752             B - called for every executable statement contained in the mobile agent source code file.
753            
754             B - called for every subroutine call contained in the mobile agent source code file.
755            
756             B<_DB::storable_decode> - takes the stringified output from B's B subroutine and turns it back into Perl code (with a little help from Data::Dumper for objects).
757            
758             B - checks to see if a list of modules/classes "used" within the mobile agent actually exist on the remote Location's Perl system.
759            
760             B - contacts the key server and requests a PK+, then stores the PK+ in a named disk-file.
761            
762             B - repeatedly contacts the key server until requested PK+ is returned (i.e., ACKed).
763            
764             =back
765            
766             =head1 ENVIRONMENT
767            
768             This module must be installed in your Perl system's B directory. This module will only work on an operating system that supports the Perl modules listed in the SEE ALSO section, below. (To date, I've only tested it on various Linux distributions).
769            
770             =head1 TO DO LIST
771            
772             Loads. The biggest item on the list would be to enhance Scooby to allow it to handle more complex data structures, such as ARRAYs of HASHes and HASHes of ARRAYs, etc., etc.
773            
774             My initial plan was to allow for the automatic relocation of open disk-files. However, on reflection, I decided not to do this at this time, but may return to the idea at some stage in the future.
775            
776             The current implementation checks to see if "used" classes are available on the next Location before attempting relocation, but does not check to see if "used" modules are available. It would be nice if it did.
777            
778             It would also be nice to incorporate an updated B (by James Duncan) to handle the relocation of objects to a Location without the need to have the module exist on the remote Location. On my system (Linux), the most recent B generates compile/run-time errors.
779            
780             =head1 SEE ALSO
781            
782             The B module and the B class. Internally, this module uses the following CPAN modules: B and B, in addition to the standard B module. The B modules provides encryption and authentication services.
783            
784             The Scooby Website: B.
785            
786             =head1 AUTHOR
787            
788             Paul Barry, Institute of Technology, Carlow in Ireland, B, B.
789            
790             =head1 COPYRIGHT
791            
792             Copyright (c) 2003, Paul Barry. All Rights Reserved.
793            
794             This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
795